summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts19
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/adsp/adsp.cpp13
-rw-r--r--src/audio_core/adsp/adsp.h3
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp48
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.h45
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.cpp107
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decode_object.h38
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.cpp269
-rw-r--r--src/audio_core/adsp/apps/opus/opus_decoder.h92
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp111
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h39
-rw-r--r--src/audio_core/adsp/apps/opus/shared_memory.h17
-rw-r--r--src/audio_core/adsp/mailbox.h27
-rw-r--r--src/audio_core/opus/decoder.cpp179
-rw-r--r--src/audio_core/opus/decoder.h53
-rw-r--r--src/audio_core/opus/decoder_manager.cpp102
-rw-r--r--src/audio_core/opus/decoder_manager.h38
-rw-r--r--src/audio_core/opus/hardware_opus.cpp241
-rw-r--r--src/audio_core/opus/hardware_opus.h45
-rw-r--r--src/audio_core/opus/parameters.h54
-rw-r--r--src/audio_core/renderer/command/command_processing_time_estimator.cpp4
-rw-r--r--src/audio_core/renderer/system.cpp10
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/bounded_threadsafe_queue.h4
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/file_sys/registered_cache.cpp4
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp20
-rw-r--r--src/core/hle/service/audio/errors.h12
-rw-r--r--src/core/hle/service/audio/hwopus.cpp722
-rw-r--r--src/core/hle/service/audio/hwopus.h25
-rw-r--r--src/core/hle/service/mii/mii.cpp357
-rw-r--r--src/core/hle/service/mii/mii_database.cpp142
-rw-r--r--src/core/hle/service/mii/mii_database.h66
-rw-r--r--src/core/hle/service/mii/mii_database_manager.cpp420
-rw-r--r--src/core/hle/service/mii/mii_database_manager.h58
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp410
-rw-r--r--src/core/hle/service/mii/mii_manager.h82
-rw-r--r--src/core/hle/service/mii/mii_result.h9
-rw-r--r--src/core/hle/service/mii/mii_types.h16
-rw-r--r--src/core/hle/service/mii/mii_util.h26
-rw-r--r--src/core/hle/service/mii/types/char_info.cpp6
-rw-r--r--src/core/hle/service/mii/types/char_info.h106
-rw-r--r--src/core/hle/service/mii/types/core_data.cpp213
-rw-r--r--src/core/hle/service/mii/types/core_data.h7
-rw-r--r--src/core/hle/service/mii/types/store_data.cpp83
-rw-r--r--src/core/hle/service/mii/types/store_data.h19
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.cpp78
-rw-r--r--src/core/hle/service/nfc/common/device.cpp8
-rw-r--r--src/core/tools/renderdoc.cpp2
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp2
52 files changed, 3810 insertions, 670 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index fe79a701c..431f899b3 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -77,13 +77,30 @@ android {
77 buildConfigField("String", "BRANCH", "\"${getBranch()}\"") 77 buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
78 } 78 }
79 79
80 val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
81 if (keystoreFile != null) {
82 signingConfigs {
83 create("release") {
84 storeFile = file(keystoreFile)
85 storePassword = System.getenv("ANDROID_KEYSTORE_PASS")
86 keyAlias = System.getenv("ANDROID_KEY_ALIAS")
87 keyPassword = System.getenv("ANDROID_KEYSTORE_PASS")
88 }
89 }
90 }
91
80 // Define build types, which are orthogonal to product flavors. 92 // Define build types, which are orthogonal to product flavors.
81 buildTypes { 93 buildTypes {
82 94
83 // Signed by release key, allowing for upload to Play Store. 95 // Signed by release key, allowing for upload to Play Store.
84 release { 96 release {
97 signingConfig = if (keystoreFile != null) {
98 signingConfigs.getByName("release")
99 } else {
100 signingConfigs.getByName("debug")
101 }
102
85 resValue("string", "app_name_suffixed", "yuzu") 103 resValue("string", "app_name_suffixed", "yuzu")
86 signingConfig = signingConfigs.getByName("debug")
87 isMinifyEnabled = true 104 isMinifyEnabled = true
88 isDebuggable = false 105 isDebuggable = false
89 proguardFiles( 106 proguardFiles(
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 67dfe0290..400988c5f 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -10,6 +10,13 @@ add_library(audio_core STATIC
10 adsp/apps/audio_renderer/command_buffer.h 10 adsp/apps/audio_renderer/command_buffer.h
11 adsp/apps/audio_renderer/command_list_processor.cpp 11 adsp/apps/audio_renderer/command_list_processor.cpp
12 adsp/apps/audio_renderer/command_list_processor.h 12 adsp/apps/audio_renderer/command_list_processor.h
13 adsp/apps/opus/opus_decoder.cpp
14 adsp/apps/opus/opus_decoder.h
15 adsp/apps/opus/opus_decode_object.cpp
16 adsp/apps/opus/opus_decode_object.h
17 adsp/apps/opus/opus_multistream_decode_object.cpp
18 adsp/apps/opus/opus_multistream_decode_object.h
19 adsp/apps/opus/shared_memory.h
13 audio_core.cpp 20 audio_core.cpp
14 audio_core.h 21 audio_core.h
15 audio_event.h 22 audio_event.h
@@ -35,6 +42,13 @@ add_library(audio_core STATIC
35 in/audio_in.h 42 in/audio_in.h
36 in/audio_in_system.cpp 43 in/audio_in_system.cpp
37 in/audio_in_system.h 44 in/audio_in_system.h
45 opus/hardware_opus.cpp
46 opus/hardware_opus.h
47 opus/decoder_manager.cpp
48 opus/decoder_manager.h
49 opus/decoder.cpp
50 opus/decoder.h
51 opus/parameters.h
38 out/audio_out.cpp 52 out/audio_out.cpp
39 out/audio_out.h 53 out/audio_out.h
40 out/audio_out_system.cpp 54 out/audio_out_system.cpp
@@ -214,7 +228,7 @@ else()
214 ) 228 )
215endif() 229endif()
216 230
217target_link_libraries(audio_core PUBLIC common core) 231target_link_libraries(audio_core PUBLIC common core Opus::opus)
218if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 232if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
219 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic) 233 target_link_libraries(audio_core PRIVATE dynarmic::dynarmic)
220endif() 234endif()
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp
index 0580990f5..6c53c98fd 100644
--- a/src/audio_core/adsp/adsp.cpp
+++ b/src/audio_core/adsp/adsp.cpp
@@ -7,12 +7,21 @@
7namespace AudioCore::ADSP { 7namespace AudioCore::ADSP {
8 8
9ADSP::ADSP(Core::System& system, Sink::Sink& sink) { 9ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
10 audio_renderer = 10 audio_renderer = std::make_unique<AudioRenderer::AudioRenderer>(system, sink);
11 std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink); 11 opus_decoder = std::make_unique<OpusDecoder::OpusDecoder>(system);
12 opus_decoder->Send(Direction::DSP, OpusDecoder::Message::Start);
13 if (opus_decoder->Receive(Direction::Host) != OpusDecoder::Message::StartOK) {
14 LOG_ERROR(Service_Audio, "OpusDeocder failed to initialize.");
15 return;
16 }
12} 17}
13 18
14AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { 19AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
15 return *audio_renderer.get(); 20 return *audio_renderer.get();
16} 21}
17 22
23OpusDecoder::OpusDecoder& ADSP::OpusDecoder() {
24 return *opus_decoder.get();
25}
26
18} // namespace AudioCore::ADSP 27} // namespace AudioCore::ADSP
diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h
index bd5bcc63b..a0c24a16a 100644
--- a/src/audio_core/adsp/adsp.h
+++ b/src/audio_core/adsp/adsp.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h" 6#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h"
7#include "audio_core/adsp/apps/opus/opus_decoder.h"
7#include "common/common_types.h" 8#include "common/common_types.h"
8 9
9namespace Core { 10namespace Core {
@@ -40,10 +41,12 @@ public:
40 ~ADSP() = default; 41 ~ADSP() = default;
41 42
42 AudioRenderer::AudioRenderer& AudioRenderer(); 43 AudioRenderer::AudioRenderer& AudioRenderer();
44 OpusDecoder::OpusDecoder& OpusDecoder();
43 45
44private: 46private:
45 /// AudioRenderer app 47 /// AudioRenderer app
46 std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{}; 48 std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{};
49 std::unique_ptr<OpusDecoder::OpusDecoder> opus_decoder{};
47}; 50};
48 51
49} // namespace ADSP 52} // namespace ADSP
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
index 2e549bc6f..972d5e45b 100644
--- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
@@ -14,13 +14,12 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16 16
17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97)); 17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP_AudioRenderer", MP_RGB(60, 19, 97));
18 18
19namespace AudioCore::ADSP::AudioRenderer { 19namespace AudioCore::ADSP::AudioRenderer {
20 20
21AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_, 21AudioRenderer::AudioRenderer(Core::System& system_, Sink::Sink& sink_)
22 Sink::Sink& sink_) 22 : system{system_}, sink{sink_} {}
23 : system{system_}, memory{memory_}, sink{sink_} {}
24 23
25AudioRenderer::~AudioRenderer() { 24AudioRenderer::~AudioRenderer() {
26 Stop(); 25 Stop();
@@ -33,8 +32,8 @@ void AudioRenderer::Start() {
33 32
34 main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); }); 33 main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
35 34
36 mailbox.Send(Direction::DSP, {Message::InitializeOK, {}}); 35 mailbox.Send(Direction::DSP, Message::InitializeOK);
37 if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) { 36 if (mailbox.Receive(Direction::Host) != Message::InitializeOK) {
38 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " 37 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
39 "message response from ADSP!"); 38 "message response from ADSP!");
40 return; 39 return;
@@ -47,8 +46,8 @@ void AudioRenderer::Stop() {
47 return; 46 return;
48 } 47 }
49 48
50 mailbox.Send(Direction::DSP, {Message::Shutdown, {}}); 49 mailbox.Send(Direction::DSP, Message::Shutdown);
51 if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) { 50 if (mailbox.Receive(Direction::Host) != Message::Shutdown) {
52 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown " 51 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
53 "message response from ADSP!"); 52 "message response from ADSP!");
54 } 53 }
@@ -67,25 +66,25 @@ void AudioRenderer::Stop() {
67 66
68void AudioRenderer::Signal() { 67void AudioRenderer::Signal() {
69 signalled_tick = system.CoreTiming().GetGlobalTimeNs().count(); 68 signalled_tick = system.CoreTiming().GetGlobalTimeNs().count();
70 Send(Direction::DSP, {Message::Render, {}}); 69 Send(Direction::DSP, Message::Render);
71} 70}
72 71
73void AudioRenderer::Wait() { 72void AudioRenderer::Wait() {
74 auto received = Receive(Direction::Host); 73 auto msg = Receive(Direction::Host);
75 if (received.msg != Message::RenderResponse) { 74 if (msg != Message::RenderResponse) {
76 LOG_ERROR(Service_Audio, 75 LOG_ERROR(Service_Audio,
77 "Did not receive the expected render response from the AudioRenderer! Expected " 76 "Did not receive the expected render response from the AudioRenderer! Expected "
78 "{}, got {}", 77 "{}, got {}",
79 Message::RenderResponse, received.msg); 78 Message::RenderResponse, msg);
80 } 79 }
81} 80}
82 81
83void AudioRenderer::Send(Direction dir, MailboxMessage message) { 82void AudioRenderer::Send(Direction dir, u32 message) {
84 mailbox.Send(dir, std::move(message)); 83 mailbox.Send(dir, std::move(message));
85} 84}
86 85
87MailboxMessage AudioRenderer::Receive(Direction dir, bool block) { 86u32 AudioRenderer::Receive(Direction dir) {
88 return mailbox.Receive(dir, block); 87 return mailbox.Receive(dir);
89} 88}
90 89
91void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, 90void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
@@ -120,7 +119,7 @@ void AudioRenderer::CreateSinkStreams() {
120} 119}
121 120
122void AudioRenderer::Main(std::stop_token stop_token) { 121void AudioRenderer::Main(std::stop_token stop_token) {
123 static constexpr char name[]{"AudioRenderer"}; 122 static constexpr char name[]{"DSP_AudioRenderer_Main"};
124 MicroProfileOnThreadCreate(name); 123 MicroProfileOnThreadCreate(name);
125 Common::SetCurrentThreadName(name); 124 Common::SetCurrentThreadName(name);
126 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 125 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
@@ -128,28 +127,28 @@ void AudioRenderer::Main(std::stop_token stop_token) {
128 // TODO: Create buffer map/unmap thread + mailbox 127 // TODO: Create buffer map/unmap thread + mailbox
129 // TODO: Create gMix devices, initialize them here 128 // TODO: Create gMix devices, initialize them here
130 129
131 if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) { 130 if (mailbox.Receive(Direction::DSP) != Message::InitializeOK) {
132 LOG_ERROR(Service_Audio, 131 LOG_ERROR(Service_Audio,
133 "ADSP Audio Renderer -- Failed to receive initialize message from host!"); 132 "ADSP Audio Renderer -- Failed to receive initialize message from host!");
134 return; 133 return;
135 } 134 }
136 135
137 mailbox.Send(Direction::Host, {Message::InitializeOK, {}}); 136 mailbox.Send(Direction::Host, Message::InitializeOK);
138 137
139 // 0.12 seconds (2,304,000 / 19,200,000) 138 // 0.12 seconds (2,304,000 / 19,200,000)
140 constexpr u64 max_process_time{2'304'000ULL}; 139 constexpr u64 max_process_time{2'304'000ULL};
141 140
142 while (!stop_token.stop_requested()) { 141 while (!stop_token.stop_requested()) {
143 auto received{mailbox.Receive(Direction::DSP)}; 142 auto msg{mailbox.Receive(Direction::DSP)};
144 switch (received.msg) { 143 switch (msg) {
145 case Message::Shutdown: 144 case Message::Shutdown:
146 mailbox.Send(Direction::Host, {Message::Shutdown, {}}); 145 mailbox.Send(Direction::Host, Message::Shutdown);
147 return; 146 return;
148 147
149 case Message::Render: { 148 case Message::Render: {
150 if (system.IsShuttingDown()) [[unlikely]] { 149 if (system.IsShuttingDown()) [[unlikely]] {
151 std::this_thread::sleep_for(std::chrono::milliseconds(5)); 150 std::this_thread::sleep_for(std::chrono::milliseconds(5));
152 mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); 151 mailbox.Send(Direction::Host, Message::RenderResponse);
153 continue; 152 continue;
154 } 153 }
155 std::array<bool, MaxRendererSessions> buffers_reset{}; 154 std::array<bool, MaxRendererSessions> buffers_reset{};
@@ -205,13 +204,12 @@ void AudioRenderer::Main(std::stop_token stop_token) {
205 } 204 }
206 } 205 }
207 206
208 mailbox.Send(Direction::Host, {Message::RenderResponse, {}}); 207 mailbox.Send(Direction::Host, Message::RenderResponse);
209 } break; 208 } break;
210 209
211 default: 210 default:
212 LOG_WARNING(Service_Audio, 211 LOG_WARNING(Service_Audio,
213 "ADSP AudioRenderer received an invalid message, msg={:02X}!", 212 "ADSP AudioRenderer received an invalid message, msg={:02X}!", msg);
214 received.msg);
215 break; 213 break;
216 } 214 }
217 } 215 }
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
index 3f5b7dca2..85874d88a 100644
--- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
@@ -17,13 +17,6 @@
17 17
18namespace Core { 18namespace Core {
19class System; 19class System;
20namespace Timing {
21struct EventType;
22}
23namespace Memory {
24class Memory;
25}
26class System;
27} // namespace Core 20} // namespace Core
28 21
29namespace AudioCore { 22namespace AudioCore {
@@ -34,19 +27,19 @@ class Sink;
34namespace ADSP::AudioRenderer { 27namespace ADSP::AudioRenderer {
35 28
36enum Message : u32 { 29enum Message : u32 {
37 Invalid = 0x00, 30 Invalid = 0,
38 MapUnmap_Map = 0x01, 31 MapUnmap_Map = 1,
39 MapUnmap_MapResponse = 0x02, 32 MapUnmap_MapResponse = 2,
40 MapUnmap_Unmap = 0x03, 33 MapUnmap_Unmap = 3,
41 MapUnmap_UnmapResponse = 0x04, 34 MapUnmap_UnmapResponse = 4,
42 MapUnmap_InvalidateCache = 0x05, 35 MapUnmap_InvalidateCache = 5,
43 MapUnmap_InvalidateCacheResponse = 0x06, 36 MapUnmap_InvalidateCacheResponse = 6,
44 MapUnmap_Shutdown = 0x07, 37 MapUnmap_Shutdown = 7,
45 MapUnmap_ShutdownResponse = 0x08, 38 MapUnmap_ShutdownResponse = 8,
46 InitializeOK = 0x16, 39 InitializeOK = 22,
47 RenderResponse = 0x20, 40 RenderResponse = 32,
48 Render = 0x2A, 41 Render = 42,
49 Shutdown = 0x34, 42 Shutdown = 52,
50}; 43};
51 44
52/** 45/**
@@ -54,7 +47,7 @@ enum Message : u32 {
54 */ 47 */
55class AudioRenderer { 48class AudioRenderer {
56public: 49public:
57 explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink); 50 explicit AudioRenderer(Core::System& system, Sink::Sink& sink);
58 ~AudioRenderer(); 51 ~AudioRenderer();
59 52
60 /** 53 /**
@@ -72,8 +65,8 @@ public:
72 void Signal(); 65 void Signal();
73 void Wait(); 66 void Wait();
74 67
75 void Send(Direction dir, MailboxMessage message); 68 void Send(Direction dir, u32 message);
76 MailboxMessage Receive(Direction dir, bool block = true); 69 u32 Receive(Direction dir);
77 70
78 void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit, 71 void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
79 u64 applet_resource_user_id, bool reset) noexcept; 72 u64 applet_resource_user_id, bool reset) noexcept;
@@ -94,9 +87,7 @@ private:
94 87
95 /// Core system 88 /// Core system
96 Core::System& system; 89 Core::System& system;
97 /// Memory 90 /// The output sink the AudioRenderer will send samples to
98 Core::Memory::Memory& memory;
99 /// The output sink the AudioRenderer will use
100 Sink::Sink& sink; 91 Sink::Sink& sink;
101 /// The active mailbox 92 /// The active mailbox
102 Mailbox mailbox; 93 Mailbox mailbox;
@@ -104,11 +95,13 @@ private:
104 std::jthread main_thread{}; 95 std::jthread main_thread{};
105 /// The current state 96 /// The current state
106 std::atomic<bool> running{}; 97 std::atomic<bool> running{};
98 /// Shared memory of input command buffers, set by host, read by DSP
107 std::array<CommandBuffer, MaxRendererSessions> command_buffers{}; 99 std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
108 /// The command lists to process 100 /// The command lists to process
109 std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{}; 101 std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{};
110 /// The streams which will receive the processed samples 102 /// The streams which will receive the processed samples
111 std::array<Sink::SinkStream*, MaxRendererSessions> streams{}; 103 std::array<Sink::SinkStream*, MaxRendererSessions> streams{};
104 /// CPU Tick when the DSP was signalled to process, uses time rather than tick
112 u64 signalled_tick{0}; 105 u64 signalled_tick{0};
113}; 106};
114 107
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
new file mode 100644
index 000000000..2c16d3769
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp
@@ -0,0 +1,107 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/adsp/apps/opus/opus_decode_object.h"
5#include "common/assert.h"
6
7namespace AudioCore::ADSP::OpusDecoder {
8namespace {
9bool IsValidChannelCount(u32 channel_count) {
10 return channel_count == 1 || channel_count == 2;
11}
12} // namespace
13
14u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
15 if (!IsValidChannelCount(channel_count)) {
16 return 0;
17 }
18 return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
19}
20
21OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {
22 auto* new_decoder = reinterpret_cast<OpusDecodeObject*>(buffer);
23 auto* comparison = reinterpret_cast<OpusDecodeObject*>(buffer2);
24
25 if (new_decoder->magic == DecodeObjectMagic) {
26 if (!new_decoder->initialized ||
27 (new_decoder->initialized && new_decoder->self == comparison)) {
28 new_decoder->state_valid = true;
29 }
30 } else {
31 new_decoder->initialized = false;
32 new_decoder->state_valid = true;
33 }
34 return *new_decoder;
35}
36
37s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) {
38 if (!state_valid) {
39 return OPUS_INVALID_STATE;
40 }
41
42 if (initialized) {
43 return OPUS_OK;
44 }
45
46 // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include
47 // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer
48 // provided.
49 // We could use _create and have libopus allocate it for us, but then we have to separately
50 // track which decoder is being used between this and multistream in order to call the correct
51 // destroy from the host side.
52 // This is a bit cringe, but is safe as these objects are only ever initialized inside the given
53 // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow.
54 decoder = (LibOpusDecoder*)(this + 1);
55 s32 ret = opus_decoder_init(decoder, sample_rate, channel_count);
56 if (ret == OPUS_OK) {
57 magic = DecodeObjectMagic;
58 initialized = true;
59 state_valid = true;
60 self = this;
61 final_range = 0;
62 }
63 return ret;
64}
65
66s32 OpusDecodeObject::Shutdown() {
67 if (!state_valid) {
68 return OPUS_INVALID_STATE;
69 }
70
71 if (initialized) {
72 magic = 0x0;
73 initialized = false;
74 state_valid = false;
75 self = nullptr;
76 final_range = 0;
77 decoder = nullptr;
78 }
79 return OPUS_OK;
80}
81
82s32 OpusDecodeObject::ResetDecoder() {
83 return opus_decoder_ctl(decoder, OPUS_RESET_STATE);
84}
85
86s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size,
87 u64 input_data, u64 input_data_size) {
88 ASSERT(initialized);
89 out_sample_count = 0;
90
91 if (!state_valid) {
92 return OPUS_INVALID_STATE;
93 }
94
95 auto ret_code_or_samples = opus_decode(
96 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
97 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
98
99 if (ret_code_or_samples < OPUS_OK) {
100 return ret_code_or_samples;
101 }
102
103 out_sample_count = ret_code_or_samples;
104 return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
105}
106
107} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.h b/src/audio_core/adsp/apps/opus/opus_decode_object.h
new file mode 100644
index 000000000..6425f987c
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_decode_object.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <opus.h>
7
8#include "common/common_types.h"
9
10namespace AudioCore::ADSP::OpusDecoder {
11using LibOpusDecoder = ::OpusDecoder;
12static constexpr u32 DecodeObjectMagic = 0xDEADBEEF;
13
14class OpusDecodeObject {
15public:
16 static u32 GetWorkBufferSize(u32 channel_count);
17 static OpusDecodeObject& Initialize(u64 buffer, u64 buffer2);
18
19 s32 InitializeDecoder(u32 sample_rate, u32 channel_count);
20 s32 Shutdown();
21 s32 ResetDecoder();
22 s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
23 u64 input_data_size);
24 u32 GetFinalRange() const noexcept {
25 return final_range;
26 }
27
28private:
29 u32 magic;
30 bool initialized;
31 bool state_valid;
32 OpusDecodeObject* self;
33 u32 final_range;
34 LibOpusDecoder* decoder;
35};
36static_assert(std::is_trivially_constructible_v<OpusDecodeObject>);
37
38} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.cpp b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
new file mode 100644
index 000000000..2084de128
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_decoder.cpp
@@ -0,0 +1,269 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <chrono>
6
7#include "audio_core/adsp/apps/opus/opus_decode_object.h"
8#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
9#include "audio_core/adsp/apps/opus/shared_memory.h"
10#include "audio_core/audio_core.h"
11#include "audio_core/common/common.h"
12#include "common/logging/log.h"
13#include "common/microprofile.h"
14#include "common/thread.h"
15#include "core/core.h"
16#include "core/core_timing.h"
17
18MICROPROFILE_DEFINE(OpusDecoder, "Audio", "DSP_OpusDecoder", MP_RGB(60, 19, 97));
19
20namespace AudioCore::ADSP::OpusDecoder {
21
22namespace {
23constexpr size_t OpusStreamCountMax = 255;
24
25bool IsValidChannelCount(u32 channel_count) {
26 return channel_count == 1 || channel_count == 2;
27}
28
29bool IsValidMultiStreamChannelCount(u32 channel_count) {
30 return channel_count <= OpusStreamCountMax;
31}
32
33bool IsValidMultiStreamStreamCounts(s32 total_stream_count, s32 sterero_stream_count) {
34 return IsValidMultiStreamChannelCount(total_stream_count) && total_stream_count > 0 &&
35 sterero_stream_count > 0 && sterero_stream_count <= total_stream_count;
36}
37} // namespace
38
39OpusDecoder::OpusDecoder(Core::System& system_) : system{system_} {
40 init_thread = std::jthread([this](std::stop_token stop_token) { Init(stop_token); });
41}
42
43OpusDecoder::~OpusDecoder() {
44 if (!running) {
45 init_thread.request_stop();
46 return;
47 }
48
49 // Shutdown the thread
50 Send(Direction::DSP, Message::Shutdown);
51 auto msg = Receive(Direction::Host);
52 ASSERT_MSG(msg == Message::ShutdownOK, "Expected Opus shutdown code {}, got {}",
53 Message::ShutdownOK, msg);
54 main_thread.request_stop();
55 main_thread.join();
56 running = false;
57}
58
59void OpusDecoder::Send(Direction dir, u32 message) {
60 mailbox.Send(dir, std::move(message));
61}
62
63u32 OpusDecoder::Receive(Direction dir, std::stop_token stop_token) {
64 return mailbox.Receive(dir, stop_token);
65}
66
67void OpusDecoder::Init(std::stop_token stop_token) {
68 Common::SetCurrentThreadName("DSP_OpusDecoder_Init");
69
70 if (Receive(Direction::DSP, stop_token) != Message::Start) {
71 LOG_ERROR(Service_Audio,
72 "DSP OpusDecoder failed to receive Start message. Opus initialization failed.");
73 return;
74 }
75 main_thread = std::jthread([this](std::stop_token st) { Main(st); });
76 running = true;
77 Send(Direction::Host, Message::StartOK);
78}
79
80void OpusDecoder::Main(std::stop_token stop_token) {
81 Common::SetCurrentThreadName("DSP_OpusDecoder_Main");
82
83 while (!stop_token.stop_requested()) {
84 auto msg = Receive(Direction::DSP, stop_token);
85 switch (msg) {
86 case Shutdown:
87 Send(Direction::Host, Message::ShutdownOK);
88 return;
89
90 case GetWorkBufferSize: {
91 auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
92
93 ASSERT(IsValidChannelCount(channel_count));
94
95 shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
96 Send(Direction::Host, Message::GetWorkBufferSizeOK);
97 } break;
98
99 case InitializeDecodeObject: {
100 auto buffer = shared_memory->host_send_data[0];
101 auto buffer_size = shared_memory->host_send_data[1];
102 auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
103 auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
104
105 ASSERT(sample_rate >= 0);
106 ASSERT(IsValidChannelCount(channel_count));
107 ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
108
109 auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
110 shared_memory->dsp_return_data[0] =
111 decoder_object.InitializeDecoder(sample_rate, channel_count);
112
113 Send(Direction::Host, Message::InitializeDecodeObjectOK);
114 } break;
115
116 case ShutdownDecodeObject: {
117 auto buffer = shared_memory->host_send_data[0];
118 [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
119
120 auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
121 shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
122
123 Send(Direction::Host, Message::ShutdownDecodeObjectOK);
124 } break;
125
126 case DecodeInterleaved: {
127 auto start_time = system.CoreTiming().GetGlobalTimeUs();
128
129 auto buffer = shared_memory->host_send_data[0];
130 auto input_data = shared_memory->host_send_data[1];
131 auto input_data_size = shared_memory->host_send_data[2];
132 auto output_data = shared_memory->host_send_data[3];
133 auto output_data_size = shared_memory->host_send_data[4];
134 auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
135 auto reset_requested = shared_memory->host_send_data[6];
136
137 u32 decoded_samples{0};
138
139 auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);
140 s32 error_code{OPUS_OK};
141 if (reset_requested) {
142 error_code = decoder_object.ResetDecoder();
143 }
144
145 if (error_code == OPUS_OK) {
146 error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
147 input_data, input_data_size);
148 }
149
150 if (error_code == OPUS_OK) {
151 if (final_range && decoder_object.GetFinalRange() != final_range) {
152 error_code = OPUS_INVALID_PACKET;
153 }
154 }
155
156 auto end_time = system.CoreTiming().GetGlobalTimeUs();
157 shared_memory->dsp_return_data[0] = error_code;
158 shared_memory->dsp_return_data[1] = decoded_samples;
159 shared_memory->dsp_return_data[2] = (end_time - start_time).count();
160
161 Send(Direction::Host, Message::DecodeInterleavedOK);
162 } break;
163
164 case MapMemory: {
165 [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
166 [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
167 Send(Direction::Host, Message::MapMemoryOK);
168 } break;
169
170 case UnmapMemory: {
171 [[maybe_unused]] auto buffer = shared_memory->host_send_data[0];
172 [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
173 Send(Direction::Host, Message::UnmapMemoryOK);
174 } break;
175
176 case GetWorkBufferSizeForMultiStream: {
177 auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[0]);
178 auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[1]);
179
180 ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
181
182 shared_memory->dsp_return_data[0] = OpusMultiStreamDecodeObject::GetWorkBufferSize(
183 total_stream_count, stereo_stream_count);
184 Send(Direction::Host, Message::GetWorkBufferSizeForMultiStreamOK);
185 } break;
186
187 case InitializeMultiStreamDecodeObject: {
188 auto buffer = shared_memory->host_send_data[0];
189 auto buffer_size = shared_memory->host_send_data[1];
190 auto sample_rate = static_cast<s32>(shared_memory->host_send_data[2]);
191 auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
192 auto total_stream_count = static_cast<s32>(shared_memory->host_send_data[4]);
193 auto stereo_stream_count = static_cast<s32>(shared_memory->host_send_data[5]);
194 // Nintendo seem to have a bug here, they try to use &host_send_data[6] for the channel
195 // mappings, but [6] is never set, and there is not enough room in the argument data for
196 // more than 40 channels, when 255 are possible.
197 // It also means the mapping values are undefined, though likely always 0,
198 // and the mappings given by the game are ignored. The mappings are copied to this
199 // dedicated buffer host side, so let's do as intended.
200 auto mappings = shared_memory->channel_mapping.data();
201
202 ASSERT(IsValidMultiStreamStreamCounts(total_stream_count, stereo_stream_count));
203 ASSERT(sample_rate >= 0);
204 ASSERT(buffer_size >= OpusMultiStreamDecodeObject::GetWorkBufferSize(
205 total_stream_count, stereo_stream_count));
206
207 auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
208 shared_memory->dsp_return_data[0] = decoder_object.InitializeDecoder(
209 sample_rate, total_stream_count, channel_count, stereo_stream_count, mappings);
210
211 Send(Direction::Host, Message::InitializeMultiStreamDecodeObjectOK);
212 } break;
213
214 case ShutdownMultiStreamDecodeObject: {
215 auto buffer = shared_memory->host_send_data[0];
216 [[maybe_unused]] auto buffer_size = shared_memory->host_send_data[1];
217
218 auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
219 shared_memory->dsp_return_data[0] = decoder_object.Shutdown();
220
221 Send(Direction::Host, Message::ShutdownMultiStreamDecodeObjectOK);
222 } break;
223
224 case DecodeInterleavedForMultiStream: {
225 auto start_time = system.CoreTiming().GetGlobalTimeUs();
226
227 auto buffer = shared_memory->host_send_data[0];
228 auto input_data = shared_memory->host_send_data[1];
229 auto input_data_size = shared_memory->host_send_data[2];
230 auto output_data = shared_memory->host_send_data[3];
231 auto output_data_size = shared_memory->host_send_data[4];
232 auto final_range = static_cast<u32>(shared_memory->host_send_data[5]);
233 auto reset_requested = shared_memory->host_send_data[6];
234
235 u32 decoded_samples{0};
236
237 auto& decoder_object = OpusMultiStreamDecodeObject::Initialize(buffer, buffer);
238 s32 error_code{OPUS_OK};
239 if (reset_requested) {
240 error_code = decoder_object.ResetDecoder();
241 }
242
243 if (error_code == OPUS_OK) {
244 error_code = decoder_object.Decode(decoded_samples, output_data, output_data_size,
245 input_data, input_data_size);
246 }
247
248 if (error_code == OPUS_OK) {
249 if (final_range && decoder_object.GetFinalRange() != final_range) {
250 error_code = OPUS_INVALID_PACKET;
251 }
252 }
253
254 auto end_time = system.CoreTiming().GetGlobalTimeUs();
255 shared_memory->dsp_return_data[0] = error_code;
256 shared_memory->dsp_return_data[1] = decoded_samples;
257 shared_memory->dsp_return_data[2] = (end_time - start_time).count();
258
259 Send(Direction::Host, Message::DecodeInterleavedForMultiStreamOK);
260 } break;
261
262 default:
263 LOG_ERROR(Service_Audio, "Invalid OpusDecoder command {}", msg);
264 continue;
265 }
266 }
267}
268
269} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_decoder.h b/src/audio_core/adsp/apps/opus/opus_decoder.h
new file mode 100644
index 000000000..fcb89bb40
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_decoder.h
@@ -0,0 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <thread>
8
9#include "audio_core/adsp/apps/opus/shared_memory.h"
10#include "audio_core/adsp/mailbox.h"
11#include "common/common_types.h"
12
13namespace Core {
14class System;
15} // namespace Core
16
17namespace AudioCore::ADSP::OpusDecoder {
18
19enum Message : u32 {
20 Invalid = 0,
21 Start = 1,
22 Shutdown = 2,
23 StartOK = 11,
24 ShutdownOK = 12,
25 GetWorkBufferSize = 21,
26 InitializeDecodeObject = 22,
27 ShutdownDecodeObject = 23,
28 DecodeInterleaved = 24,
29 MapMemory = 25,
30 UnmapMemory = 26,
31 GetWorkBufferSizeForMultiStream = 27,
32 InitializeMultiStreamDecodeObject = 28,
33 ShutdownMultiStreamDecodeObject = 29,
34 DecodeInterleavedForMultiStream = 30,
35
36 GetWorkBufferSizeOK = 41,
37 InitializeDecodeObjectOK = 42,
38 ShutdownDecodeObjectOK = 43,
39 DecodeInterleavedOK = 44,
40 MapMemoryOK = 45,
41 UnmapMemoryOK = 46,
42 GetWorkBufferSizeForMultiStreamOK = 47,
43 InitializeMultiStreamDecodeObjectOK = 48,
44 ShutdownMultiStreamDecodeObjectOK = 49,
45 DecodeInterleavedForMultiStreamOK = 50,
46};
47
48/**
49 * The AudioRenderer application running on the ADSP.
50 */
51class OpusDecoder {
52public:
53 explicit OpusDecoder(Core::System& system);
54 ~OpusDecoder();
55
56 bool IsRunning() const noexcept {
57 return running;
58 }
59
60 void Send(Direction dir, u32 message);
61 u32 Receive(Direction dir, std::stop_token stop_token = {});
62
63 void SetSharedMemory(SharedMemory& shared_memory_) {
64 shared_memory = &shared_memory_;
65 }
66
67private:
68 /**
69 * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
70 */
71 void Init(std::stop_token stop_token);
72 /**
73 * Main OpusDecoder thread, responsible for processing the incoming Opus packets.
74 */
75 void Main(std::stop_token stop_token);
76
77 /// Core system
78 Core::System& system;
79 /// Mailbox to communicate messages with the host, drives the main thread
80 Mailbox mailbox;
81 /// Init thread
82 std::jthread init_thread{};
83 /// Main thread
84 std::jthread main_thread{};
85 /// The current state
86 bool running{};
87 /// Structure shared with the host, input data set by the host before sending a mailbox message,
88 /// and the responses are written back by the OpusDecoder.
89 SharedMemory* shared_memory{};
90};
91
92} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
new file mode 100644
index 000000000..f6d362e68
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h"
5#include "common/assert.h"
6
7namespace AudioCore::ADSP::OpusDecoder {
8
9namespace {
10bool IsValidChannelCount(u32 channel_count) {
11 return channel_count == 1 || channel_count == 2;
12}
13
14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
15 return total_stream_count > 0 && stereo_stream_count > 0 &&
16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
17}
18} // namespace
19
20u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count,
21 u32 stereo_stream_count) {
22 if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) {
23 return static_cast<u32>(sizeof(OpusMultiStreamDecodeObject)) +
24 opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count);
25 }
26 return 0;
27}
28
29OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) {
30 auto* new_decoder = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer);
31 auto* comparison = reinterpret_cast<OpusMultiStreamDecodeObject*>(buffer2);
32
33 if (new_decoder->magic == DecodeMultiStreamObjectMagic) {
34 if (!new_decoder->initialized ||
35 (new_decoder->initialized && new_decoder->self == comparison)) {
36 new_decoder->state_valid = true;
37 }
38 } else {
39 new_decoder->initialized = false;
40 new_decoder->state_valid = true;
41 }
42 return *new_decoder;
43}
44
45s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count,
46 u32 channel_count, u32 stereo_stream_count,
47 u8* mappings) {
48 if (!state_valid) {
49 return OPUS_INVALID_STATE;
50 }
51
52 if (initialized) {
53 return OPUS_OK;
54 }
55
56 // See OpusDecodeObject::InitializeDecoder for an explanation of this
57 decoder = (LibOpusMSDecoder*)(this + 1);
58 s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count,
59 stereo_stream_count, mappings);
60 if (ret == OPUS_OK) {
61 magic = DecodeMultiStreamObjectMagic;
62 initialized = true;
63 state_valid = true;
64 self = this;
65 final_range = 0;
66 }
67 return ret;
68}
69
70s32 OpusMultiStreamDecodeObject::Shutdown() {
71 if (!state_valid) {
72 return OPUS_INVALID_STATE;
73 }
74
75 if (initialized) {
76 magic = 0x0;
77 initialized = false;
78 state_valid = false;
79 self = nullptr;
80 final_range = 0;
81 decoder = nullptr;
82 }
83 return OPUS_OK;
84}
85
86s32 OpusMultiStreamDecodeObject::ResetDecoder() {
87 return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
88}
89
90s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data,
91 u64 output_data_size, u64 input_data, u64 input_data_size) {
92 ASSERT(initialized);
93 out_sample_count = 0;
94
95 if (!state_valid) {
96 return OPUS_INVALID_STATE;
97 }
98
99 auto ret_code_or_samples = opus_multistream_decode(
100 decoder, reinterpret_cast<const u8*>(input_data), static_cast<opus_int32>(input_data_size),
101 reinterpret_cast<opus_int16*>(output_data), static_cast<opus_int32>(output_data_size), 0);
102
103 if (ret_code_or_samples < OPUS_OK) {
104 return ret_code_or_samples;
105 }
106
107 out_sample_count = ret_code_or_samples;
108 return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range);
109}
110
111} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h
new file mode 100644
index 000000000..93558ded5
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <opus_multistream.h>
7
8#include "common/common_types.h"
9
10namespace AudioCore::ADSP::OpusDecoder {
11using LibOpusMSDecoder = ::OpusMSDecoder;
12static constexpr u32 DecodeMultiStreamObjectMagic = 0xDEADBEEF;
13
14class OpusMultiStreamDecodeObject {
15public:
16 static u32 GetWorkBufferSize(u32 total_stream_count, u32 stereo_stream_count);
17 static OpusMultiStreamDecodeObject& Initialize(u64 buffer, u64 buffer2);
18
19 s32 InitializeDecoder(u32 sample_rate, u32 total_stream_count, u32 channel_count,
20 u32 stereo_stream_count, u8* mappings);
21 s32 Shutdown();
22 s32 ResetDecoder();
23 s32 Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, u64 input_data,
24 u64 input_data_size);
25 u32 GetFinalRange() const noexcept {
26 return final_range;
27 }
28
29private:
30 u32 magic;
31 bool initialized;
32 bool state_valid;
33 OpusMultiStreamDecodeObject* self;
34 u32 final_range;
35 LibOpusMSDecoder* decoder;
36};
37static_assert(std::is_trivially_constructible_v<OpusMultiStreamDecodeObject>);
38
39} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/apps/opus/shared_memory.h b/src/audio_core/adsp/apps/opus/shared_memory.h
new file mode 100644
index 000000000..c696731ed
--- /dev/null
+++ b/src/audio_core/adsp/apps/opus/shared_memory.h
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace AudioCore::ADSP::OpusDecoder {
10
11struct SharedMemory {
12 std::array<u8, 0x100> channel_mapping{};
13 std::array<u64, 16> host_send_data{};
14 std::array<u64, 16> dsp_return_data{};
15};
16
17} // namespace AudioCore::ADSP::OpusDecoder
diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h
index c31b73717..1dd40ebfa 100644
--- a/src/audio_core/adsp/mailbox.h
+++ b/src/audio_core/adsp/mailbox.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
7
6#include "common/bounded_threadsafe_queue.h" 8#include "common/bounded_threadsafe_queue.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
@@ -19,11 +21,6 @@ enum class Direction : u32 {
19 DSP, 21 DSP,
20}; 22};
21 23
22struct MailboxMessage {
23 u32 msg;
24 std::span<u8> data;
25};
26
27class Mailbox { 24class Mailbox {
28public: 25public:
29 void Initialize(AppMailboxId id_) { 26 void Initialize(AppMailboxId id_) {
@@ -35,25 +32,19 @@ public:
35 return id; 32 return id;
36 } 33 }
37 34
38 void Send(Direction dir, MailboxMessage&& message) { 35 void Send(Direction dir, u32 message) {
39 auto& queue = dir == Direction::Host ? host_queue : adsp_queue; 36 auto& queue = dir == Direction::Host ? host_queue : adsp_queue;
40 queue.EmplaceWait(std::move(message)); 37 queue.EmplaceWait(message);
41 } 38 }
42 39
43 MailboxMessage Receive(Direction dir, bool block = true) { 40 u32 Receive(Direction dir, std::stop_token stop_token = {}) {
44 auto& queue = dir == Direction::Host ? host_queue : adsp_queue; 41 auto& queue = dir == Direction::Host ? host_queue : adsp_queue;
45 MailboxMessage t; 42 return queue.PopWait(stop_token);
46 if (block) {
47 queue.PopWait(t);
48 } else {
49 queue.TryPop(t);
50 }
51 return t;
52 } 43 }
53 44
54 void Reset() { 45 void Reset() {
55 id = AppMailboxId::Invalid; 46 id = AppMailboxId::Invalid;
56 MailboxMessage t; 47 u32 t{};
57 while (host_queue.TryPop(t)) { 48 while (host_queue.TryPop(t)) {
58 } 49 }
59 while (adsp_queue.TryPop(t)) { 50 while (adsp_queue.TryPop(t)) {
@@ -62,8 +53,8 @@ public:
62 53
63private: 54private:
64 AppMailboxId id{0}; 55 AppMailboxId id{0};
65 Common::SPSCQueue<MailboxMessage> host_queue; 56 Common::SPSCQueue<u32> host_queue;
66 Common::SPSCQueue<MailboxMessage> adsp_queue; 57 Common::SPSCQueue<u32> adsp_queue;
67}; 58};
68 59
69} // namespace AudioCore::ADSP 60} // namespace AudioCore::ADSP
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp
new file mode 100644
index 000000000..5b23fce14
--- /dev/null
+++ b/src/audio_core/opus/decoder.cpp
@@ -0,0 +1,179 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/opus/decoder.h"
5#include "audio_core/opus/hardware_opus.h"
6#include "audio_core/opus/parameters.h"
7#include "common/alignment.h"
8#include "common/swap.h"
9#include "core/core.h"
10
11namespace AudioCore::OpusDecoder {
12using namespace Service::Audio;
13namespace {
14OpusPacketHeader ReverseHeader(OpusPacketHeader header) {
15 OpusPacketHeader out;
16 out.size = Common::swap32(header.size);
17 out.final_range = Common::swap32(header.final_range);
18 return out;
19}
20} // namespace
21
22OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
23 : system{system_}, hardware_opus{hardware_opus_} {}
24
25OpusDecoder::~OpusDecoder() {
26 if (decode_object_initialized) {
27 hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size);
28 }
29}
30
31Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
32 u64 transfer_memory_size) {
33 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
34 shared_buffer_size = transfer_memory_size;
35 shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
36 shared_memory_mapped = true;
37
38 buffer_size =
39 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
40
41 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
42 size_t in_data_size{0x600u};
43 in_data = {out_data.data() - in_data_size, in_data_size};
44
45 ON_RESULT_FAILURE {
46 if (shared_memory_mapped) {
47 shared_memory_mapped = false;
48 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
49 }
50 };
51
52 R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count,
53 shared_buffer.get(), shared_buffer_size));
54
55 sample_rate = params.sample_rate;
56 channel_count = params.channel_count;
57 use_large_frame_size = params.use_large_frame_size;
58 decode_object_initialized = true;
59 R_SUCCEED();
60}
61
62Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params,
63 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
64 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
65 shared_buffer_size = transfer_memory_size;
66 shared_buffer = std::make_unique<u8[]>(shared_buffer_size);
67 shared_memory_mapped = true;
68
69 buffer_size =
70 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16);
71
72 out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size};
73 size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)};
74 in_data = {out_data.data() - in_data_size, in_data_size};
75
76 ON_RESULT_FAILURE {
77 if (shared_memory_mapped) {
78 shared_memory_mapped = false;
79 ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size)));
80 }
81 };
82
83 R_TRY(hardware_opus.InitializeMultiStreamDecodeObject(
84 params.sample_rate, params.channel_count, params.total_stream_count,
85 params.stereo_stream_count, params.mappings.data(), shared_buffer.get(),
86 shared_buffer_size));
87
88 sample_rate = params.sample_rate;
89 channel_count = params.channel_count;
90 total_stream_count = params.total_stream_count;
91 stereo_stream_count = params.stereo_stream_count;
92 use_large_frame_size = params.use_large_frame_size;
93 decode_object_initialized = true;
94 R_SUCCEED();
95}
96
97Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken,
98 u32* out_sample_count, std::span<const u8> input_data,
99 std::span<u8> output_data, bool reset) {
100 u32 out_samples;
101 u64 time_taken{};
102
103 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
104
105 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
106 OpusPacketHeader header{ReverseHeader(*header_p)};
107
108 R_UNLESS(in_data.size_bytes() >= header.size &&
109 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
110 ResultBufferTooSmall);
111
112 if (!shared_memory_mapped) {
113 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
114 shared_memory_mapped = true;
115 }
116
117 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
118
119 R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(),
120 channel_count, in_data.data(), header.size,
121 shared_buffer.get(), time_taken, reset));
122
123 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
124
125 *out_data_size = header.size + sizeof(OpusPacketHeader);
126 *out_sample_count = out_samples;
127 if (out_time_taken) {
128 *out_time_taken = time_taken / 1000;
129 }
130 R_SUCCEED();
131}
132
133Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) {
134 R_SUCCEED_IF(shared_memory_mapped);
135 shared_memory_mapped = true;
136 R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
137}
138
139Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
140 u32* out_sample_count,
141 std::span<const u8> input_data,
142 std::span<u8> output_data, bool reset) {
143 u32 out_samples;
144 u64 time_taken{};
145
146 R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall);
147
148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
149 OpusPacketHeader header{ReverseHeader(*header_p)};
150
151 LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
152 header.size, input_data.size_bytes(), in_data.size_bytes());
153
154 R_UNLESS(in_data.size_bytes() >= header.size &&
155 header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(),
156 ResultBufferTooSmall);
157
158 if (!shared_memory_mapped) {
159 R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size));
160 shared_memory_mapped = true;
161 }
162
163 std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size);
164
165 R_TRY(hardware_opus.DecodeInterleavedForMultiStream(
166 out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(),
167 header.size, shared_buffer.get(), time_taken, reset));
168
169 std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16));
170
171 *out_data_size = header.size + sizeof(OpusPacketHeader);
172 *out_sample_count = out_samples;
173 if (out_time_taken) {
174 *out_time_taken = time_taken / 1000;
175 }
176 R_SUCCEED();
177}
178
179} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h
new file mode 100644
index 000000000..d08d8a4a4
--- /dev/null
+++ b/src/audio_core/opus/decoder.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/opus/parameters.h"
9#include "common/common_types.h"
10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/service/audio/errors.h"
12
13namespace Core {
14class System;
15}
16
17namespace AudioCore::OpusDecoder {
18class HardwareOpus;
19
20class OpusDecoder {
21public:
22 explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_);
23 ~OpusDecoder();
24
25 Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
26 u64 transfer_memory_size);
27 Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
28 u64 transfer_memory_size);
29 Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count,
30 std::span<const u8> input_data, std::span<u8> output_data, bool reset);
31 Result SetContext([[maybe_unused]] std::span<const u8> context);
32 Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken,
33 u32* out_sample_count, std::span<const u8> input_data,
34 std::span<u8> output_data, bool reset);
35
36private:
37 Core::System& system;
38 HardwareOpus& hardware_opus;
39 std::unique_ptr<u8[]> shared_buffer{};
40 u64 shared_buffer_size;
41 std::span<u8> in_data{};
42 std::span<u8> out_data{};
43 u64 buffer_size{};
44 s32 sample_rate{};
45 s32 channel_count{};
46 bool use_large_frame_size{false};
47 s32 total_stream_count{};
48 s32 stereo_stream_count{};
49 bool shared_memory_mapped{false};
50 bool decode_object_initialized{false};
51};
52
53} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp
new file mode 100644
index 000000000..4a5382973
--- /dev/null
+++ b/src/audio_core/opus/decoder_manager.cpp
@@ -0,0 +1,102 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/adsp/apps/opus/opus_decoder.h"
5#include "audio_core/opus/decoder_manager.h"
6#include "common/alignment.h"
7#include "core/core.h"
8
9namespace AudioCore::OpusDecoder {
10using namespace Service::Audio;
11
12namespace {
13bool IsValidChannelCount(u32 channel_count) {
14 return channel_count == 1 || channel_count == 2;
15}
16
17bool IsValidMultiStreamChannelCount(u32 channel_count) {
18 return channel_count > 0 && channel_count <= OpusStreamCountMax;
19}
20
21bool IsValidSampleRate(u32 sample_rate) {
22 return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 ||
23 sample_rate == 24'000 || sample_rate == 48'000;
24}
25
26bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) {
27 return total_stream_count > 0 && stereo_stream_count > 0 &&
28 stereo_stream_count <= total_stream_count &&
29 total_stream_count + stereo_stream_count <= channel_count;
30}
31
32} // namespace
33
34OpusDecoderManager::OpusDecoderManager(Core::System& system_)
35 : system{system_}, hardware_opus{system} {
36 for (u32 i = 0; i < MaxChannels; i++) {
37 required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i);
38 }
39}
40
41Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) {
42 OpusParametersEx ex{
43 .sample_rate = params.sample_rate,
44 .channel_count = params.channel_count,
45 .use_large_frame_size = false,
46 };
47 R_RETURN(GetWorkBufferSizeExEx(ex, out_size));
48}
49
50Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) {
51 R_RETURN(GetWorkBufferSizeExEx(params, out_size));
52}
53
54Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) {
55 R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
56 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
57
58 auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]};
59 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
60 work_buffer_size +=
61 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
62 out_size = work_buffer_size + 0x600;
63 R_SUCCEED();
64}
65
66Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params,
67 u64& out_size) {
68 OpusMultiStreamParametersEx ex{
69 .sample_rate = params.sample_rate,
70 .channel_count = params.channel_count,
71 .total_stream_count = params.total_stream_count,
72 .stereo_stream_count = params.stereo_stream_count,
73 .use_large_frame_size = false,
74 .mappings = {},
75 };
76 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size));
77}
78
79Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params,
80 u64& out_size) {
81 R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size));
82}
83
84Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params,
85 u64& out_size) {
86 R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount);
87 R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate);
88 R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count,
89 params.stereo_stream_count),
90 ResultInvalidOpusSampleRate);
91
92 auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream(
93 params.total_stream_count, params.stereo_stream_count)};
94 auto frame_size{params.use_large_frame_size ? 5760 : 1920};
95 work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64);
96 work_buffer_size +=
97 Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64);
98 out_size = work_buffer_size;
99 R_SUCCEED();
100}
101
102} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h
new file mode 100644
index 000000000..466e1967b
--- /dev/null
+++ b/src/audio_core/opus/decoder_manager.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/opus/hardware_opus.h"
7#include "audio_core/opus/parameters.h"
8#include "common/common_types.h"
9#include "core/hle/service/audio/errors.h"
10
11namespace Core {
12class System;
13}
14
15namespace AudioCore::OpusDecoder {
16
17class OpusDecoderManager {
18public:
19 OpusDecoderManager(Core::System& system);
20
21 HardwareOpus& GetHardwareOpus() {
22 return hardware_opus;
23 }
24
25 Result GetWorkBufferSize(OpusParameters& params, u64& out_size);
26 Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size);
27 Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size);
28 Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size);
29 Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size);
30 Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size);
31
32private:
33 Core::System& system;
34 HardwareOpus hardware_opus;
35 std::array<u64, MaxChannels> required_workbuffer_sizes{};
36};
37
38} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp
new file mode 100644
index 000000000..d6544dcb0
--- /dev/null
+++ b/src/audio_core/opus/hardware_opus.cpp
@@ -0,0 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5
6#include "audio_core/audio_core.h"
7#include "audio_core/opus/hardware_opus.h"
8#include "core/core.h"
9
10namespace AudioCore::OpusDecoder {
11namespace {
12using namespace Service::Audio;
13
14static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) {
15 s32 error{static_cast<s32>(error_code)};
16 ASSERT(error <= OPUS_OK);
17 switch (error) {
18 case OPUS_ALLOC_FAIL:
19 R_THROW(ResultLibOpusAllocFail);
20 case OPUS_INVALID_STATE:
21 R_THROW(ResultLibOpusInvalidState);
22 case OPUS_UNIMPLEMENTED:
23 R_THROW(ResultLibOpusUnimplemented);
24 case OPUS_INVALID_PACKET:
25 R_THROW(ResultLibOpusInvalidPacket);
26 case OPUS_INTERNAL_ERROR:
27 R_THROW(ResultLibOpusInternalError);
28 case OPUS_BUFFER_TOO_SMALL:
29 R_THROW(ResultBufferTooSmall);
30 case OPUS_BAD_ARG:
31 R_THROW(ResultLibOpusBadArg);
32 case OPUS_OK:
33 R_RETURN(ResultSuccess);
34 }
35 UNREACHABLE();
36}
37
38} // namespace
39
40HardwareOpus::HardwareOpus(Core::System& system_)
41 : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} {
42 opus_decoder.SetSharedMemory(shared_memory);
43}
44
45u64 HardwareOpus::GetWorkBufferSize(u32 channel) {
46 if (!opus_decoder.IsRunning()) {
47 return 0;
48 }
49 std::scoped_lock l{mutex};
50 shared_memory.host_send_data[0] = channel;
51 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize);
52 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
53 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) {
54 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
55 ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg);
56 return 0;
57 }
58 return shared_memory.dsp_return_data[0];
59}
60
61u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) {
62 std::scoped_lock l{mutex};
63 shared_memory.host_send_data[0] = total_stream_count;
64 shared_memory.host_send_data[1] = stereo_stream_count;
65 opus_decoder.Send(ADSP::Direction::DSP,
66 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream);
67 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
68 if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) {
69 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
70 ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg);
71 return 0;
72 }
73 return shared_memory.dsp_return_data[0];
74}
75
76Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
77 u64 buffer_size) {
78 std::scoped_lock l{mutex};
79 shared_memory.host_send_data[0] = (u64)buffer;
80 shared_memory.host_send_data[1] = buffer_size;
81 shared_memory.host_send_data[2] = sample_rate;
82 shared_memory.host_send_data[3] = channel_count;
83
84 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject);
85 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
86 if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) {
87 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
88 ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg);
89 R_THROW(ResultInvalidOpusDSPReturnCode);
90 }
91
92 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
93}
94
95Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
96 u32 total_stream_count,
97 u32 stereo_stream_count, void* mappings,
98 void* buffer, u64 buffer_size) {
99 std::scoped_lock l{mutex};
100 shared_memory.host_send_data[0] = (u64)buffer;
101 shared_memory.host_send_data[1] = buffer_size;
102 shared_memory.host_send_data[2] = sample_rate;
103 shared_memory.host_send_data[3] = channel_count;
104 shared_memory.host_send_data[4] = total_stream_count;
105 shared_memory.host_send_data[5] = stereo_stream_count;
106
107 ASSERT(channel_count <= MaxChannels);
108 std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
109
110 opus_decoder.Send(ADSP::Direction::DSP,
111 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject);
112 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
113 if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) {
114 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
115 ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg);
116 R_THROW(ResultInvalidOpusDSPReturnCode);
117 }
118
119 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
120}
121
122Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) {
123 std::scoped_lock l{mutex};
124 shared_memory.host_send_data[0] = (u64)buffer;
125 shared_memory.host_send_data[1] = buffer_size;
126
127 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject);
128 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
129 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK,
130 "Expected Opus shutdown code {}, got {}",
131 ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg);
132
133 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
134}
135
136Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) {
137 std::scoped_lock l{mutex};
138 shared_memory.host_send_data[0] = (u64)buffer;
139 shared_memory.host_send_data[1] = buffer_size;
140
141 opus_decoder.Send(ADSP::Direction::DSP,
142 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject);
143 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
144 ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK,
145 "Expected Opus shutdown code {}, got {}",
146 ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg);
147
148 R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0]));
149}
150
151Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data,
152 u64 output_data_size, u32 channel_count, void* input_data,
153 u64 input_data_size, void* buffer, u64& out_time_taken,
154 bool reset) {
155 std::scoped_lock l{mutex};
156 shared_memory.host_send_data[0] = (u64)buffer;
157 shared_memory.host_send_data[1] = (u64)input_data;
158 shared_memory.host_send_data[2] = input_data_size;
159 shared_memory.host_send_data[3] = (u64)output_data;
160 shared_memory.host_send_data[4] = output_data_size;
161 shared_memory.host_send_data[5] = 0;
162 shared_memory.host_send_data[6] = reset;
163
164 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved);
165 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
166 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) {
167 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
168 ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg);
169 R_THROW(ResultInvalidOpusDSPReturnCode);
170 }
171
172 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
173 if (error_code == OPUS_OK) {
174 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
175 out_time_taken = 1000 * shared_memory.dsp_return_data[2];
176 }
177 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
178}
179
180Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
181 u64 output_data_size, u32 channel_count,
182 void* input_data, u64 input_data_size,
183 void* buffer, u64& out_time_taken,
184 bool reset) {
185 std::scoped_lock l{mutex};
186 shared_memory.host_send_data[0] = (u64)buffer;
187 shared_memory.host_send_data[1] = (u64)input_data;
188 shared_memory.host_send_data[2] = input_data_size;
189 shared_memory.host_send_data[3] = (u64)output_data;
190 shared_memory.host_send_data[4] = output_data_size;
191 shared_memory.host_send_data[5] = 0;
192 shared_memory.host_send_data[6] = reset;
193
194 opus_decoder.Send(ADSP::Direction::DSP,
195 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream);
196 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
197 if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) {
198 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
199 ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg);
200 R_THROW(ResultInvalidOpusDSPReturnCode);
201 }
202
203 auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])};
204 if (error_code == OPUS_OK) {
205 out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]);
206 out_time_taken = 1000 * shared_memory.dsp_return_data[2];
207 }
208 R_RETURN(ResultCodeFromLibOpusErrorCode(error_code));
209}
210
211Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) {
212 std::scoped_lock l{mutex};
213 shared_memory.host_send_data[0] = (u64)buffer;
214 shared_memory.host_send_data[1] = buffer_size;
215
216 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory);
217 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
218 if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) {
219 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
220 ADSP::OpusDecoder::Message::MapMemoryOK, msg);
221 R_THROW(ResultInvalidOpusDSPReturnCode);
222 }
223 R_SUCCEED();
224}
225
226Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) {
227 std::scoped_lock l{mutex};
228 shared_memory.host_send_data[0] = (u64)buffer;
229 shared_memory.host_send_data[1] = buffer_size;
230
231 opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory);
232 auto msg = opus_decoder.Receive(ADSP::Direction::Host);
233 if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) {
234 LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}",
235 ADSP::OpusDecoder::Message::UnmapMemoryOK, msg);
236 R_THROW(ResultInvalidOpusDSPReturnCode);
237 }
238 R_SUCCEED();
239}
240
241} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h
new file mode 100644
index 000000000..7013a6b40
--- /dev/null
+++ b/src/audio_core/opus/hardware_opus.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include <opus.h>
8
9#include "audio_core/adsp/apps/opus/opus_decoder.h"
10#include "audio_core/adsp/apps/opus/shared_memory.h"
11#include "audio_core/adsp/mailbox.h"
12#include "core/hle/service/audio/errors.h"
13
14namespace AudioCore::OpusDecoder {
15class HardwareOpus {
16public:
17 HardwareOpus(Core::System& system);
18
19 u64 GetWorkBufferSize(u32 channel);
20 u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
21
22 Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer,
23 u64 buffer_size);
24 Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count,
25 u32 totaL_stream_count, u32 stereo_stream_count,
26 void* mappings, void* buffer, u64 buffer_size);
27 Result ShutdownDecodeObject(void* buffer, u64 buffer_size);
28 Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size);
29 Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size,
30 u32 channel_count, void* input_data, u64 input_data_size, void* buffer,
31 u64& out_time_taken, bool reset);
32 Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data,
33 u64 output_data_size, u32 channel_count,
34 void* input_data, u64 input_data_size, void* buffer,
35 u64& out_time_taken, bool reset);
36 Result MapMemory(void* buffer, u64 buffer_size);
37 Result UnmapMemory(void* buffer, u64 buffer_size);
38
39private:
40 Core::System& system;
41 std::mutex mutex;
42 ADSP::OpusDecoder::OpusDecoder& opus_decoder;
43 ADSP::OpusDecoder::SharedMemory shared_memory;
44};
45} // namespace AudioCore::OpusDecoder
diff --git a/src/audio_core/opus/parameters.h b/src/audio_core/opus/parameters.h
new file mode 100644
index 000000000..4c54b2825
--- /dev/null
+++ b/src/audio_core/opus/parameters.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace AudioCore::OpusDecoder {
10constexpr size_t OpusStreamCountMax = 255;
11constexpr size_t MaxChannels = 2;
12
13struct OpusParameters {
14 /* 0x00 */ u32 sample_rate;
15 /* 0x04 */ u32 channel_count;
16}; // size = 0x8
17static_assert(sizeof(OpusParameters) == 0x8, "OpusParameters has the wrong size!");
18
19struct OpusParametersEx {
20 /* 0x00 */ u32 sample_rate;
21 /* 0x04 */ u32 channel_count;
22 /* 0x08 */ bool use_large_frame_size;
23 /* 0x09 */ INSERT_PADDING_BYTES(7);
24}; // size = 0x10
25static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!");
26
27struct OpusMultiStreamParameters {
28 /* 0x00 */ u32 sample_rate;
29 /* 0x04 */ u32 channel_count;
30 /* 0x08 */ u32 total_stream_count;
31 /* 0x0C */ u32 stereo_stream_count;
32 /* 0x10 */ std::array<u8, OpusStreamCountMax + 1> mappings;
33}; // size = 0x110
34static_assert(sizeof(OpusMultiStreamParameters) == 0x110,
35 "OpusMultiStreamParameters has the wrong size!");
36
37struct OpusMultiStreamParametersEx {
38 /* 0x00 */ u32 sample_rate;
39 /* 0x04 */ u32 channel_count;
40 /* 0x08 */ u32 total_stream_count;
41 /* 0x0C */ u32 stereo_stream_count;
42 /* 0x10 */ bool use_large_frame_size;
43 /* 0x11 */ INSERT_PADDING_BYTES(7);
44 /* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings;
45}; // size = 0x118
46static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
47 "OpusMultiStreamParametersEx has the wrong size!");
48
49struct OpusPacketHeader {
50 /* 0x00 */ u32 size;
51 /* 0x04 */ u32 final_range;
52}; // size = 0x8
53static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusPacketHeader has the wrong size!");
54} // namespace AudioCore::OpusDecoder
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 a48a016b1..0f7aff1b4 100644
--- a/src/audio_core/renderer/command/command_processing_time_estimator.cpp
+++ b/src/audio_core/renderer/command/command_processing_time_estimator.cpp
@@ -27,12 +27,12 @@ u32 CommandProcessingTimeEstimatorVersion1::Estimate(
27 27
28u32 CommandProcessingTimeEstimatorVersion1::Estimate( 28u32 CommandProcessingTimeEstimatorVersion1::Estimate(
29 const AdpcmDataSourceVersion1Command& command) const { 29 const AdpcmDataSourceVersion1Command& command) const {
30 return static_cast<u32>(command.pitch * 0.25f * 1.2f); 30 return static_cast<u32>(command.pitch * 0.46f * 1.2f);
31} 31}
32 32
33u32 CommandProcessingTimeEstimatorVersion1::Estimate( 33u32 CommandProcessingTimeEstimatorVersion1::Estimate(
34 const AdpcmDataSourceVersion2Command& command) const { 34 const AdpcmDataSourceVersion2Command& command) const {
35 return static_cast<u32>(command.pitch * 0.25f * 1.2f); 35 return static_cast<u32>(command.pitch * 0.46f * 1.2f);
36} 36}
37 37
38u32 CommandProcessingTimeEstimatorVersion1::Estimate( 38u32 CommandProcessingTimeEstimatorVersion1::Estimate(
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index d29754634..31f92087c 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -684,11 +684,11 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
684 sink_context, splitter_context, perf_manager}; 684 sink_context, splitter_context, perf_manager};
685 685
686 voice_context.SortInfo(); 686 voice_context.SortInfo();
687 command_generator.GenerateVoiceCommands();
687 688
688 const auto start_estimated_time{drop_voice_param * 689 const auto start_estimated_time{drop_voice_param *
689 static_cast<f32>(command_buffer.estimated_process_time)}; 690 static_cast<f32>(command_buffer.estimated_process_time)};
690 691
691 command_generator.GenerateVoiceCommands();
692 command_generator.GenerateSubMixCommands(); 692 command_generator.GenerateSubMixCommands();
693 command_generator.GenerateFinalMixCommands(); 693 command_generator.GenerateFinalMixCommands();
694 command_generator.GenerateSinkCommands(); 694 command_generator.GenerateSinkCommands();
@@ -708,11 +708,13 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
708 708
709 const auto end_estimated_time{drop_voice_param * 709 const auto end_estimated_time{drop_voice_param *
710 static_cast<f32>(command_buffer.estimated_process_time)}; 710 static_cast<f32>(command_buffer.estimated_process_time)};
711
712 const auto dsp_time_limit{((time_limit_percent / 100.0f) * 2'880'000.0f) *
713 (static_cast<f32>(render_time_limit_percent) / 100.0f)};
714
711 const auto estimated_time{start_estimated_time - end_estimated_time}; 715 const auto estimated_time{start_estimated_time - end_estimated_time};
712 716
713 const auto time_limit{static_cast<u32>( 717 const auto time_limit{static_cast<u32>(std::max(dsp_time_limit + estimated_time, 0.0f))};
714 estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) *
715 (static_cast<f32>(render_time_limit_percent) / 100.0f)))};
716 num_voices_dropped = 718 num_voices_dropped =
717 DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit); 719 DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit);
718 } 720 }
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 34877b461..416203c59 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -26,12 +26,11 @@ add_library(common STATIC
26 assert.h 26 assert.h
27 atomic_helpers.h 27 atomic_helpers.h
28 atomic_ops.h 28 atomic_ops.h
29 detached_tasks.cpp
30 detached_tasks.h
31 bit_cast.h 29 bit_cast.h
32 bit_field.h 30 bit_field.h
33 bit_set.h 31 bit_set.h
34 bit_util.h 32 bit_util.h
33 bounded_threadsafe_queue.h
35 cityhash.cpp 34 cityhash.cpp
36 cityhash.h 35 cityhash.h
37 common_funcs.h 36 common_funcs.h
@@ -41,6 +40,8 @@ add_library(common STATIC
41 container_hash.h 40 container_hash.h
42 demangle.cpp 41 demangle.cpp
43 demangle.h 42 demangle.h
43 detached_tasks.cpp
44 detached_tasks.h
44 div_ceil.h 45 div_ceil.h
45 dynamic_library.cpp 46 dynamic_library.cpp
46 dynamic_library.h 47 dynamic_library.h
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
index bd87aa09b..b36fc1de9 100644
--- a/src/common/bounded_threadsafe_queue.h
+++ b/src/common/bounded_threadsafe_queue.h
@@ -45,13 +45,13 @@ public:
45 } 45 }
46 46
47 T PopWait() { 47 T PopWait() {
48 T t; 48 T t{};
49 Pop<PopMode::Wait>(t); 49 Pop<PopMode::Wait>(t);
50 return t; 50 return t;
51 } 51 }
52 52
53 T PopWait(std::stop_token stop_token) { 53 T PopWait(std::stop_token stop_token) {
54 T t; 54 T t{};
55 Pop<PopMode::WaitWithStopToken>(t, stop_token); 55 Pop<PopMode::WaitWithStopToken>(t, stop_token);
56 return t; 56 return t;
57 } 57 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 30d2f7df6..d0f76e57e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -596,6 +596,10 @@ add_library(core STATIC
596 hle/service/mii/types/ver3_store_data.h 596 hle/service/mii/types/ver3_store_data.h
597 hle/service/mii/mii.cpp 597 hle/service/mii/mii.cpp
598 hle/service/mii/mii.h 598 hle/service/mii/mii.h
599 hle/service/mii/mii_database.cpp
600 hle/service/mii/mii_database.h
601 hle/service/mii/mii_database_manager.cpp
602 hle/service/mii/mii_database_manager.h
599 hle/service/mii/mii_manager.cpp 603 hle/service/mii/mii_manager.cpp
600 hle/service/mii/mii_manager.h 604 hle/service/mii/mii_manager.h
601 hle/service/mii/mii_result.h 605 hle/service/mii/mii_result.h
@@ -890,7 +894,7 @@ endif()
890create_target_directory_groups(core) 894create_target_directory_groups(core)
891 895
892target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) 896target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
893target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) 897target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
894if (MINGW) 898if (MINGW)
895 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 899 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
896endif() 900endif()
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e33b00d89..04da93d5c 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -752,7 +752,9 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
752 for (u8 i = 0; i < 0x10; i++) { 752 for (u8 i = 0; i < 0x10; i++) {
753 const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta"); 753 const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta");
754 const auto filename = GetCNMTName(TitleType::Update, title_id + i); 754 const auto filename = GetCNMTName(TitleType::Update, title_id + i);
755 removed_data |= meta_dir->DeleteFile(filename); 755 if (meta_dir->GetFile(filename)) {
756 removed_data |= meta_dir->DeleteFile(filename);
757 }
756 } 758 }
757 759
758 return removed_data; 760 return removed_data;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 92a1439eb..dd0b27f47 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -62,7 +62,7 @@ enum class ErrorModule : u32 {
62 XCD = 108, 62 XCD = 108,
63 TMP451 = 109, 63 TMP451 = 109,
64 NIFM = 110, 64 NIFM = 110,
65 Hwopus = 111, 65 HwOpus = 111,
66 LSM6DS3 = 112, 66 LSM6DS3 = 112,
67 Bluetooth = 113, 67 Bluetooth = 113,
68 VI = 114, 68 VI = 114,
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 38c2138e8..7075ab800 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -22,6 +22,8 @@
22 22
23namespace Service::AOC { 23namespace Service::AOC {
24 24
25constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
26
25static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { 27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
26 return FileSys::GetBaseTitleID(title_id) == base; 28 return FileSys::GetBaseTitleID(title_id) == base;
27} 29}
@@ -54,8 +56,8 @@ public:
54 {0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"}, 56 {0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"},
55 {1, &IPurchaseEventManager::SetDeliveryTarget, "SetDeliveryTarget"}, 57 {1, &IPurchaseEventManager::SetDeliveryTarget, "SetDeliveryTarget"},
56 {2, &IPurchaseEventManager::GetPurchasedEventReadableHandle, "GetPurchasedEventReadableHandle"}, 58 {2, &IPurchaseEventManager::GetPurchasedEventReadableHandle, "GetPurchasedEventReadableHandle"},
57 {3, nullptr, "PopPurchasedProductInfo"}, 59 {3, &IPurchaseEventManager::PopPurchasedProductInfo, "PopPurchasedProductInfo"},
58 {4, nullptr, "PopPurchasedProductInfoWithUid"}, 60 {4, &IPurchaseEventManager::PopPurchasedProductInfoWithUid, "PopPurchasedProductInfoWithUid"},
59 }; 61 };
60 // clang-format on 62 // clang-format on
61 63
@@ -101,6 +103,20 @@ private:
101 rb.PushCopyObjects(purchased_event->GetReadableEvent()); 103 rb.PushCopyObjects(purchased_event->GetReadableEvent());
102 } 104 }
103 105
106 void PopPurchasedProductInfo(HLERequestContext& ctx) {
107 LOG_DEBUG(Service_AOC, "(STUBBED) called");
108
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(ResultNoPurchasedProductInfoAvailable);
111 }
112
113 void PopPurchasedProductInfoWithUid(HLERequestContext& ctx) {
114 LOG_DEBUG(Service_AOC, "(STUBBED) called");
115
116 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(ResultNoPurchasedProductInfoAvailable);
118 }
119
104 KernelHelpers::ServiceContext service_context; 120 KernelHelpers::ServiceContext service_context;
105 121
106 Kernel::KEvent* purchased_event; 122 Kernel::KEvent* purchased_event;
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index 3d3d3d97a..c41345f7e 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -20,4 +20,16 @@ constexpr Result ResultNotSupported{ErrorModule::Audio, 513};
20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; 20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; 21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
22 22
23constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
24constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
25constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6};
26constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5};
27constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17};
28constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4};
29constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3};
30constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2};
31constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259};
32constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001};
33constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002};
34
23} // namespace Service::Audio 35} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 1557e6088..6a7bf9416 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -1,420 +1,506 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <chrono>
5#include <cstring>
6#include <memory> 4#include <memory>
7#include <vector> 5#include <vector>
8 6
9#include <opus.h> 7#include "audio_core/opus/decoder.h"
10#include <opus_multistream.h> 8#include "audio_core/opus/parameters.h"
11
12#include "common/assert.h" 9#include "common/assert.h"
13#include "common/logging/log.h" 10#include "common/logging/log.h"
14#include "common/scratch_buffer.h" 11#include "common/scratch_buffer.h"
12#include "core/core.h"
15#include "core/hle/service/audio/hwopus.h" 13#include "core/hle/service/audio/hwopus.h"
16#include "core/hle/service/ipc_helpers.h" 14#include "core/hle/service/ipc_helpers.h"
17 15
18namespace Service::Audio { 16namespace Service::Audio {
19namespace { 17using namespace AudioCore::OpusDecoder;
20struct OpusDeleter { 18
21 void operator()(OpusMSDecoder* ptr) const { 19class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> {
22 opus_multistream_decoder_destroy(ptr); 20public:
21 explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus)
22 : ServiceFramework{system_, "IHardwareOpusDecoder"},
23 impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} {
24 // clang-format off
25 static const FunctionInfo functions[] = {
26 {0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"},
27 {1, &IHardwareOpusDecoder::SetContext, "SetContext"},
28 {2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"},
29 {3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"},
30 {4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
31 {5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"},
32 {6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"},
33 {7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
34 {8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"},
35 {9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
36 };
37 // clang-format on
38
39 RegisterHandlers(functions);
23 } 40 }
24};
25 41
26using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>; 42 Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory,
43 u64 transfer_memory_size) {
44 return impl->Initialize(params, transfer_memory, transfer_memory_size);
45 }
27 46
28struct OpusPacketHeader { 47 Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory,
29 // Packet size in bytes. 48 u64 transfer_memory_size) {
30 u32_be size; 49 return impl->Initialize(params, transfer_memory, transfer_memory_size);
31 // Indicates the final range of the codec's entropy coder. 50 }
32 u32_be final_range;
33};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 51
36class OpusDecoderState { 52private:
37public: 53 void DecodeInterleavedOld(HLERequestContext& ctx) {
38 /// Describes extra behavior that may be asked of the decoding context. 54 IPC::RequestParser rp{ctx};
39 enum class ExtraBehavior {
40 /// No extra behavior.
41 None,
42 55
43 /// Resets the decoder context back to a freshly initialized state. 56 auto input_data{ctx.ReadBuffer(0)};
44 ResetContext, 57 output_data.resize_destructive(ctx.GetWriteBufferSize());
45 };
46 58
47 enum class PerfTime { 59 u32 size{};
48 Disabled, 60 u32 sample_count{};
49 Enabled, 61 auto result =
50 }; 62 impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false);
51 63
52 explicit OpusDecoderState(OpusDecoderPtr decoder_, u32 sample_rate_, u32 channel_count_) 64 LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count);
53 : decoder{std::move(decoder_)}, sample_rate{sample_rate_}, channel_count{channel_count_} {} 65
54 66 ctx.WriteBuffer(output_data);
55 // Decodes interleaved Opus packets. Optionally allows reporting time taken to 67
56 // perform the decoding, as well as any relevant extra behavior. 68 IPC::ResponseBuilder rb{ctx, 4};
57 void DecodeInterleaved(HLERequestContext& ctx, PerfTime perf_time, 69 rb.Push(result);
58 ExtraBehavior extra_behavior) { 70 rb.Push(size);
59 if (perf_time == PerfTime::Disabled) { 71 rb.Push(sample_count);
60 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
61 } else {
62 u64 performance = 0;
63 DecodeInterleavedHelper(ctx, &performance, extra_behavior);
64 }
65 } 72 }
66 73
67private: 74 void SetContext(HLERequestContext& ctx) {
68 void DecodeInterleavedHelper(HLERequestContext& ctx, u64* performance, 75 IPC::RequestParser rp{ctx};
69 ExtraBehavior extra_behavior) { 76
70 u32 consumed = 0; 77 LOG_DEBUG(Service_Audio, "called");
71 u32 sample_count = 0; 78
72 samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); 79 auto input_data{ctx.ReadBuffer(0)};
73 80 auto result = impl->SetContext(input_data);
74 if (extra_behavior == ExtraBehavior::ResetContext) { 81
75 ResetDecoderContext(); 82 IPC::ResponseBuilder rb{ctx, 2};
76 } 83 rb.Push(result);
77 84 }
78 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { 85
79 LOG_ERROR(Audio, "Failed to decode opus data"); 86 void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) {
80 IPC::ResponseBuilder rb{ctx, 2}; 87 IPC::RequestParser rp{ctx};
81 // TODO(ogniK): Use correct error code 88
82 rb.Push(ResultUnknown); 89 auto input_data{ctx.ReadBuffer(0)};
83 return; 90 output_data.resize_destructive(ctx.GetWriteBufferSize());
84 } 91
85 92 u32 size{};
86 const u32 param_size = performance != nullptr ? 6 : 4; 93 u32 sample_count{};
87 IPC::ResponseBuilder rb{ctx, param_size}; 94 auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count,
88 rb.Push(ResultSuccess); 95 input_data, output_data, false);
89 rb.Push<u32>(consumed); 96
90 rb.Push<u32>(sample_count); 97 LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count);
91 if (performance) { 98
92 rb.Push<u64>(*performance); 99 ctx.WriteBuffer(output_data);
93 } 100
94 ctx.WriteBuffer(samples); 101 IPC::ResponseBuilder rb{ctx, 4};
102 rb.Push(result);
103 rb.Push(size);
104 rb.Push(sample_count);
95 } 105 }
96 106
97 bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, 107 void SetContextForMultiStream(HLERequestContext& ctx) {
98 std::span<opus_int16> output, u64* out_performance_time) const { 108 IPC::RequestParser rp{ctx};
99 const auto start_time = std::chrono::steady_clock::now(); 109
100 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 110 LOG_DEBUG(Service_Audio, "called");
101 if (sizeof(OpusPacketHeader) > input.size()) { 111
102 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", 112 auto input_data{ctx.ReadBuffer(0)};
103 sizeof(OpusPacketHeader), input.size()); 113 auto result = impl->SetContext(input_data);
104 return false; 114
105 } 115 IPC::ResponseBuilder rb{ctx, 2};
106 116 rb.Push(result);
107 OpusPacketHeader hdr{};
108 std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
109 if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
110 LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
111 sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
112 return false;
113 }
114
115 const auto frame = input.data() + sizeof(OpusPacketHeader);
116 const auto decoded_sample_count = opus_packet_get_nb_samples(
117 frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
118 static_cast<opus_int32>(sample_rate));
119 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
120 LOG_ERROR(
121 Audio,
122 "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}",
123 decoded_sample_count * channel_count * sizeof(u16), raw_output_sz);
124 return false;
125 }
126
127 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
128 const auto out_sample_count =
129 opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
130 if (out_sample_count < 0) {
131 LOG_ERROR(Audio,
132 "Incorrect sample count received from opus_decode, "
133 "output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
134 out_sample_count, frame_size, static_cast<u32>(hdr.size));
135 return false;
136 }
137
138 const auto end_time = std::chrono::steady_clock::now() - start_time;
139 sample_count = out_sample_count;
140 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
141 if (out_performance_time != nullptr) {
142 *out_performance_time =
143 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
144 }
145
146 return true;
147 } 117 }
148 118
149 void ResetDecoderContext() { 119 void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) {
150 ASSERT(decoder != nullptr); 120 IPC::RequestParser rp{ctx};
121
122 auto input_data{ctx.ReadBuffer(0)};
123 output_data.resize_destructive(ctx.GetWriteBufferSize());
124
125 u32 size{};
126 u32 sample_count{};
127 u64 time_taken{};
128 auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
129 output_data, false);
130
131 LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size,
132 sample_count, time_taken);
151 133
152 opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 134 ctx.WriteBuffer(output_data);
135
136 IPC::ResponseBuilder rb{ctx, 6};
137 rb.Push(result);
138 rb.Push(size);
139 rb.Push(sample_count);
140 rb.Push(time_taken);
153 } 141 }
154 142
155 OpusDecoderPtr decoder; 143 void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) {
156 u32 sample_rate; 144 IPC::RequestParser rp{ctx};
157 u32 channel_count;
158 Common::ScratchBuffer<opus_int16> samples;
159};
160 145
161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 146 auto input_data{ctx.ReadBuffer(0)};
162public: 147 output_data.resize_destructive(ctx.GetWriteBufferSize());
163 explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state_)
164 : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{
165 std::move(decoder_state_)} {
166 // clang-format off
167 static const FunctionInfo functions[] = {
168 {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
169 {1, nullptr, "SetContext"},
170 {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
171 {3, nullptr, "SetContextForMultiStream"},
172 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
173 {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
174 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
175 {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
176 {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
177 {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
178 };
179 // clang-format on
180 148
181 RegisterHandlers(functions); 149 u32 size{};
150 u32 sample_count{};
151 u64 time_taken{};
152 auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
153 input_data, output_data, false);
154
155 LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size,
156 sample_count, time_taken);
157
158 ctx.WriteBuffer(output_data);
159
160 IPC::ResponseBuilder rb{ctx, 6};
161 rb.Push(result);
162 rb.Push(size);
163 rb.Push(sample_count);
164 rb.Push(time_taken);
182 } 165 }
183 166
184private: 167 void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) {
185 void DecodeInterleavedOld(HLERequestContext& ctx) { 168 IPC::RequestParser rp{ctx};
186 LOG_DEBUG(Audio, "called"); 169
170 auto reset{rp.Pop<bool>()};
171
172 auto input_data{ctx.ReadBuffer(0)};
173 output_data.resize_destructive(ctx.GetWriteBufferSize());
174
175 u32 size{};
176 u32 sample_count{};
177 u64 time_taken{};
178 auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
179 output_data, reset);
180
181 LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
182 reset, size, sample_count, time_taken);
183
184 ctx.WriteBuffer(output_data);
187 185
188 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, 186 IPC::ResponseBuilder rb{ctx, 6};
189 OpusDecoderState::ExtraBehavior::None); 187 rb.Push(result);
188 rb.Push(size);
189 rb.Push(sample_count);
190 rb.Push(time_taken);
190 } 191 }
191 192
192 void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { 193 void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) {
193 LOG_DEBUG(Audio, "called"); 194 IPC::RequestParser rp{ctx};
195
196 auto reset{rp.Pop<bool>()};
197
198 auto input_data{ctx.ReadBuffer(0)};
199 output_data.resize_destructive(ctx.GetWriteBufferSize());
200
201 u32 size{};
202 u32 sample_count{};
203 u64 time_taken{};
204 auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
205 input_data, output_data, reset);
194 206
195 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, 207 LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
196 OpusDecoderState::ExtraBehavior::None); 208 reset, size, sample_count, time_taken);
209
210 ctx.WriteBuffer(output_data);
211
212 IPC::ResponseBuilder rb{ctx, 6};
213 rb.Push(result);
214 rb.Push(size);
215 rb.Push(sample_count);
216 rb.Push(time_taken);
197 } 217 }
198 218
199 void DecodeInterleaved(HLERequestContext& ctx) { 219 void DecodeInterleaved(HLERequestContext& ctx) {
200 LOG_DEBUG(Audio, "called");
201
202 IPC::RequestParser rp{ctx}; 220 IPC::RequestParser rp{ctx};
203 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
204 : OpusDecoderState::ExtraBehavior::None;
205 221
206 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); 222 auto reset{rp.Pop<bool>()};
223
224 auto input_data{ctx.ReadBuffer(0)};
225 output_data.resize_destructive(ctx.GetWriteBufferSize());
226
227 u32 size{};
228 u32 sample_count{};
229 u64 time_taken{};
230 auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data,
231 output_data, reset);
232
233 LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
234 reset, size, sample_count, time_taken);
235
236 ctx.WriteBuffer(output_data);
237
238 IPC::ResponseBuilder rb{ctx, 6};
239 rb.Push(result);
240 rb.Push(size);
241 rb.Push(sample_count);
242 rb.Push(time_taken);
207 } 243 }
208 244
209 void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { 245 void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
210 LOG_DEBUG(Audio, "called");
211
212 IPC::RequestParser rp{ctx}; 246 IPC::RequestParser rp{ctx};
213 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
214 : OpusDecoderState::ExtraBehavior::None;
215 247
216 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); 248 auto reset{rp.Pop<bool>()};
249
250 auto input_data{ctx.ReadBuffer(0)};
251 output_data.resize_destructive(ctx.GetWriteBufferSize());
252
253 u32 size{};
254 u32 sample_count{};
255 u64 time_taken{};
256 auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count,
257 input_data, output_data, reset);
258
259 LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}",
260 reset, size, sample_count, time_taken);
261
262 ctx.WriteBuffer(output_data);
263
264 IPC::ResponseBuilder rb{ctx, 6};
265 rb.Push(result);
266 rb.Push(size);
267 rb.Push(sample_count);
268 rb.Push(time_taken);
217 } 269 }
218 270
219 OpusDecoderState decoder_state; 271 std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl;
272 Common::ScratchBuffer<u8> output_data;
220}; 273};
221 274
222std::size_t WorkerBufferSize(u32 channel_count) { 275void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
223 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 276 IPC::RequestParser rp{ctx};
224 constexpr int num_streams = 1;
225 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
226 return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
227}
228 277
229// Creates the mapping table that maps the input channels to the particular 278 auto params = rp.PopRaw<OpusParameters>();
230// output channels. In the stereo case, we map the left and right input channels 279 auto transfer_memory_size{rp.Pop<u32>()};
231// to the left and right output channels respectively. 280 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
232// 281 auto transfer_memory{
233// However, in the monophonic case, we only map the one available channel 282 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
234// to the sole output channel. We specify 255 for the would-be right channel 283 transfer_memory_handle)};
235// as this is a special value defined by Opus to indicate to the decoder to 284
236// ignore that channel. 285 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
237std::array<u8, 2> CreateMappingTable(u32 channel_count) { 286 params.sample_rate, params.channel_count, transfer_memory_size);
238 if (channel_count == 2) {
239 return {{0, 1}};
240 }
241 287
242 return {{0, 255}}; 288 auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
289
290 OpusParametersEx ex{
291 .sample_rate = params.sample_rate,
292 .channel_count = params.channel_count,
293 .use_large_frame_size = false,
294 };
295 auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
296
297 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
298 rb.Push(result);
299 rb.PushIpcInterface(decoder);
243} 300}
244} // Anonymous namespace
245 301
246void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { 302void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) {
247 IPC::RequestParser rp{ctx}; 303 IPC::RequestParser rp{ctx};
248 const auto sample_rate = rp.Pop<u32>(); 304 auto params = rp.PopRaw<OpusParameters>();
249 const auto channel_count = rp.Pop<u32>();
250 305
251 LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); 306 u64 size{};
307 auto result = impl.GetWorkBufferSize(params, size);
252 308
253 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || 309 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}",
254 sample_rate == 12000 || sample_rate == 8000, 310 params.sample_rate, params.channel_count, size);
255 "Invalid sample rate");
256 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
257 311
258 const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); 312 IPC::ResponseBuilder rb{ctx, 4};
259 LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); 313 rb.Push(result);
260 314 rb.Push(size);
261 IPC::ResponseBuilder rb{ctx, 3};
262 rb.Push(ResultSuccess);
263 rb.Push<u32>(worker_buffer_sz);
264} 315}
265 316
266void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { 317void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
267 GetWorkBufferSize(ctx); 318 IPC::RequestParser rp{ctx};
319
320 auto input{ctx.ReadBuffer()};
321 OpusMultiStreamParameters params;
322 std::memcpy(&params, input.data(), sizeof(OpusMultiStreamParameters));
323
324 auto transfer_memory_size{rp.Pop<u32>()};
325 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
326 auto transfer_memory{
327 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
328 transfer_memory_handle)};
329
330 LOG_DEBUG(Service_Audio,
331 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
332 "transfer_memory_size 0x{:X}",
333 params.sample_rate, params.channel_count, params.total_stream_count,
334 params.stereo_stream_count, transfer_memory_size);
335
336 auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
337
338 OpusMultiStreamParametersEx ex{
339 .sample_rate = params.sample_rate,
340 .channel_count = params.channel_count,
341 .total_stream_count = params.total_stream_count,
342 .stereo_stream_count = params.stereo_stream_count,
343 .use_large_frame_size = false,
344 .mappings{},
345 };
346 std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings));
347 auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
348
349 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
350 rb.Push(result);
351 rb.PushIpcInterface(decoder);
268} 352}
269 353
270void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { 354void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) {
271 GetWorkBufferSizeEx(ctx); 355 IPC::RequestParser rp{ctx};
356
357 auto input{ctx.ReadBuffer()};
358 OpusMultiStreamParameters params;
359 std::memcpy(&params, input.data(), sizeof(OpusMultiStreamParameters));
360
361 u64 size{};
362 auto result = impl.GetWorkBufferSizeForMultiStream(params, size);
363
364 LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
365
366 IPC::ResponseBuilder rb{ctx, 4};
367 rb.Push(result);
368 rb.Push(size);
272} 369}
273 370
274void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { 371void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
275 OpusMultiStreamParametersEx param; 372 IPC::RequestParser rp{ctx};
276 std::memcpy(&param, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
277 373
278 const auto sample_rate = param.sample_rate; 374 auto params = rp.PopRaw<OpusParametersEx>();
279 const auto channel_count = param.channel_count; 375 auto transfer_memory_size{rp.Pop<u32>()};
280 const auto number_streams = param.number_streams; 376 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
281 const auto number_stereo_streams = param.number_stereo_streams; 377 auto transfer_memory{
378 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
379 transfer_memory_handle)};
282 380
283 LOG_DEBUG( 381 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
284 Audio, 382 params.sample_rate, params.channel_count, transfer_memory_size);
285 "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
286 sample_rate, channel_count, number_streams, number_stereo_streams);
287 383
288 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || 384 auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
289 sample_rate == 12000 || sample_rate == 8000,
290 "Invalid sample rate");
291 385
292 const u32 worker_buffer_sz = 386 auto result =
293 static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); 387 decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
294 388
295 IPC::ResponseBuilder rb{ctx, 3}; 389 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
296 rb.Push(ResultSuccess); 390 rb.Push(result);
297 rb.Push<u32>(worker_buffer_sz); 391 rb.PushIpcInterface(decoder);
298} 392}
299 393
300void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { 394void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) {
301 IPC::RequestParser rp{ctx}; 395 IPC::RequestParser rp{ctx};
302 const auto sample_rate = rp.Pop<u32>(); 396 auto params = rp.PopRaw<OpusParametersEx>();
303 const auto channel_count = rp.Pop<u32>();
304 const auto buffer_sz = rp.Pop<u32>();
305
306 LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
307 channel_count, buffer_sz);
308
309 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
310 sample_rate == 12000 || sample_rate == 8000,
311 "Invalid sample rate");
312 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
313
314 const std::size_t worker_sz = WorkerBufferSize(channel_count);
315 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
316
317 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
318 const auto mapping_table = CreateMappingTable(channel_count);
319
320 int error = 0;
321 OpusDecoderPtr decoder{
322 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
323 num_stereo_streams, mapping_table.data(), &error)};
324 if (error != OPUS_OK || decoder == nullptr) {
325 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
326 IPC::ResponseBuilder rb{ctx, 2};
327 // TODO(ogniK): Use correct error code
328 rb.Push(ResultUnknown);
329 return;
330 }
331 397
332 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 398 u64 size{};
333 rb.Push(ResultSuccess); 399 auto result = impl.GetWorkBufferSizeEx(params, size);
334 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 400
335 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); 401 LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
402
403 IPC::ResponseBuilder rb{ctx, 4};
404 rb.Push(result);
405 rb.Push(size);
336} 406}
337 407
338void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { 408void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx}; 409 IPC::RequestParser rp{ctx};
340 const auto sample_rate = rp.Pop<u32>();
341 const auto channel_count = rp.Pop<u32>();
342 410
343 LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); 411 auto input{ctx.ReadBuffer()};
412 OpusMultiStreamParametersEx params;
413 std::memcpy(&params, input.data(), sizeof(OpusMultiStreamParametersEx));
414
415 auto transfer_memory_size{rp.Pop<u32>()};
416 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
417 auto transfer_memory{
418 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
419 transfer_memory_handle)};
344 420
345 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || 421 LOG_DEBUG(Service_Audio,
346 sample_rate == 12000 || sample_rate == 8000, 422 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
347 "Invalid sample rate"); 423 "use_large_frame_size {}"
348 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 424 "transfer_memory_size 0x{:X}",
425 params.sample_rate, params.channel_count, params.total_stream_count,
426 params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size);
349 427
350 const int num_stereo_streams = channel_count == 2 ? 1 : 0; 428 auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())};
351 const auto mapping_table = CreateMappingTable(channel_count);
352 429
353 int error = 0; 430 auto result =
354 OpusDecoderPtr decoder{ 431 decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size);
355 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
356 num_stereo_streams, mapping_table.data(), &error)};
357 if (error != OPUS_OK || decoder == nullptr) {
358 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
359 IPC::ResponseBuilder rb{ctx, 2};
360 // TODO(ogniK): Use correct error code
361 rb.Push(ResultUnknown);
362 return;
363 }
364 432
365 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 433 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
366 rb.Push(ResultSuccess); 434 rb.Push(result);
367 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 435 rb.PushIpcInterface(decoder);
368 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
369} 436}
370 437
371void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { 438void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440
441 auto input{ctx.ReadBuffer()};
372 OpusMultiStreamParametersEx params; 442 OpusMultiStreamParametersEx params;
373 std::memcpy(&params, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 443 std::memcpy(&params, input.data(), sizeof(OpusMultiStreamParametersEx));
374
375 const auto& sample_rate = params.sample_rate;
376 const auto& channel_count = params.channel_count;
377
378 LOG_INFO(
379 Audio,
380 "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
381 sample_rate, channel_count, params.number_streams, params.number_stereo_streams);
382
383 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
384 sample_rate == 12000 || sample_rate == 8000,
385 "Invalid sample rate");
386
387 int error = 0;
388 OpusDecoderPtr decoder{opus_multistream_decoder_create(
389 sample_rate, static_cast<int>(channel_count), params.number_streams,
390 params.number_stereo_streams, params.channel_mappings.data(), &error)};
391 if (error != OPUS_OK || decoder == nullptr) {
392 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
393 IPC::ResponseBuilder rb{ctx, 2};
394 // TODO(ogniK): Use correct error code
395 rb.Push(ResultUnknown);
396 return;
397 }
398 444
399 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 445 u64 size{};
400 rb.Push(ResultSuccess); 446 auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size);
401 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 447
402 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); 448 LOG_DEBUG(Service_Audio,
449 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
450 "use_large_frame_size {} -- returned size 0x{:X}",
451 params.sample_rate, params.channel_count, params.total_stream_count,
452 params.stereo_stream_count, params.use_large_frame_size, size);
453
454 IPC::ResponseBuilder rb{ctx, 4};
455 rb.Push(result);
456 rb.Push(size);
457}
458
459void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) {
460 IPC::RequestParser rp{ctx};
461 auto params = rp.PopRaw<OpusParametersEx>();
462
463 u64 size{};
464 auto result = impl.GetWorkBufferSizeExEx(params, size);
465
466 LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
467
468 IPC::ResponseBuilder rb{ctx, 4};
469 rb.Push(result);
470 rb.Push(size);
471}
472
473void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475
476 auto input{ctx.ReadBuffer()};
477 OpusMultiStreamParametersEx params;
478 std::memcpy(&params, input.data(), sizeof(OpusMultiStreamParametersEx));
479
480 u64 size{};
481 auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size);
482
483 LOG_DEBUG(Service_Audio, "size 0x{:X}", size);
484
485 IPC::ResponseBuilder rb{ctx, 4};
486 rb.Push(result);
487 rb.Push(size);
403} 488}
404 489
405HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 490HwOpus::HwOpus(Core::System& system_)
491 : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} {
406 static const FunctionInfo functions[] = { 492 static const FunctionInfo functions[] = {
407 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, 493 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
408 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, 494 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
409 {2, nullptr, "OpenOpusDecoderForMultiStream"}, 495 {2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"},
410 {3, nullptr, "GetWorkBufferSizeForMultiStream"}, 496 {3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"},
411 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, 497 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
412 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, 498 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
413 {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, 499 {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
414 "OpenHardwareOpusDecoderForMultiStreamEx"}, 500 "OpenHardwareOpusDecoderForMultiStreamEx"},
415 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, 501 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
416 {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, 502 {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"},
417 {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, 503 {9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"},
418 }; 504 };
419 RegisterHandlers(functions); 505 RegisterHandlers(functions);
420} 506}
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index 90867bf74..d3960065e 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "audio_core/opus/decoder_manager.h"
6#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
7 8
8namespace Core { 9namespace Core {
@@ -11,18 +12,6 @@ class System;
11 12
12namespace Service::Audio { 13namespace Service::Audio {
13 14
14struct OpusMultiStreamParametersEx {
15 u32 sample_rate;
16 u32 channel_count;
17 u32 number_streams;
18 u32 number_stereo_streams;
19 u32 use_large_frame_size;
20 u32 padding;
21 std::array<u8, 0x100> channel_mappings;
22};
23static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
24 "OpusMultiStreamParametersEx has incorrect size");
25
26class HwOpus final : public ServiceFramework<HwOpus> { 15class HwOpus final : public ServiceFramework<HwOpus> {
27public: 16public:
28 explicit HwOpus(Core::System& system_); 17 explicit HwOpus(Core::System& system_);
@@ -30,12 +19,18 @@ public:
30 19
31private: 20private:
32 void OpenHardwareOpusDecoder(HLERequestContext& ctx); 21 void OpenHardwareOpusDecoder(HLERequestContext& ctx);
33 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
34 void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
35 void GetWorkBufferSize(HLERequestContext& ctx); 22 void GetWorkBufferSize(HLERequestContext& ctx);
23 void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx);
24 void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx);
25 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
36 void GetWorkBufferSizeEx(HLERequestContext& ctx); 26 void GetWorkBufferSizeEx(HLERequestContext& ctx);
37 void GetWorkBufferSizeExEx(HLERequestContext& ctx); 27 void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
38 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); 28 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
29 void GetWorkBufferSizeExEx(HLERequestContext& ctx);
30 void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx);
31
32 Core::System& system;
33 AudioCore::OpusDecoder::OpusDecoderManager impl;
39}; 34};
40 35
41} // namespace Service::Audio 36} // namespace Service::Audio
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 3b83c5ed7..8de806cfb 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -8,6 +8,9 @@
8#include "core/hle/service/mii/mii.h" 8#include "core/hle/service/mii/mii.h"
9#include "core/hle/service/mii/mii_manager.h" 9#include "core/hle/service/mii/mii_manager.h"
10#include "core/hle/service/mii/mii_result.h" 10#include "core/hle/service/mii/mii_result.h"
11#include "core/hle/service/mii/types/char_info.h"
12#include "core/hle/service/mii/types/store_data.h"
13#include "core/hle/service/mii/types/ver3_store_data.h"
11#include "core/hle/service/server_manager.h" 14#include "core/hle/service/server_manager.h"
12#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
13 16
@@ -27,29 +30,31 @@ public:
27 {5, &IDatabaseService::UpdateLatest, "UpdateLatest"}, 30 {5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
28 {6, &IDatabaseService::BuildRandom, "BuildRandom"}, 31 {6, &IDatabaseService::BuildRandom, "BuildRandom"},
29 {7, &IDatabaseService::BuildDefault, "BuildDefault"}, 32 {7, &IDatabaseService::BuildDefault, "BuildDefault"},
30 {8, nullptr, "Get2"}, 33 {8, &IDatabaseService::Get2, "Get2"},
31 {9, nullptr, "Get3"}, 34 {9, &IDatabaseService::Get3, "Get3"},
32 {10, nullptr, "UpdateLatest1"}, 35 {10, &IDatabaseService::UpdateLatest1, "UpdateLatest1"},
33 {11, nullptr, "FindIndex"}, 36 {11, &IDatabaseService::FindIndex, "FindIndex"},
34 {12, nullptr, "Move"}, 37 {12, &IDatabaseService::Move, "Move"},
35 {13, nullptr, "AddOrReplace"}, 38 {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
36 {14, nullptr, "Delete"}, 39 {14, &IDatabaseService::Delete, "Delete"},
37 {15, nullptr, "DestroyFile"}, 40 {15, &IDatabaseService::DestroyFile, "DestroyFile"},
38 {16, nullptr, "DeleteFile"}, 41 {16, &IDatabaseService::DeleteFile, "DeleteFile"},
39 {17, nullptr, "Format"}, 42 {17, &IDatabaseService::Format, "Format"},
40 {18, nullptr, "Import"}, 43 {18, nullptr, "Import"},
41 {19, nullptr, "Export"}, 44 {19, nullptr, "Export"},
42 {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, 45 {20, &IDatabaseService::IsBrokenDatabaseWithClearFlag, "IsBrokenDatabaseWithClearFlag"},
43 {21, &IDatabaseService::GetIndex, "GetIndex"}, 46 {21, &IDatabaseService::GetIndex, "GetIndex"},
44 {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, 47 {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
45 {23, &IDatabaseService::Convert, "Convert"}, 48 {23, &IDatabaseService::Convert, "Convert"},
46 {24, nullptr, "ConvertCoreDataToCharInfo"}, 49 {24, &IDatabaseService::ConvertCoreDataToCharInfo, "ConvertCoreDataToCharInfo"},
47 {25, nullptr, "ConvertCharInfoToCoreData"}, 50 {25, &IDatabaseService::ConvertCharInfoToCoreData, "ConvertCharInfoToCoreData"},
48 {26, nullptr, "Append"}, 51 {26, &IDatabaseService::Append, "Append"},
49 }; 52 };
50 // clang-format on 53 // clang-format on
51 54
52 RegisterHandlers(functions); 55 RegisterHandlers(functions);
56
57 manager.Initialize(metadata);
53 } 58 }
54 59
55private: 60private:
@@ -80,10 +85,10 @@ private:
80 IPC::RequestParser rp{ctx}; 85 IPC::RequestParser rp{ctx};
81 const auto source_flag{rp.PopRaw<SourceFlag>()}; 86 const auto source_flag{rp.PopRaw<SourceFlag>()};
82 87
83 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
84
85 const u32 mii_count = manager.GetCount(metadata, source_flag); 88 const u32 mii_count = manager.GetCount(metadata, source_flag);
86 89
90 LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
91
87 IPC::ResponseBuilder rb{ctx, 3}; 92 IPC::ResponseBuilder rb{ctx, 3};
88 rb.Push(ResultSuccess); 93 rb.Push(ResultSuccess);
89 rb.Push(mii_count); 94 rb.Push(mii_count);
@@ -94,16 +99,17 @@ private:
94 const auto source_flag{rp.PopRaw<SourceFlag>()}; 99 const auto source_flag{rp.PopRaw<SourceFlag>()};
95 const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()}; 100 const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
96 101
97 LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
98
99 u32 mii_count{}; 102 u32 mii_count{};
100 std::vector<CharInfoElement> char_info_elements(output_size); 103 std::vector<CharInfoElement> char_info_elements(output_size);
101 Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag); 104 const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
102 105
103 if (mii_count != 0) { 106 if (mii_count != 0) {
104 ctx.WriteBuffer(char_info_elements); 107 ctx.WriteBuffer(char_info_elements);
105 } 108 }
106 109
110 LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
111 output_size, mii_count);
112
107 IPC::ResponseBuilder rb{ctx, 3}; 113 IPC::ResponseBuilder rb{ctx, 3};
108 rb.Push(result); 114 rb.Push(result);
109 rb.Push(mii_count); 115 rb.Push(mii_count);
@@ -114,16 +120,17 @@ private:
114 const auto source_flag{rp.PopRaw<SourceFlag>()}; 120 const auto source_flag{rp.PopRaw<SourceFlag>()};
115 const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()}; 121 const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
116 122
117 LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
118
119 u32 mii_count{}; 123 u32 mii_count{};
120 std::vector<CharInfo> char_info(output_size); 124 std::vector<CharInfo> char_info(output_size);
121 Result result = manager.Get(metadata, char_info, mii_count, source_flag); 125 const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
122 126
123 if (mii_count != 0) { 127 if (mii_count != 0) {
124 ctx.WriteBuffer(char_info); 128 ctx.WriteBuffer(char_info);
125 } 129 }
126 130
131 LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
132 output_size, mii_count);
133
127 IPC::ResponseBuilder rb{ctx, 3}; 134 IPC::ResponseBuilder rb{ctx, 3};
128 rb.Push(result); 135 rb.Push(result);
129 rb.Push(mii_count); 136 rb.Push(mii_count);
@@ -134,7 +141,7 @@ private:
134 const auto char_info{rp.PopRaw<CharInfo>()}; 141 const auto char_info{rp.PopRaw<CharInfo>()};
135 const auto source_flag{rp.PopRaw<SourceFlag>()}; 142 const auto source_flag{rp.PopRaw<SourceFlag>()};
136 143
137 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 144 LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
138 145
139 CharInfo new_char_info{}; 146 CharInfo new_char_info{};
140 const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag); 147 const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
@@ -146,7 +153,7 @@ private:
146 153
147 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 154 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
148 rb.Push(ResultSuccess); 155 rb.Push(ResultSuccess);
149 rb.PushRaw<CharInfo>(new_char_info); 156 rb.PushRaw(new_char_info);
150 } 157 }
151 158
152 void BuildRandom(HLERequestContext& ctx) { 159 void BuildRandom(HLERequestContext& ctx) {
@@ -180,14 +187,14 @@ private:
180 187
181 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 188 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
182 rb.Push(ResultSuccess); 189 rb.Push(ResultSuccess);
183 rb.PushRaw<CharInfo>(char_info); 190 rb.PushRaw(char_info);
184 } 191 }
185 192
186 void BuildDefault(HLERequestContext& ctx) { 193 void BuildDefault(HLERequestContext& ctx) {
187 IPC::RequestParser rp{ctx}; 194 IPC::RequestParser rp{ctx};
188 const auto index{rp.Pop<u32>()}; 195 const auto index{rp.Pop<u32>()};
189 196
190 LOG_INFO(Service_Mii, "called with index={}", index); 197 LOG_DEBUG(Service_Mii, "called with index={}", index);
191 198
192 if (index > 5) { 199 if (index > 5) {
193 IPC::ResponseBuilder rb{ctx, 2}; 200 IPC::ResponseBuilder rb{ctx, 2};
@@ -200,7 +207,239 @@ private:
200 207
201 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 208 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
202 rb.Push(ResultSuccess); 209 rb.Push(ResultSuccess);
203 rb.PushRaw<CharInfo>(char_info); 210 rb.PushRaw(char_info);
211 }
212
213 void Get2(HLERequestContext& ctx) {
214 IPC::RequestParser rp{ctx};
215 const auto source_flag{rp.PopRaw<SourceFlag>()};
216 const auto output_size{ctx.GetWriteBufferNumElements<StoreDataElement>()};
217
218 u32 mii_count{};
219 std::vector<StoreDataElement> store_data_elements(output_size);
220 const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
221
222 if (mii_count != 0) {
223 ctx.WriteBuffer(store_data_elements);
224 }
225
226 LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
227 output_size, mii_count);
228
229 IPC::ResponseBuilder rb{ctx, 3};
230 rb.Push(result);
231 rb.Push(mii_count);
232 }
233
234 void Get3(HLERequestContext& ctx) {
235 IPC::RequestParser rp{ctx};
236 const auto source_flag{rp.PopRaw<SourceFlag>()};
237 const auto output_size{ctx.GetWriteBufferNumElements<StoreData>()};
238
239 u32 mii_count{};
240 std::vector<StoreData> store_data(output_size);
241 const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
242
243 if (mii_count != 0) {
244 ctx.WriteBuffer(store_data);
245 }
246
247 LOG_INFO(Service_Mii, "called with source_flag={}, out_size={}, mii_count={}", source_flag,
248 output_size, mii_count);
249
250 IPC::ResponseBuilder rb{ctx, 3};
251 rb.Push(result);
252 rb.Push(mii_count);
253 }
254
255 void UpdateLatest1(HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto store_data{rp.PopRaw<StoreData>()};
258 const auto source_flag{rp.PopRaw<SourceFlag>()};
259
260 LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
261
262 Result result = ResultSuccess;
263 if (!is_system) {
264 result = ResultPermissionDenied;
265 }
266
267 StoreData new_store_data{};
268 if (result.IsSuccess()) {
269 result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
270 }
271
272 if (result.IsFailure()) {
273 IPC::ResponseBuilder rb{ctx, 2};
274 rb.Push(result);
275 return;
276 }
277
278 IPC::ResponseBuilder rb{ctx, 2 + sizeof(StoreData) / sizeof(u32)};
279 rb.Push(ResultSuccess);
280 rb.PushRaw<StoreData>(new_store_data);
281 }
282
283 void FindIndex(HLERequestContext& ctx) {
284 IPC::RequestParser rp{ctx};
285 const auto create_id{rp.PopRaw<Common::UUID>()};
286 const auto is_special{rp.PopRaw<bool>()};
287
288 LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
289 create_id.FormattedString(), is_special);
290
291 const s32 index = manager.FindIndex(create_id, is_special);
292
293 IPC::ResponseBuilder rb{ctx, 3};
294 rb.Push(ResultSuccess);
295 rb.Push(index);
296 }
297
298 void Move(HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto create_id{rp.PopRaw<Common::UUID>()};
301 const auto new_index{rp.PopRaw<s32>()};
302
303 LOG_INFO(Service_Mii, "called with create_id={}, new_index={}", create_id.FormattedString(),
304 new_index);
305
306 Result result = ResultSuccess;
307 if (!is_system) {
308 result = ResultPermissionDenied;
309 }
310
311 if (result.IsSuccess()) {
312 const u32 count = manager.GetCount(metadata, SourceFlag::Database);
313 if (new_index < 0 || new_index >= static_cast<s32>(count)) {
314 result = ResultInvalidArgument;
315 }
316 }
317
318 if (result.IsSuccess()) {
319 result = manager.Move(metadata, new_index, create_id);
320 }
321
322 IPC::ResponseBuilder rb{ctx, 2};
323 rb.Push(result);
324 }
325
326 void AddOrReplace(HLERequestContext& ctx) {
327 IPC::RequestParser rp{ctx};
328 const auto store_data{rp.PopRaw<StoreData>()};
329
330 LOG_INFO(Service_Mii, "called");
331
332 Result result = ResultSuccess;
333
334 if (!is_system) {
335 result = ResultPermissionDenied;
336 }
337
338 if (result.IsSuccess()) {
339 result = manager.AddOrReplace(metadata, store_data);
340 }
341
342 IPC::ResponseBuilder rb{ctx, 2};
343 rb.Push(result);
344 }
345
346 void Delete(HLERequestContext& ctx) {
347 IPC::RequestParser rp{ctx};
348 const auto create_id{rp.PopRaw<Common::UUID>()};
349
350 LOG_INFO(Service_Mii, "called, create_id={}", create_id.FormattedString());
351
352 Result result = ResultSuccess;
353
354 if (!is_system) {
355 result = ResultPermissionDenied;
356 }
357
358 if (result.IsSuccess()) {
359 result = manager.Delete(metadata, create_id);
360 }
361
362 IPC::ResponseBuilder rb{ctx, 2};
363 rb.Push(result);
364 }
365
366 void DestroyFile(HLERequestContext& ctx) {
367 // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
368 const bool is_db_test_mode_enabled = false;
369
370 LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
371
372 Result result = ResultSuccess;
373
374 if (!is_db_test_mode_enabled) {
375 result = ResultTestModeOnly;
376 }
377
378 if (result.IsSuccess()) {
379 result = manager.DestroyFile(metadata);
380 }
381
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(result);
384 }
385
386 void DeleteFile(HLERequestContext& ctx) {
387 // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
388 const bool is_db_test_mode_enabled = false;
389
390 LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
391
392 Result result = ResultSuccess;
393
394 if (!is_db_test_mode_enabled) {
395 result = ResultTestModeOnly;
396 }
397
398 if (result.IsSuccess()) {
399 result = manager.DeleteFile();
400 }
401
402 IPC::ResponseBuilder rb{ctx, 2};
403 rb.Push(result);
404 }
405
406 void Format(HLERequestContext& ctx) {
407 // This calls nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
408 const bool is_db_test_mode_enabled = false;
409
410 LOG_INFO(Service_Mii, "called is_db_test_mode_enabled={}", is_db_test_mode_enabled);
411
412 Result result = ResultSuccess;
413
414 if (!is_db_test_mode_enabled) {
415 result = ResultTestModeOnly;
416 }
417
418 if (result.IsSuccess()) {
419 result = manager.Format(metadata);
420 }
421
422 IPC::ResponseBuilder rb{ctx, 2};
423 rb.Push(result);
424 }
425
426 void IsBrokenDatabaseWithClearFlag(HLERequestContext& ctx) {
427 LOG_DEBUG(Service_Mii, "called");
428
429 bool is_broken_with_clear_flag = false;
430 Result result = ResultSuccess;
431
432 if (!is_system) {
433 result = ResultPermissionDenied;
434 }
435
436 if (result.IsSuccess()) {
437 is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
438 }
439
440 IPC::ResponseBuilder rb{ctx, 3};
441 rb.Push(result);
442 rb.Push<u8>(is_broken_with_clear_flag);
204 } 443 }
205 444
206 void GetIndex(HLERequestContext& ctx) { 445 void GetIndex(HLERequestContext& ctx) {
@@ -236,13 +475,53 @@ private:
236 LOG_INFO(Service_Mii, "called"); 475 LOG_INFO(Service_Mii, "called");
237 476
238 CharInfo char_info{}; 477 CharInfo char_info{};
239 manager.ConvertV3ToCharInfo(char_info, mii_v3); 478 const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
240 479
241 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 480 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
242 rb.Push(ResultSuccess); 481 rb.Push(result);
482 rb.PushRaw<CharInfo>(char_info);
483 }
484
485 void ConvertCoreDataToCharInfo(HLERequestContext& ctx) {
486 IPC::RequestParser rp{ctx};
487 const auto core_data{rp.PopRaw<CoreData>()};
488
489 LOG_INFO(Service_Mii, "called");
490
491 CharInfo char_info{};
492 const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
493
494 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
495 rb.Push(result);
243 rb.PushRaw<CharInfo>(char_info); 496 rb.PushRaw<CharInfo>(char_info);
244 } 497 }
245 498
499 void ConvertCharInfoToCoreData(HLERequestContext& ctx) {
500 IPC::RequestParser rp{ctx};
501 const auto char_info{rp.PopRaw<CharInfo>()};
502
503 LOG_INFO(Service_Mii, "called");
504
505 CoreData core_data{};
506 const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
507
508 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
509 rb.Push(result);
510 rb.PushRaw<CoreData>(core_data);
511 }
512
513 void Append(HLERequestContext& ctx) {
514 IPC::RequestParser rp{ctx};
515 const auto char_info{rp.PopRaw<CharInfo>()};
516
517 LOG_INFO(Service_Mii, "called");
518
519 const auto result = manager.Append(metadata, char_info);
520
521 IPC::ResponseBuilder rb{ctx, 2};
522 rb.Push(result);
523 }
524
246 MiiManager manager{}; 525 MiiManager manager{};
247 DatabaseSessionMetadata metadata{}; 526 DatabaseSessionMetadata metadata{};
248 bool is_system{}; 527 bool is_system{};
@@ -278,9 +557,9 @@ public:
278 explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} { 557 explicit MiiImg(Core::System& system_) : ServiceFramework{system_, "miiimg"} {
279 // clang-format off 558 // clang-format off
280 static const FunctionInfo functions[] = { 559 static const FunctionInfo functions[] = {
281 {0, nullptr, "Initialize"}, 560 {0, &MiiImg::Initialize, "Initialize"},
282 {10, nullptr, "Reload"}, 561 {10, nullptr, "Reload"},
283 {11, nullptr, "GetCount"}, 562 {11, &MiiImg::GetCount, "GetCount"},
284 {12, nullptr, "IsEmpty"}, 563 {12, nullptr, "IsEmpty"},
285 {13, nullptr, "IsFull"}, 564 {13, nullptr, "IsFull"},
286 {14, nullptr, "GetAttribute"}, 565 {14, nullptr, "GetAttribute"},
@@ -297,6 +576,22 @@ public:
297 576
298 RegisterHandlers(functions); 577 RegisterHandlers(functions);
299 } 578 }
579
580private:
581 void Initialize(HLERequestContext& ctx) {
582 LOG_INFO(Service_Mii, "called");
583
584 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(ResultSuccess);
586 }
587
588 void GetCount(HLERequestContext& ctx) {
589 LOG_DEBUG(Service_Mii, "called");
590
591 IPC::ResponseBuilder rb{ctx, 3};
592 rb.Push(ResultSuccess);
593 rb.Push(0);
594 }
300}; 595};
301 596
302void LoopProcess(Core::System& system) { 597void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/mii/mii_database.cpp b/src/core/hle/service/mii/mii_database.cpp
new file mode 100644
index 000000000..3803e58e2
--- /dev/null
+++ b/src/core/hle/service/mii/mii_database.cpp
@@ -0,0 +1,142 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/mii/mii_database.h"
5#include "core/hle/service/mii/mii_result.h"
6#include "core/hle/service/mii/mii_util.h"
7
8namespace Service::Mii {
9
10u8 NintendoFigurineDatabase::GetDatabaseLength() const {
11 return database_length;
12}
13
14bool NintendoFigurineDatabase::IsFull() const {
15 return database_length >= MaxDatabaseLength;
16}
17
18StoreData NintendoFigurineDatabase::Get(std::size_t index) const {
19 StoreData store_data = miis.at(index);
20
21 // This hack is to make external database dumps compatible
22 store_data.SetDeviceChecksum();
23
24 return store_data;
25}
26
27u32 NintendoFigurineDatabase::GetCount(const DatabaseSessionMetadata& metadata) const {
28 if (magic == MiiMagic) {
29 return GetDatabaseLength();
30 }
31
32 u32 mii_count{};
33 for (std::size_t index = 0; index < mii_count; ++index) {
34 const auto& store_data = Get(index);
35 if (!store_data.IsSpecial()) {
36 mii_count++;
37 }
38 }
39
40 return mii_count;
41}
42
43bool NintendoFigurineDatabase::GetIndexByCreatorId(u32& out_index,
44 const Common::UUID& create_id) const {
45 for (std::size_t index = 0; index < database_length; ++index) {
46 if (miis[index].GetCreateId() == create_id) {
47 out_index = static_cast<u32>(index);
48 return true;
49 }
50 }
51
52 return false;
53}
54
55Result NintendoFigurineDatabase::Move(u32 current_index, u32 new_index) {
56 if (current_index == new_index) {
57 return ResultNotUpdated;
58 }
59
60 const StoreData store_data = miis[current_index];
61
62 if (new_index > current_index) {
63 // Shift left
64 const u32 index_diff = new_index - current_index;
65 for (std::size_t i = 0; i < index_diff; i++) {
66 miis[current_index + i] = miis[current_index + i + 1];
67 }
68 } else {
69 // Shift right
70 const u32 index_diff = current_index - new_index;
71 for (std::size_t i = 0; i < index_diff; i++) {
72 miis[current_index - i] = miis[current_index - i - 1];
73 }
74 }
75
76 miis[new_index] = store_data;
77 crc = GenerateDatabaseCrc();
78 return ResultSuccess;
79}
80
81void NintendoFigurineDatabase::Replace(u32 index, const StoreData& store_data) {
82 miis[index] = store_data;
83 crc = GenerateDatabaseCrc();
84}
85
86void NintendoFigurineDatabase::Add(const StoreData& store_data) {
87 miis[database_length] = store_data;
88 database_length++;
89 crc = GenerateDatabaseCrc();
90}
91
92void NintendoFigurineDatabase::Delete(u32 index) {
93 // Shift left
94 const s32 new_database_size = database_length - 1;
95 if (static_cast<s32>(index) < new_database_size) {
96 for (std::size_t i = index; i < static_cast<std::size_t>(new_database_size); i++) {
97 miis[i] = miis[i + 1];
98 }
99 }
100
101 database_length = static_cast<u8>(new_database_size);
102 crc = GenerateDatabaseCrc();
103}
104
105void NintendoFigurineDatabase::CleanDatabase() {
106 miis = {};
107 version = 1;
108 magic = DatabaseMagic;
109 database_length = 0;
110 crc = GenerateDatabaseCrc();
111}
112
113void NintendoFigurineDatabase::CorruptCrc() {
114 crc = GenerateDatabaseCrc();
115 crc = ~crc;
116}
117
118Result NintendoFigurineDatabase::CheckIntegrity() {
119 if (magic != DatabaseMagic) {
120 return ResultInvalidDatabaseSignature;
121 }
122
123 if (version != 1) {
124 return ResultInvalidDatabaseVersion;
125 }
126
127 if (crc != GenerateDatabaseCrc()) {
128 return ResultInvalidDatabaseChecksum;
129 }
130
131 if (database_length >= MaxDatabaseLength) {
132 return ResultInvalidDatabaseLength;
133 }
134
135 return ResultSuccess;
136}
137
138u16 NintendoFigurineDatabase::GenerateDatabaseCrc() {
139 return MiiUtil::CalculateCrc16(&magic, sizeof(NintendoFigurineDatabase) - sizeof(crc));
140}
141
142} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_database.h b/src/core/hle/service/mii/mii_database.h
new file mode 100644
index 000000000..3bd240f93
--- /dev/null
+++ b/src/core/hle/service/mii/mii_database.h
@@ -0,0 +1,66 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/mii/types/store_data.h"
8
9namespace Service::Mii {
10
11constexpr std::size_t MaxDatabaseLength{100};
12constexpr u32 MiiMagic{0xa523b78f};
13constexpr u32 DatabaseMagic{0x4244464e}; // NFDB
14
15class NintendoFigurineDatabase {
16public:
17 /// Returns the total mii count.
18 u8 GetDatabaseLength() const;
19
20 /// Returns true if database is full.
21 bool IsFull() const;
22
23 /// Returns the mii of the specified index.
24 StoreData Get(std::size_t index) const;
25
26 /// Returns the total mii count. Ignoring special mii.
27 u32 GetCount(const DatabaseSessionMetadata& metadata) const;
28
29 /// Returns the index of a mii. If the mii isn't found returns false.
30 bool GetIndexByCreatorId(u32& out_index, const Common::UUID& create_id) const;
31
32 /// Moves the location of a specific mii.
33 Result Move(u32 current_index, u32 new_index);
34
35 /// Replaces mii with new data.
36 void Replace(u32 index, const StoreData& store_data);
37
38 /// Adds a new mii to the end of the database.
39 void Add(const StoreData& store_data);
40
41 /// Removes mii from database and shifts left the remainding data.
42 void Delete(u32 index);
43
44 /// Deletes all contents with a fresh database
45 void CleanDatabase();
46
47 /// Intentionally sets a bad checksum
48 void CorruptCrc();
49
50 /// Returns success if database is valid otherwise returns the corresponding error code.
51 Result CheckIntegrity();
52
53private:
54 /// Returns the checksum of the database
55 u16 GenerateDatabaseCrc();
56
57 u32 magic{}; // 'NFDB'
58 std::array<StoreData, MaxDatabaseLength> miis{};
59 u8 version{};
60 u8 database_length{};
61 u16 crc{};
62};
63static_assert(sizeof(NintendoFigurineDatabase) == 0x1A98,
64 "NintendoFigurineDatabase has incorrect size.");
65
66}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_database_manager.cpp b/src/core/hle/service/mii/mii_database_manager.cpp
new file mode 100644
index 000000000..c39898594
--- /dev/null
+++ b/src/core/hle/service/mii/mii_database_manager.cpp
@@ -0,0 +1,420 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/fs/file.h"
6#include "common/fs/fs.h"
7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10
11#include "core/hle/service/mii/mii_database_manager.h"
12#include "core/hle/service/mii/mii_result.h"
13#include "core/hle/service/mii/mii_util.h"
14#include "core/hle/service/mii/types/char_info.h"
15#include "core/hle/service/mii/types/store_data.h"
16
17namespace Service::Mii {
18const char* DbFileName = "MiiDatabase.dat";
19
20DatabaseManager::DatabaseManager() {}
21
22Result DatabaseManager::MountSaveData() {
23 if (!is_save_data_mounted) {
24 system_save_dir =
25 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000030";
26 if (is_test_db) {
27 system_save_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
28 "system/save/8000000000000031";
29 }
30
31 // mount point should be "mii:"
32
33 if (!Common::FS::CreateDirs(system_save_dir)) {
34 return ResultUnknown;
35 }
36 }
37
38 is_save_data_mounted = true;
39 return ResultSuccess;
40}
41
42Result DatabaseManager::Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken) {
43 is_database_broken = false;
44 if (!is_save_data_mounted) {
45 return ResultInvalidArgument;
46 }
47
48 database.CleanDatabase();
49 update_counter++;
50 metadata.update_counter = update_counter;
51
52 const Common::FS::IOFile db_file{system_save_dir / DbFileName, Common::FS::FileAccessMode::Read,
53 Common::FS::FileType::BinaryFile};
54
55 if (!db_file.IsOpen()) {
56 return SaveDatabase();
57 }
58
59 if (Common::FS::GetSize(system_save_dir / DbFileName) != sizeof(NintendoFigurineDatabase)) {
60 is_database_broken = true;
61 }
62
63 if (db_file.Read(database) != 1) {
64 is_database_broken = true;
65 }
66
67 if (is_database_broken) {
68 // Dragons happen here for simplicity just clean the database
69 LOG_ERROR(Service_Mii, "Mii database is corrupted");
70 database.CleanDatabase();
71 return ResultUnknown;
72 }
73
74 const auto result = database.CheckIntegrity();
75
76 if (result.IsError()) {
77 LOG_ERROR(Service_Mii, "Mii database is corrupted 0x{:0x}", result.raw);
78 database.CleanDatabase();
79 return ResultSuccess;
80 }
81
82 LOG_INFO(Service_Mii, "Successfully loaded mii database. size={}",
83 database.GetDatabaseLength());
84 return ResultSuccess;
85}
86
87bool DatabaseManager::IsFullDatabase() const {
88 return database.GetDatabaseLength() == MaxDatabaseLength;
89}
90
91bool DatabaseManager::IsModified() const {
92 return is_moddified;
93}
94
95u64 DatabaseManager::GetUpdateCounter() const {
96 return update_counter;
97}
98
99u32 DatabaseManager::GetCount(const DatabaseSessionMetadata& metadata) const {
100 const u32 database_size = database.GetDatabaseLength();
101 if (metadata.magic == MiiMagic) {
102 return database_size;
103 }
104
105 // Special mii can't be used. Skip those.
106
107 u32 mii_count{};
108 for (std::size_t index = 0; index < database_size; ++index) {
109 const auto& store_data = database.Get(index);
110 if (store_data.IsSpecial()) {
111 continue;
112 }
113 mii_count++;
114 }
115
116 return mii_count;
117}
118
119void DatabaseManager::Get(StoreData& out_store_data, std::size_t index,
120 const DatabaseSessionMetadata& metadata) const {
121 if (metadata.magic == MiiMagic) {
122 out_store_data = database.Get(index);
123 return;
124 }
125
126 // The index refeers to the mii index without special mii.
127 // Search on the database until we find it
128
129 u32 virtual_index = 0;
130 const u32 database_size = database.GetDatabaseLength();
131 for (std::size_t i = 0; i < database_size; ++i) {
132 const auto& store_data = database.Get(i);
133 if (store_data.IsSpecial()) {
134 continue;
135 }
136 if (virtual_index == index) {
137 out_store_data = store_data;
138 return;
139 }
140 virtual_index++;
141 }
142
143 // This function doesn't fail. It returns the first mii instead
144 out_store_data = database.Get(0);
145}
146
147Result DatabaseManager::FindIndex(s32& out_index, const Common::UUID& create_id,
148 bool is_special) const {
149 u32 index{};
150 const bool is_found = database.GetIndexByCreatorId(index, create_id);
151
152 if (!is_found) {
153 return ResultNotFound;
154 }
155
156 if (is_special) {
157 out_index = index;
158 return ResultSuccess;
159 }
160
161 if (database.Get(index).IsSpecial()) {
162 return ResultNotFound;
163 }
164
165 out_index = 0;
166
167 if (index < 1) {
168 return ResultSuccess;
169 }
170
171 for (std::size_t i = 0; i <= index; ++i) {
172 if (database.Get(i).IsSpecial()) {
173 continue;
174 }
175 out_index++;
176 }
177 return ResultSuccess;
178}
179
180Result DatabaseManager::FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
181 const Common::UUID& create_id) const {
182 u32 index{};
183 const bool is_found = database.GetIndexByCreatorId(index, create_id);
184
185 if (!is_found) {
186 return ResultNotFound;
187 }
188
189 if (metadata.magic == MiiMagic) {
190 out_index = index;
191 return ResultSuccess;
192 }
193
194 if (database.Get(index).IsSpecial()) {
195 return ResultNotFound;
196 }
197
198 out_index = 0;
199
200 if (index < 1) {
201 return ResultSuccess;
202 }
203
204 // The index refeers to the mii index without special mii.
205 // Search on the database until we find it
206
207 for (std::size_t i = 0; i <= index; ++i) {
208 const auto& store_data = database.Get(i);
209 if (store_data.IsSpecial()) {
210 continue;
211 }
212 out_index++;
213 }
214 return ResultSuccess;
215}
216
217Result DatabaseManager::FindMoveIndex(u32& out_index, u32 new_index,
218 const Common::UUID& create_id) const {
219 const auto database_size = database.GetDatabaseLength();
220
221 if (database_size >= 1) {
222 u32 virtual_index{};
223 for (std::size_t i = 0; i < database_size; ++i) {
224 const StoreData& store_data = database.Get(i);
225 if (store_data.IsSpecial()) {
226 continue;
227 }
228 if (virtual_index == new_index) {
229 const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
230 if (!is_found) {
231 return ResultNotFound;
232 }
233 if (store_data.IsSpecial()) {
234 return ResultInvalidOperation;
235 }
236 return ResultSuccess;
237 }
238 virtual_index++;
239 }
240 }
241
242 const bool is_found = database.GetIndexByCreatorId(out_index, create_id);
243 if (!is_found) {
244 return ResultNotFound;
245 }
246 const StoreData& store_data = database.Get(out_index);
247 if (store_data.IsSpecial()) {
248 return ResultInvalidOperation;
249 }
250 return ResultSuccess;
251}
252
253Result DatabaseManager::Move(DatabaseSessionMetadata& metadata, u32 new_index,
254 const Common::UUID& create_id) {
255 u32 current_index{};
256 if (metadata.magic == MiiMagic) {
257 const bool is_found = database.GetIndexByCreatorId(current_index, create_id);
258 if (!is_found) {
259 return ResultNotFound;
260 }
261 } else {
262 const auto result = FindMoveIndex(current_index, new_index, create_id);
263 if (result.IsError()) {
264 return result;
265 }
266 }
267
268 const auto result = database.Move(current_index, new_index);
269 if (result.IsFailure()) {
270 return result;
271 }
272
273 is_moddified = true;
274 update_counter++;
275 metadata.update_counter = update_counter;
276 return ResultSuccess;
277}
278
279Result DatabaseManager::AddOrReplace(DatabaseSessionMetadata& metadata,
280 const StoreData& store_data) {
281 if (store_data.IsValid() != ValidationResult::NoErrors) {
282 return ResultInvalidStoreData;
283 }
284 if (metadata.magic != MiiMagic && store_data.IsSpecial()) {
285 return ResultInvalidOperation;
286 }
287
288 u32 index{};
289 const bool is_found = database.GetIndexByCreatorId(index, store_data.GetCreateId());
290 if (is_found) {
291 const StoreData& old_store_data = database.Get(index);
292
293 if (store_data.IsSpecial() != old_store_data.IsSpecial()) {
294 return ResultInvalidOperation;
295 }
296
297 database.Replace(index, store_data);
298 } else {
299 if (database.IsFull()) {
300 return ResultDatabaseFull;
301 }
302
303 database.Add(store_data);
304 }
305
306 is_moddified = true;
307 update_counter++;
308 metadata.update_counter = update_counter;
309 return ResultSuccess;
310}
311
312Result DatabaseManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
313 u32 index{};
314 const bool is_found = database.GetIndexByCreatorId(index, create_id);
315 if (!is_found) {
316 return ResultNotFound;
317 }
318
319 if (metadata.magic != MiiMagic) {
320 const auto& store_data = database.Get(index);
321 if (store_data.IsSpecial()) {
322 return ResultInvalidOperation;
323 }
324 }
325
326 database.Delete(index);
327
328 is_moddified = true;
329 update_counter++;
330 metadata.update_counter = update_counter;
331 return ResultSuccess;
332}
333
334Result DatabaseManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
335 if (char_info.Verify() != ValidationResult::NoErrors) {
336 return ResultInvalidCharInfo2;
337 }
338 if (char_info.GetType() == 1) {
339 return ResultInvalidCharInfoType;
340 }
341
342 u32 index{};
343 StoreData store_data{};
344
345 // Loop until the mii we created is not on the database
346 do {
347 store_data.BuildWithCharInfo(char_info);
348 } while (database.GetIndexByCreatorId(index, store_data.GetCreateId()));
349
350 const Result result = store_data.Restore();
351
352 if (result.IsSuccess() || result == ResultNotUpdated) {
353 return AddOrReplace(metadata, store_data);
354 }
355
356 return result;
357}
358
359Result DatabaseManager::DestroyFile(DatabaseSessionMetadata& metadata) {
360 database.CorruptCrc();
361
362 is_moddified = true;
363 update_counter++;
364 metadata.update_counter = update_counter;
365
366 const auto result = SaveDatabase();
367 database.CleanDatabase();
368
369 return result;
370}
371
372Result DatabaseManager::DeleteFile() {
373 const bool result = Common::FS::RemoveFile(system_save_dir / DbFileName);
374 // TODO: Return proper FS error here
375 return result ? ResultSuccess : ResultUnknown;
376}
377
378void DatabaseManager::Format(DatabaseSessionMetadata& metadata) {
379 database.CleanDatabase();
380 is_moddified = true;
381 update_counter++;
382 metadata.update_counter = update_counter;
383}
384
385Result DatabaseManager::SaveDatabase() {
386 // TODO: Replace unknown error codes with proper FS error codes when available
387
388 if (!Common::FS::Exists(system_save_dir / DbFileName)) {
389 if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
390 LOG_ERROR(Service_Mii, "Failed to create mii database");
391 return ResultUnknown;
392 }
393 }
394
395 const auto file_size = Common::FS::GetSize(system_save_dir / DbFileName);
396 if (file_size != 0 && file_size != sizeof(NintendoFigurineDatabase)) {
397 if (!Common::FS::RemoveFile(system_save_dir / DbFileName)) {
398 LOG_ERROR(Service_Mii, "Failed to delete mii database");
399 return ResultUnknown;
400 }
401 if (!Common::FS::NewFile(system_save_dir / DbFileName)) {
402 LOG_ERROR(Service_Mii, "Failed to create mii database");
403 return ResultUnknown;
404 }
405 }
406
407 const Common::FS::IOFile db_file{system_save_dir / DbFileName,
408 Common::FS::FileAccessMode::ReadWrite,
409 Common::FS::FileType::BinaryFile};
410
411 if (db_file.Write(database) != 1) {
412 LOG_ERROR(Service_Mii, "Failed to save mii database");
413 return ResultUnknown;
414 }
415
416 is_moddified = false;
417 return ResultSuccess;
418}
419
420} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_database_manager.h b/src/core/hle/service/mii/mii_database_manager.h
new file mode 100644
index 000000000..52c32be82
--- /dev/null
+++ b/src/core/hle/service/mii/mii_database_manager.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/fs/fs.h"
7#include "core/hle/result.h"
8#include "core/hle/service/mii/mii_database.h"
9
10namespace Service::Mii {
11class CharInfo;
12class StoreData;
13
14class DatabaseManager {
15public:
16 DatabaseManager();
17 Result MountSaveData();
18 Result Initialize(DatabaseSessionMetadata& metadata, bool& is_database_broken);
19
20 bool IsFullDatabase() const;
21 bool IsModified() const;
22 u64 GetUpdateCounter() const;
23
24 void Get(StoreData& out_store_data, std::size_t index,
25 const DatabaseSessionMetadata& metadata) const;
26 u32 GetCount(const DatabaseSessionMetadata& metadata) const;
27
28 Result FindIndex(s32& out_index, const Common::UUID& create_id, bool is_special) const;
29 Result FindIndex(const DatabaseSessionMetadata& metadata, u32& out_index,
30 const Common::UUID& create_id) const;
31 Result FindMoveIndex(u32& out_index, u32 new_index, const Common::UUID& create_id) const;
32
33 Result Move(DatabaseSessionMetadata& metadata, u32 current_index,
34 const Common::UUID& create_id);
35 Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& out_store_data);
36 Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
37 Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);
38
39 Result DestroyFile(DatabaseSessionMetadata& metadata);
40 Result DeleteFile();
41 void Format(DatabaseSessionMetadata& metadata);
42
43 Result SaveDatabase();
44
45private:
46 // This is the global value of
47 // nn::settings::fwdbg::GetSettingsItemValue("is_db_test_mode_enabled");
48 bool is_test_db{};
49
50 bool is_moddified{};
51 bool is_save_data_mounted{};
52 u64 update_counter{};
53 NintendoFigurineDatabase database{};
54
55 std::filesystem::path system_save_dir{};
56};
57
58}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 292d63777..a5a2a9232 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -1,38 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstring>
5#include <random>
6
7#include "common/assert.h"
8#include "common/logging/log.h" 4#include "common/logging/log.h"
9#include "common/string_util.h" 5#include "core/hle/service/mii/mii_database_manager.h"
10
11#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/mii/mii_manager.h" 6#include "core/hle/service/mii/mii_manager.h"
13#include "core/hle/service/mii/mii_result.h" 7#include "core/hle/service/mii/mii_result.h"
14#include "core/hle/service/mii/mii_util.h" 8#include "core/hle/service/mii/mii_util.h"
9#include "core/hle/service/mii/types/char_info.h"
15#include "core/hle/service/mii/types/core_data.h" 10#include "core/hle/service/mii/types/core_data.h"
16#include "core/hle/service/mii/types/raw_data.h" 11#include "core/hle/service/mii/types/raw_data.h"
12#include "core/hle/service/mii/types/store_data.h"
13#include "core/hle/service/mii/types/ver3_store_data.h"
17 14
18namespace Service::Mii { 15namespace Service::Mii {
19constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; 16constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
20 17
21MiiManager::MiiManager() {} 18MiiManager::MiiManager() {}
22 19
20Result MiiManager::Initialize(DatabaseSessionMetadata& metadata) {
21 database_manager.MountSaveData();
22 database_manager.Initialize(metadata, is_broken_with_clear_flag);
23 return ResultSuccess;
24}
25
26void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
27 StoreData store_data{};
28 store_data.BuildDefault(index);
29 out_char_info.SetFromStoreData(store_data);
30}
31
32void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
33 StoreData store_data{};
34 store_data.BuildBase(gender);
35 out_char_info.SetFromStoreData(store_data);
36}
37
38void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
39 StoreData store_data{};
40 store_data.BuildRandom(age, gender, race);
41 out_char_info.SetFromStoreData(store_data);
42}
43
44bool MiiManager::IsFullDatabase() const {
45 return database_manager.IsFullDatabase();
46}
47
48void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const {
49 metadata.interface_version = version;
50}
51
23bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { 52bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
24 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 53 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
25 return false; 54 return false;
26 } 55 }
27 56
28 const auto metadata_update_counter = metadata.update_counter; 57 const u64 metadata_update_counter = metadata.update_counter;
29 metadata.update_counter = update_counter; 58 const u64 database_update_counter = database_manager.GetUpdateCounter();
30 return metadata_update_counter != update_counter; 59 metadata.update_counter = database_update_counter;
31} 60 return metadata_update_counter != database_update_counter;
32
33bool MiiManager::IsFullDatabase() const {
34 // TODO(bunnei): We don't implement the Mii database, so it cannot be full
35 return false;
36} 61}
37 62
38u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const { 63u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
@@ -41,72 +66,343 @@ u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag sou
41 mii_count += DefaultMiiCount; 66 mii_count += DefaultMiiCount;
42 } 67 }
43 if ((source_flag & SourceFlag::Database) != SourceFlag::None) { 68 if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
44 // TODO(bunnei): We don't implement the Mii database, but when we do, update this 69 mii_count += database_manager.GetCount(metadata);
45 } 70 }
46 return mii_count; 71 return mii_count;
47} 72}
48 73
49Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, 74Result MiiManager::Move(DatabaseSessionMetadata& metadata, u32 index,
50 const CharInfo& char_info, SourceFlag source_flag) { 75 const Common::UUID& create_id) {
51 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 76 const auto result = database_manager.Move(metadata, index, create_id);
77
78 if (result.IsFailure()) {
79 return result;
80 }
81
82 if (!database_manager.IsModified()) {
83 return ResultNotUpdated;
84 }
85
86 return database_manager.SaveDatabase();
87}
88
89Result MiiManager::AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data) {
90 const auto result = database_manager.AddOrReplace(metadata, store_data);
91
92 if (result.IsFailure()) {
93 return result;
94 }
95
96 if (!database_manager.IsModified()) {
97 return ResultNotUpdated;
98 }
99
100 return database_manager.SaveDatabase();
101}
102
103Result MiiManager::Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id) {
104 const auto result = database_manager.Delete(metadata, create_id);
105
106 if (result.IsFailure()) {
107 return result;
108 }
109
110 if (!database_manager.IsModified()) {
111 return ResultNotUpdated;
112 }
113
114 return database_manager.SaveDatabase();
115}
116
117s32 MiiManager::FindIndex(const Common::UUID& create_id, bool is_special) const {
118 s32 index{};
119 const auto result = database_manager.FindIndex(index, create_id, is_special);
120 if (result.IsError()) {
121 index = -1;
122 }
123 return index;
124}
125
126Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
127 s32& out_index) const {
128 if (char_info.Verify() != ValidationResult::NoErrors) {
129 return ResultInvalidCharInfo;
130 }
131
132 s32 index{};
133 Result result = {};
134 // FindIndex(index);
135
136 if (result.IsError()) {
137 return ResultNotFound;
138 }
139
140 if (index == -1) {
52 return ResultNotFound; 141 return ResultNotFound;
53 } 142 }
54 143
55 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry 144 out_index = index;
56 return ResultNotFound; 145 return ResultSuccess;
57} 146}
58 147
59void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const { 148Result MiiManager::Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info) {
60 StoreData store_data{}; 149 const auto result = database_manager.Append(metadata, char_info);
61 store_data.BuildDefault(index); 150
62 out_char_info.SetFromStoreData(store_data); 151 if (result.IsError()) {
152 return ResultNotFound;
153 }
154
155 if (!database_manager.IsModified()) {
156 return ResultNotUpdated;
157 }
158
159 return database_manager.SaveDatabase();
63} 160}
64 161
65void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const { 162bool MiiManager::IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata) {
163 const bool is_broken = is_broken_with_clear_flag;
164 if (is_broken_with_clear_flag) {
165 is_broken_with_clear_flag = false;
166 database_manager.Format(metadata);
167 database_manager.SaveDatabase();
168 }
169 return is_broken;
170}
171
172Result MiiManager::DestroyFile(DatabaseSessionMetadata& metadata) {
173 is_broken_with_clear_flag = true;
174 return database_manager.DestroyFile(metadata);
175}
176
177Result MiiManager::DeleteFile() {
178 return database_manager.DeleteFile();
179}
180
181Result MiiManager::Format(DatabaseSessionMetadata& metadata) {
182 database_manager.Format(metadata);
183
184 if (!database_manager.IsModified()) {
185 return ResultNotUpdated;
186 }
187 return database_manager.SaveDatabase();
188}
189
190Result MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
191 if (!mii_v3.IsValid()) {
192 return ResultInvalidCharInfo;
193 }
194
66 StoreData store_data{}; 195 StoreData store_data{};
67 store_data.BuildBase(gender); 196 mii_v3.BuildToStoreData(store_data);
197 const auto name = store_data.GetNickname();
198 if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
199 store_data.SetInvalidName();
200 }
201
68 out_char_info.SetFromStoreData(store_data); 202 out_char_info.SetFromStoreData(store_data);
203 return ResultSuccess;
69} 204}
70 205
71void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const { 206Result MiiManager::ConvertCoreDataToCharInfo(CharInfo& out_char_info,
207 const CoreData& core_data) const {
208 if (core_data.IsValid() != ValidationResult::NoErrors) {
209 return ResultInvalidCharInfo;
210 }
211
72 StoreData store_data{}; 212 StoreData store_data{};
73 store_data.BuildRandom(age, gender, race); 213 store_data.BuildWithCoreData(core_data);
214 const auto name = store_data.GetNickname();
215 if (!MiiUtil::IsFontRegionValid(store_data.GetFontRegion(), name.data)) {
216 store_data.SetInvalidName();
217 }
218
74 out_char_info.SetFromStoreData(store_data); 219 out_char_info.SetFromStoreData(store_data);
220 return ResultSuccess;
221}
222
223Result MiiManager::ConvertCharInfoToCoreData(CoreData& out_core_data,
224 const CharInfo& char_info) const {
225 if (char_info.Verify() != ValidationResult::NoErrors) {
226 return ResultInvalidCharInfo;
227 }
228
229 out_core_data.BuildFromCharInfo(char_info);
230 const auto name = out_core_data.GetNickname();
231 if (!MiiUtil::IsFontRegionValid(out_core_data.GetFontRegion(), name.data)) {
232 out_core_data.SetNickname(out_core_data.GetInvalidNickname());
233 }
234
235 return ResultSuccess;
75} 236}
76 237
77void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const { 238Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
239 const CharInfo& char_info, SourceFlag source_flag) const {
240 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
241 return ResultNotFound;
242 }
243
244 if (metadata.IsInterfaceVersionSupported(1)) {
245 if (char_info.Verify() != ValidationResult::NoErrors) {
246 return ResultInvalidCharInfo;
247 }
248 }
249
250 u32 index{};
251 Result result = database_manager.FindIndex(metadata, index, char_info.GetCreateId());
252
253 if (result.IsError()) {
254 return result;
255 }
256
78 StoreData store_data{}; 257 StoreData store_data{};
79 mii_v3.BuildToStoreData(store_data); 258 database_manager.Get(store_data, index, metadata);
259
260 if (store_data.GetType() != char_info.GetType()) {
261 return ResultNotFound;
262 }
263
80 out_char_info.SetFromStoreData(store_data); 264 out_char_info.SetFromStoreData(store_data);
265
266 if (char_info == out_char_info) {
267 return ResultNotUpdated;
268 }
269
270 return ResultSuccess;
271}
272
273Result MiiManager::UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
274 const StoreData& store_data, SourceFlag source_flag) const {
275 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
276 return ResultNotFound;
277 }
278
279 if (metadata.IsInterfaceVersionSupported(1)) {
280 if (store_data.IsValid() != ValidationResult::NoErrors) {
281 return ResultInvalidCharInfo;
282 }
283 }
284
285 u32 index{};
286 Result result = database_manager.FindIndex(metadata, index, store_data.GetCreateId());
287
288 if (result.IsError()) {
289 return result;
290 }
291
292 database_manager.Get(out_store_data, index, metadata);
293
294 if (out_store_data.GetType() != store_data.GetType()) {
295 return ResultNotFound;
296 }
297
298 if (store_data == out_store_data) {
299 return ResultNotUpdated;
300 }
301
302 return ResultSuccess;
81} 303}
82 304
83Result MiiManager::Get(const DatabaseSessionMetadata& metadata, 305Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
84 std::span<CharInfoElement> out_elements, u32& out_count, 306 std::span<CharInfoElement> out_elements, u32& out_count,
85 SourceFlag source_flag) { 307 SourceFlag source_flag) const {
86 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 308 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
87 return BuildDefault(out_elements, out_count, source_flag); 309 return BuildDefault(out_elements, out_count, source_flag);
88 } 310 }
89 311
90 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry 312 const auto mii_count = database_manager.GetCount(metadata);
313
314 for (std::size_t index = 0; index < mii_count; ++index) {
315 if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
316 return ResultInvalidArgumentSize;
317 }
318
319 StoreData store_data{};
320 database_manager.Get(store_data, index, metadata);
321
322 out_elements[out_count].source = Source::Database;
323 out_elements[out_count].char_info.SetFromStoreData(store_data);
324 out_count++;
325 }
91 326
92 // Include default Mii at the end of the list 327 // Include default Mii at the end of the list
93 return BuildDefault(out_elements, out_count, source_flag); 328 return BuildDefault(out_elements, out_count, source_flag);
94} 329}
95 330
96Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, 331Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
97 u32& out_count, SourceFlag source_flag) { 332 u32& out_count, SourceFlag source_flag) const {
98 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 333 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
99 return BuildDefault(out_char_info, out_count, source_flag); 334 return BuildDefault(out_char_info, out_count, source_flag);
100 } 335 }
101 336
102 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry 337 const auto mii_count = database_manager.GetCount(metadata);
338
339 for (std::size_t index = 0; index < mii_count; ++index) {
340 if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
341 return ResultInvalidArgumentSize;
342 }
343
344 StoreData store_data{};
345 database_manager.Get(store_data, index, metadata);
346
347 out_char_info[out_count].SetFromStoreData(store_data);
348 out_count++;
349 }
103 350
104 // Include default Mii at the end of the list 351 // Include default Mii at the end of the list
105 return BuildDefault(out_char_info, out_count, source_flag); 352 return BuildDefault(out_char_info, out_count, source_flag);
106} 353}
107 354
355Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
356 std::span<StoreDataElement> out_elements, u32& out_count,
357 SourceFlag source_flag) const {
358 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
359 return BuildDefault(out_elements, out_count, source_flag);
360 }
361
362 const auto mii_count = database_manager.GetCount(metadata);
363
364 for (std::size_t index = 0; index < mii_count; ++index) {
365 if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
366 return ResultInvalidArgumentSize;
367 }
368
369 StoreData store_data{};
370 database_manager.Get(store_data, index, metadata);
371
372 out_elements[out_count].store_data = store_data;
373 out_elements[out_count].source = Source::Database;
374 out_count++;
375 }
376
377 // Include default Mii at the end of the list
378 return BuildDefault(out_elements, out_count, source_flag);
379}
380
381Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<StoreData> out_store_data,
382 u32& out_count, SourceFlag source_flag) const {
383 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
384 return BuildDefault(out_store_data, out_count, source_flag);
385 }
386
387 const auto mii_count = database_manager.GetCount(metadata);
388
389 for (std::size_t index = 0; index < mii_count; ++index) {
390 if (out_store_data.size() <= static_cast<std::size_t>(out_count)) {
391 return ResultInvalidArgumentSize;
392 }
393
394 StoreData store_data{};
395 database_manager.Get(store_data, index, metadata);
396
397 out_store_data[out_count] = store_data;
398 out_count++;
399 }
400
401 // Include default Mii at the end of the list
402 return BuildDefault(out_store_data, out_count, source_flag);
403}
108Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, 404Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
109 SourceFlag source_flag) { 405 SourceFlag source_flag) const {
110 if ((source_flag & SourceFlag::Default) == SourceFlag::None) { 406 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
111 return ResultSuccess; 407 return ResultSuccess;
112 } 408 }
@@ -129,7 +425,7 @@ Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& ou
129} 425}
130 426
131Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, 427Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
132 SourceFlag source_flag) { 428 SourceFlag source_flag) const {
133 if ((source_flag & SourceFlag::Default) == SourceFlag::None) { 429 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
134 return ResultSuccess; 430 return ResultSuccess;
135 } 431 }
@@ -150,23 +446,41 @@ Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_coun
150 return ResultSuccess; 446 return ResultSuccess;
151} 447}
152 448
153Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info, 449Result MiiManager::BuildDefault(std::span<StoreDataElement> out_elements, u32& out_count,
154 s32& out_index) { 450 SourceFlag source_flag) const {
155 451 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
156 if (char_info.Verify() != ValidationResult::NoErrors) { 452 return ResultSuccess;
157 return ResultInvalidCharInfo;
158 } 453 }
159 454
160 constexpr u32 INVALID_INDEX{0xFFFFFFFF}; 455 for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
456 if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
457 return ResultInvalidArgumentSize;
458 }
161 459
162 out_index = INVALID_INDEX; 460 out_elements[out_count].store_data.BuildDefault(static_cast<u32>(index));
461 out_elements[out_count].source = Source::Default;
462 out_count++;
463 }
163 464
164 // TODO(bunnei): We don't implement the Mii database, so we can't have an index 465 return ResultSuccess;
165 return ResultNotFound;
166} 466}
167 467
168void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) { 468Result MiiManager::BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
169 metadata.interface_version = version; 469 SourceFlag source_flag) const {
470 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
471 return ResultSuccess;
472 }
473
474 for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
475 if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
476 return ResultInvalidArgumentSize;
477 }
478
479 out_char_info[out_count].BuildDefault(static_cast<u32>(index));
480 out_count++;
481 }
482
483 return ResultSuccess;
170} 484}
171 485
172} // namespace Service::Mii 486} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index a2e7a6d73..48d8e8bb7 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -3,47 +3,85 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <vector> 6#include <span>
7 7
8#include "core/hle/result.h" 8#include "core/hle/result.h"
9#include "core/hle/service/mii/mii_database_manager.h"
9#include "core/hle/service/mii/mii_types.h" 10#include "core/hle/service/mii/mii_types.h"
10#include "core/hle/service/mii/types/char_info.h"
11#include "core/hle/service/mii/types/store_data.h"
12#include "core/hle/service/mii/types/ver3_store_data.h"
13 11
14namespace Service::Mii { 12namespace Service::Mii {
13class CharInfo;
14class CoreData;
15class StoreData;
16class Ver3StoreData;
15 17
16// The Mii manager is responsible for loading and storing the Miis to the database in NAND along 18struct CharInfoElement;
17// with providing an easy interface for HLE emulation of the mii service. 19struct StoreDataElement;
20
21// The Mii manager is responsible for handling mii operations along with providing an easy interface
22// for HLE emulation of the mii service.
18class MiiManager { 23class MiiManager {
19public: 24public:
20 MiiManager(); 25 MiiManager();
26 Result Initialize(DatabaseSessionMetadata& metadata);
21 27
22 bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; 28 // Auto generated mii
29 void BuildDefault(CharInfo& out_char_info, u32 index) const;
30 void BuildBase(CharInfo& out_char_info, Gender gender) const;
31 void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const;
23 32
33 // Database operations
24 bool IsFullDatabase() const; 34 bool IsFullDatabase() const;
35 void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) const;
36 bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
25 u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const; 37 u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
26 Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info, 38 Result Move(DatabaseSessionMetadata& metadata, u32 index, const Common::UUID& create_id);
27 const CharInfo& char_info, SourceFlag source_flag); 39 Result AddOrReplace(DatabaseSessionMetadata& metadata, const StoreData& store_data);
40 Result Delete(DatabaseSessionMetadata& metadata, const Common::UUID& create_id);
41 s32 FindIndex(const Common::UUID& create_id, bool is_special) const;
42 Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
43 s32& out_index) const;
44 Result Append(DatabaseSessionMetadata& metadata, const CharInfo& char_info);
45
46 // Test database operations
47 bool IsBrokenWithClearFlag(DatabaseSessionMetadata& metadata);
48 Result DestroyFile(DatabaseSessionMetadata& metadata);
49 Result DeleteFile();
50 Result Format(DatabaseSessionMetadata& metadata);
51
52 // Mii conversions
53 Result ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
54 Result ConvertCoreDataToCharInfo(CharInfo& out_char_info, const CoreData& core_data) const;
55 Result ConvertCharInfoToCoreData(CoreData& out_core_data, const CharInfo& char_info) const;
56 Result UpdateLatest(const DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
57 const CharInfo& char_info, SourceFlag source_flag) const;
58 Result UpdateLatest(const DatabaseSessionMetadata& metadata, StoreData& out_store_data,
59 const StoreData& store_data, SourceFlag source_flag) const;
60
61 // Overloaded getters
28 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements, 62 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
29 u32& out_count, SourceFlag source_flag); 63 u32& out_count, SourceFlag source_flag) const;
30 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info, 64 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
31 u32& out_count, SourceFlag source_flag); 65 u32& out_count, SourceFlag source_flag) const;
32 void BuildDefault(CharInfo& out_char_info, u32 index) const; 66 Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreDataElement> out_elements,
33 void BuildBase(CharInfo& out_char_info, Gender gender) const; 67 u32& out_count, SourceFlag source_flag) const;
34 void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const; 68 Result Get(const DatabaseSessionMetadata& metadata, std::span<StoreData> out_store_data,
35 void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const; 69 u32& out_count, SourceFlag source_flag) const;
36 std::vector<CharInfoElement> GetDefault(SourceFlag source_flag);
37 Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
38 s32& out_index);
39 void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
40 70
41private: 71private:
42 Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count, 72 Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
43 SourceFlag source_flag); 73 SourceFlag source_flag) const;
44 Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag); 74 Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
75 SourceFlag source_flag) const;
76 Result BuildDefault(std::span<StoreDataElement> out_char_info, u32& out_count,
77 SourceFlag source_flag) const;
78 Result BuildDefault(std::span<StoreData> out_char_info, u32& out_count,
79 SourceFlag source_flag) const;
80
81 DatabaseManager database_manager{};
45 82
46 u64 update_counter{}; 83 // This should be a global value
84 bool is_broken_with_clear_flag{};
47}; 85};
48 86
49}; // namespace Service::Mii 87}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h
index 021cb76da..e2c36e556 100644
--- a/src/core/hle/service/mii/mii_result.h
+++ b/src/core/hle/service/mii/mii_result.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -13,8 +13,15 @@ constexpr Result ResultNotUpdated{ErrorModule::Mii, 3};
13constexpr Result ResultNotFound{ErrorModule::Mii, 4}; 13constexpr Result ResultNotFound{ErrorModule::Mii, 4};
14constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5}; 14constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5};
15constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100}; 15constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100};
16constexpr Result ResultInvalidDatabaseChecksum{ErrorModule::Mii, 101};
17constexpr Result ResultInvalidDatabaseSignature{ErrorModule::Mii, 103};
18constexpr Result ResultInvalidDatabaseVersion{ErrorModule::Mii, 104};
19constexpr Result ResultInvalidDatabaseLength{ErrorModule::Mii, 105};
20constexpr Result ResultInvalidCharInfo2{ErrorModule::Mii, 107};
16constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109}; 21constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109};
17constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202}; 22constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202};
18constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203}; 23constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203};
24constexpr Result ResultTestModeOnly{ErrorModule::Mii, 204};
25constexpr Result ResultInvalidCharInfoType{ErrorModule::Mii, 205};
19 26
20}; // namespace Service::Mii 27}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h
index 95476f745..f43efd83c 100644
--- a/src/core/hle/service/mii/mii_types.h
+++ b/src/core/hle/service/mii/mii_types.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
@@ -13,6 +13,7 @@
13 13
14namespace Service::Mii { 14namespace Service::Mii {
15 15
16constexpr std::size_t MaxNameSize = 10;
16constexpr u8 MaxHeight = 127; 17constexpr u8 MaxHeight = 127;
17constexpr u8 MaxBuild = 127; 18constexpr u8 MaxBuild = 127;
18constexpr u8 MaxType = 1; 19constexpr u8 MaxType = 1;
@@ -26,14 +27,14 @@ constexpr u8 MaxEyebrowScale = 8;
26constexpr u8 MaxEyebrowAspect = 6; 27constexpr u8 MaxEyebrowAspect = 6;
27constexpr u8 MaxEyebrowRotate = 11; 28constexpr u8 MaxEyebrowRotate = 11;
28constexpr u8 MaxEyebrowX = 12; 29constexpr u8 MaxEyebrowX = 12;
29constexpr u8 MaxEyebrowY = 18; 30constexpr u8 MaxEyebrowY = 15;
30constexpr u8 MaxNoseScale = 8; 31constexpr u8 MaxNoseScale = 8;
31constexpr u8 MaxNoseY = 18; 32constexpr u8 MaxNoseY = 18;
32constexpr u8 MaxMouthScale = 8; 33constexpr u8 MaxMouthScale = 8;
33constexpr u8 MaxMoutAspect = 6; 34constexpr u8 MaxMoutAspect = 6;
34constexpr u8 MaxMouthY = 18; 35constexpr u8 MaxMouthY = 18;
35constexpr u8 MaxMustacheScale = 8; 36constexpr u8 MaxMustacheScale = 8;
36constexpr u8 MasMustacheY = 16; 37constexpr u8 MaxMustacheY = 16;
37constexpr u8 MaxGlassScale = 7; 38constexpr u8 MaxGlassScale = 7;
38constexpr u8 MaxGlassY = 20; 39constexpr u8 MaxGlassY = 20;
39constexpr u8 MaxMoleScale = 8; 40constexpr u8 MaxMoleScale = 8;
@@ -599,22 +600,19 @@ enum class ValidationResult : u32 {
599 InvalidRegionMove = 0x31, 600 InvalidRegionMove = 0x31,
600 InvalidCreateId = 0x32, 601 InvalidCreateId = 0x32,
601 InvalidName = 0x33, 602 InvalidName = 0x33,
603 InvalidChecksum = 0x34,
602 InvalidType = 0x35, 604 InvalidType = 0x35,
603}; 605};
604 606
605struct Nickname { 607struct Nickname {
606 static constexpr std::size_t MaxNameSize = 10; 608 std::array<char16_t, MaxNameSize> data{};
607 std::array<char16_t, MaxNameSize> data;
608 609
609 // Checks for null, non-zero terminated or dirty strings 610 // Checks for null or dirty strings
610 bool IsValid() const { 611 bool IsValid() const {
611 if (data[0] == 0) { 612 if (data[0] == 0) {
612 return false; 613 return false;
613 } 614 }
614 615
615 if (data[MaxNameSize] != 0) {
616 return false;
617 }
618 std::size_t index = 1; 616 std::size_t index = 1;
619 while (data[index] != 0) { 617 while (data[index] != 0) {
620 index++; 618 index++;
diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h
index ddb544c23..3534fa31d 100644
--- a/src/core/hle/service/mii/mii_util.h
+++ b/src/core/hle/service/mii/mii_util.h
@@ -28,6 +28,32 @@ public:
28 return Common::swap16(static_cast<u16>(crc)); 28 return Common::swap16(static_cast<u16>(crc));
29 } 29 }
30 30
31 static u16 CalculateDeviceCrc16(const Common::UUID& uuid, std::size_t data_size) {
32 constexpr u16 magic{0x1021};
33 s32 crc{};
34
35 for (std::size_t i = 0; i < uuid.uuid.size(); i++) {
36 for (std::size_t j = 0; j < 8; j++) {
37 crc <<= 1;
38 if ((crc & 0x10000) != 0) {
39 crc = crc ^ magic;
40 }
41 }
42 crc ^= uuid.uuid[i];
43 }
44
45 // As much as this looks wrong this is what N's does
46
47 for (std::size_t i = 0; i < data_size * 8; i++) {
48 crc <<= 1;
49 if ((crc & 0x10000) != 0) {
50 crc = crc ^ magic;
51 }
52 }
53
54 return Common::swap16(static_cast<u16>(crc));
55 }
56
31 static Common::UUID MakeCreateId() { 57 static Common::UUID MakeCreateId() {
32 return Common::UUID::MakeRandomRFC4122V4(); 58 return Common::UUID::MakeRandomRFC4122V4();
33 } 59 }
diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp
index bb948c628..e90124af4 100644
--- a/src/core/hle/service/mii/types/char_info.cpp
+++ b/src/core/hle/service/mii/types/char_info.cpp
@@ -37,7 +37,7 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) {
37 eyebrow_aspect = store_data.GetEyebrowAspect(); 37 eyebrow_aspect = store_data.GetEyebrowAspect();
38 eyebrow_rotate = store_data.GetEyebrowRotate(); 38 eyebrow_rotate = store_data.GetEyebrowRotate();
39 eyebrow_x = store_data.GetEyebrowX(); 39 eyebrow_x = store_data.GetEyebrowX();
40 eyebrow_y = store_data.GetEyebrowY(); 40 eyebrow_y = store_data.GetEyebrowY() + 3;
41 nose_type = store_data.GetNoseType(); 41 nose_type = store_data.GetNoseType();
42 nose_scale = store_data.GetNoseScale(); 42 nose_scale = store_data.GetNoseScale();
43 nose_y = store_data.GetNoseY(); 43 nose_y = store_data.GetNoseY();
@@ -150,7 +150,7 @@ ValidationResult CharInfo::Verify() const {
150 if (eyebrow_x > MaxEyebrowX) { 150 if (eyebrow_x > MaxEyebrowX) {
151 return ValidationResult::InvalidEyebrowX; 151 return ValidationResult::InvalidEyebrowX;
152 } 152 }
153 if (eyebrow_y > MaxEyebrowY) { 153 if (eyebrow_y - 3 > MaxEyebrowY) {
154 return ValidationResult::InvalidEyebrowY; 154 return ValidationResult::InvalidEyebrowY;
155 } 155 }
156 if (nose_type > NoseType::Max) { 156 if (nose_type > NoseType::Max) {
@@ -189,7 +189,7 @@ ValidationResult CharInfo::Verify() const {
189 if (mustache_scale > MaxMustacheScale) { 189 if (mustache_scale > MaxMustacheScale) {
190 return ValidationResult::InvalidMustacheScale; 190 return ValidationResult::InvalidMustacheScale;
191 } 191 }
192 if (mustache_y > MasMustacheY) { 192 if (mustache_y > MaxMustacheY) {
193 return ValidationResult::InvalidMustacheY; 193 return ValidationResult::InvalidMustacheY;
194 } 194 }
195 if (glass_type > GlassType::Max) { 195 if (glass_type > GlassType::Max) {
diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h
index d069b221f..d0c457fd5 100644
--- a/src/core/hle/service/mii/types/char_info.h
+++ b/src/core/hle/service/mii/types/char_info.h
@@ -70,59 +70,59 @@ public:
70 bool operator==(const CharInfo& info); 70 bool operator==(const CharInfo& info);
71 71
72private: 72private:
73 Common::UUID create_id; 73 Common::UUID create_id{};
74 Nickname name; 74 Nickname name{};
75 u16 null_terminator; 75 u16 null_terminator{};
76 FontRegion font_region; 76 FontRegion font_region{};
77 FavoriteColor favorite_color; 77 FavoriteColor favorite_color{};
78 Gender gender; 78 Gender gender{};
79 u8 height; 79 u8 height{};
80 u8 build; 80 u8 build{};
81 u8 type; 81 u8 type{};
82 u8 region_move; 82 u8 region_move{};
83 FacelineType faceline_type; 83 FacelineType faceline_type{};
84 FacelineColor faceline_color; 84 FacelineColor faceline_color{};
85 FacelineWrinkle faceline_wrinkle; 85 FacelineWrinkle faceline_wrinkle{};
86 FacelineMake faceline_make; 86 FacelineMake faceline_make{};
87 HairType hair_type; 87 HairType hair_type{};
88 CommonColor hair_color; 88 CommonColor hair_color{};
89 HairFlip hair_flip; 89 HairFlip hair_flip{};
90 EyeType eye_type; 90 EyeType eye_type{};
91 CommonColor eye_color; 91 CommonColor eye_color{};
92 u8 eye_scale; 92 u8 eye_scale{};
93 u8 eye_aspect; 93 u8 eye_aspect{};
94 u8 eye_rotate; 94 u8 eye_rotate{};
95 u8 eye_x; 95 u8 eye_x{};
96 u8 eye_y; 96 u8 eye_y{};
97 EyebrowType eyebrow_type; 97 EyebrowType eyebrow_type{};
98 CommonColor eyebrow_color; 98 CommonColor eyebrow_color{};
99 u8 eyebrow_scale; 99 u8 eyebrow_scale{};
100 u8 eyebrow_aspect; 100 u8 eyebrow_aspect{};
101 u8 eyebrow_rotate; 101 u8 eyebrow_rotate{};
102 u8 eyebrow_x; 102 u8 eyebrow_x{};
103 u8 eyebrow_y; 103 u8 eyebrow_y{};
104 NoseType nose_type; 104 NoseType nose_type{};
105 u8 nose_scale; 105 u8 nose_scale{};
106 u8 nose_y; 106 u8 nose_y{};
107 MouthType mouth_type; 107 MouthType mouth_type{};
108 CommonColor mouth_color; 108 CommonColor mouth_color{};
109 u8 mouth_scale; 109 u8 mouth_scale{};
110 u8 mouth_aspect; 110 u8 mouth_aspect{};
111 u8 mouth_y; 111 u8 mouth_y{};
112 CommonColor beard_color; 112 CommonColor beard_color{};
113 BeardType beard_type; 113 BeardType beard_type{};
114 MustacheType mustache_type; 114 MustacheType mustache_type{};
115 u8 mustache_scale; 115 u8 mustache_scale{};
116 u8 mustache_y; 116 u8 mustache_y{};
117 GlassType glass_type; 117 GlassType glass_type{};
118 CommonColor glass_color; 118 CommonColor glass_color{};
119 u8 glass_scale; 119 u8 glass_scale{};
120 u8 glass_y; 120 u8 glass_y{};
121 MoleType mole_type; 121 MoleType mole_type{};
122 u8 mole_scale; 122 u8 mole_scale{};
123 u8 mole_x; 123 u8 mole_x{};
124 u8 mole_y; 124 u8 mole_y{};
125 u8 padding; 125 u8 padding{};
126}; 126};
127static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size."); 127static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
128static_assert(std::has_unique_object_representations_v<CharInfo>, 128static_assert(std::has_unique_object_representations_v<CharInfo>,
diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp
index 659288b51..465c6293a 100644
--- a/src/core/hle/service/mii/types/core_data.cpp
+++ b/src/core/hle/service/mii/types/core_data.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "core/hle/service/mii/mii_util.h" 5#include "core/hle/service/mii/mii_util.h"
6#include "core/hle/service/mii/types/char_info.h"
6#include "core/hle/service/mii/types/core_data.h" 7#include "core/hle/service/mii/types/core_data.h"
7#include "core/hle/service/mii/types/raw_data.h" 8#include "core/hle/service/mii/types/raw_data.h"
8 9
@@ -185,9 +186,211 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
185 SetMoleY(20); 186 SetMoleY(20);
186} 187}
187 188
188u32 CoreData::IsValid() const { 189void CoreData::BuildFromCharInfo(const CharInfo& char_info) {
189 // TODO: Complete this 190 name = char_info.GetNickname();
190 return 0; 191 SetFontRegion(char_info.GetFontRegion());
192 SetFavoriteColor(char_info.GetFavoriteColor());
193 SetGender(char_info.GetGender());
194 SetHeight(char_info.GetHeight());
195 SetBuild(char_info.GetBuild());
196 SetType(char_info.GetType());
197 SetRegionMove(char_info.GetRegionMove());
198 SetFacelineType(char_info.GetFacelineType());
199 SetFacelineColor(char_info.GetFacelineColor());
200 SetFacelineWrinkle(char_info.GetFacelineWrinkle());
201 SetFacelineMake(char_info.GetFacelineMake());
202 SetHairType(char_info.GetHairType());
203 SetHairColor(char_info.GetHairColor());
204 SetHairFlip(char_info.GetHairFlip());
205 SetEyeType(char_info.GetEyeType());
206 SetEyeColor(char_info.GetEyeColor());
207 SetEyeScale(char_info.GetEyeScale());
208 SetEyeAspect(char_info.GetEyeAspect());
209 SetEyeRotate(char_info.GetEyeRotate());
210 SetEyeX(char_info.GetEyeX());
211 SetEyeY(char_info.GetEyeY());
212 SetEyebrowType(char_info.GetEyebrowType());
213 SetEyebrowColor(char_info.GetEyebrowColor());
214 SetEyebrowScale(char_info.GetEyebrowScale());
215 SetEyebrowAspect(char_info.GetEyebrowAspect());
216 SetEyebrowRotate(char_info.GetEyebrowRotate());
217 SetEyebrowX(char_info.GetEyebrowX());
218 SetEyebrowY(char_info.GetEyebrowY() - 3);
219 SetNoseType(char_info.GetNoseType());
220 SetNoseScale(char_info.GetNoseScale());
221 SetNoseY(char_info.GetNoseY());
222 SetMouthType(char_info.GetMouthType());
223 SetMouthColor(char_info.GetMouthColor());
224 SetMouthScale(char_info.GetMouthScale());
225 SetMouthAspect(char_info.GetMouthAspect());
226 SetMouthY(char_info.GetMouthY());
227 SetBeardColor(char_info.GetBeardColor());
228 SetBeardType(char_info.GetBeardType());
229 SetMustacheType(char_info.GetMustacheType());
230 SetMustacheScale(char_info.GetMustacheScale());
231 SetMustacheY(char_info.GetMustacheY());
232 SetGlassType(char_info.GetGlassType());
233 SetGlassColor(char_info.GetGlassColor());
234 SetGlassScale(char_info.GetGlassScale());
235 SetGlassY(char_info.GetGlassY());
236 SetMoleType(char_info.GetMoleType());
237 SetMoleScale(char_info.GetMoleScale());
238 SetMoleX(char_info.GetMoleX());
239 SetMoleY(char_info.GetMoleY());
240}
241
242ValidationResult CoreData::IsValid() const {
243 if (!name.IsValid()) {
244 return ValidationResult::InvalidName;
245 }
246 if (GetFontRegion() > FontRegion::Max) {
247 return ValidationResult::InvalidFont;
248 }
249 if (GetFavoriteColor() > FavoriteColor::Max) {
250 return ValidationResult::InvalidColor;
251 }
252 if (GetGender() > Gender::Max) {
253 return ValidationResult::InvalidGender;
254 }
255 if (GetHeight() > MaxHeight) {
256 return ValidationResult::InvalidHeight;
257 }
258 if (GetBuild() > MaxBuild) {
259 return ValidationResult::InvalidBuild;
260 }
261 if (GetType() > MaxType) {
262 return ValidationResult::InvalidType;
263 }
264 if (GetRegionMove() > MaxRegionMove) {
265 return ValidationResult::InvalidRegionMove;
266 }
267 if (GetFacelineType() > FacelineType::Max) {
268 return ValidationResult::InvalidFacelineType;
269 }
270 if (GetFacelineColor() > FacelineColor::Max) {
271 return ValidationResult::InvalidFacelineColor;
272 }
273 if (GetFacelineWrinkle() > FacelineWrinkle::Max) {
274 return ValidationResult::InvalidFacelineWrinkle;
275 }
276 if (GetFacelineMake() > FacelineMake::Max) {
277 return ValidationResult::InvalidFacelineMake;
278 }
279 if (GetHairType() > HairType::Max) {
280 return ValidationResult::InvalidHairType;
281 }
282 if (GetHairColor() > CommonColor::Max) {
283 return ValidationResult::InvalidHairColor;
284 }
285 if (GetHairFlip() > HairFlip::Max) {
286 return ValidationResult::InvalidHairFlip;
287 }
288 if (GetEyeType() > EyeType::Max) {
289 return ValidationResult::InvalidEyeType;
290 }
291 if (GetEyeColor() > CommonColor::Max) {
292 return ValidationResult::InvalidEyeColor;
293 }
294 if (GetEyeScale() > MaxEyeScale) {
295 return ValidationResult::InvalidEyeScale;
296 }
297 if (GetEyeAspect() > MaxEyeAspect) {
298 return ValidationResult::InvalidEyeAspect;
299 }
300 if (GetEyeRotate() > MaxEyeRotate) {
301 return ValidationResult::InvalidEyeRotate;
302 }
303 if (GetEyeX() > MaxEyeX) {
304 return ValidationResult::InvalidEyeX;
305 }
306 if (GetEyeY() > MaxEyeY) {
307 return ValidationResult::InvalidEyeY;
308 }
309 if (GetEyebrowType() > EyebrowType::Max) {
310 return ValidationResult::InvalidEyebrowType;
311 }
312 if (GetEyebrowColor() > CommonColor::Max) {
313 return ValidationResult::InvalidEyebrowColor;
314 }
315 if (GetEyebrowScale() > MaxEyebrowScale) {
316 return ValidationResult::InvalidEyebrowScale;
317 }
318 if (GetEyebrowAspect() > MaxEyebrowAspect) {
319 return ValidationResult::InvalidEyebrowAspect;
320 }
321 if (GetEyebrowRotate() > MaxEyebrowRotate) {
322 return ValidationResult::InvalidEyebrowRotate;
323 }
324 if (GetEyebrowX() > MaxEyebrowX) {
325 return ValidationResult::InvalidEyebrowX;
326 }
327 if (GetEyebrowY() > MaxEyebrowY) {
328 return ValidationResult::InvalidEyebrowY;
329 }
330 if (GetNoseType() > NoseType::Max) {
331 return ValidationResult::InvalidNoseType;
332 }
333 if (GetNoseScale() > MaxNoseScale) {
334 return ValidationResult::InvalidNoseScale;
335 }
336 if (GetNoseY() > MaxNoseY) {
337 return ValidationResult::InvalidNoseY;
338 }
339 if (GetMouthType() > MouthType::Max) {
340 return ValidationResult::InvalidMouthType;
341 }
342 if (GetMouthColor() > CommonColor::Max) {
343 return ValidationResult::InvalidMouthColor;
344 }
345 if (GetMouthScale() > MaxMouthScale) {
346 return ValidationResult::InvalidMouthScale;
347 }
348 if (GetMouthAspect() > MaxMoutAspect) {
349 return ValidationResult::InvalidMouthAspect;
350 }
351 if (GetMouthY() > MaxMouthY) {
352 return ValidationResult::InvalidMouthY;
353 }
354 if (GetBeardColor() > CommonColor::Max) {
355 return ValidationResult::InvalidBeardColor;
356 }
357 if (GetBeardType() > BeardType::Max) {
358 return ValidationResult::InvalidBeardType;
359 }
360 if (GetMustacheType() > MustacheType::Max) {
361 return ValidationResult::InvalidMustacheType;
362 }
363 if (GetMustacheScale() > MaxMustacheScale) {
364 return ValidationResult::InvalidMustacheScale;
365 }
366 if (GetMustacheY() > MaxMustacheY) {
367 return ValidationResult::InvalidMustacheY;
368 }
369 if (GetGlassType() > GlassType::Max) {
370 return ValidationResult::InvalidGlassType;
371 }
372 if (GetGlassColor() > CommonColor::Max) {
373 return ValidationResult::InvalidGlassColor;
374 }
375 if (GetGlassScale() > MaxGlassScale) {
376 return ValidationResult::InvalidGlassScale;
377 }
378 if (GetGlassY() > MaxGlassY) {
379 return ValidationResult::InvalidGlassY;
380 }
381 if (GetMoleType() > MoleType::Max) {
382 return ValidationResult::InvalidMoleType;
383 }
384 if (GetMoleScale() > MaxMoleScale) {
385 return ValidationResult::InvalidMoleScale;
386 }
387 if (GetMoleX() > MaxMoleX) {
388 return ValidationResult::InvalidMoleX;
389 }
390 if (GetMoleY() > MaxMoleY) {
391 return ValidationResult::InvalidMoleY;
392 }
393 return ValidationResult::NoErrors;
191} 394}
192 395
193void CoreData::SetFontRegion(FontRegion value) { 396void CoreData::SetFontRegion(FontRegion value) {
@@ -314,8 +517,8 @@ void CoreData::SetNoseY(u8 value) {
314 data.nose_y.Assign(value); 517 data.nose_y.Assign(value);
315} 518}
316 519
317void CoreData::SetMouthType(u8 value) { 520void CoreData::SetMouthType(MouthType value) {
318 data.mouth_type.Assign(value); 521 data.mouth_type.Assign(static_cast<u32>(value));
319} 522}
320 523
321void CoreData::SetMouthColor(CommonColor value) { 524void CoreData::SetMouthColor(CommonColor value) {
diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h
index cebcd2ee4..8897e4f3b 100644
--- a/src/core/hle/service/mii/types/core_data.h
+++ b/src/core/hle/service/mii/types/core_data.h
@@ -6,6 +6,7 @@
6#include "core/hle/service/mii/mii_types.h" 6#include "core/hle/service/mii/mii_types.h"
7 7
8namespace Service::Mii { 8namespace Service::Mii {
9class CharInfo;
9 10
10struct StoreDataBitFields { 11struct StoreDataBitFields {
11 union { 12 union {
@@ -100,8 +101,9 @@ class CoreData {
100public: 101public:
101 void SetDefault(); 102 void SetDefault();
102 void BuildRandom(Age age, Gender gender, Race race); 103 void BuildRandom(Age age, Gender gender, Race race);
104 void BuildFromCharInfo(const CharInfo& char_info);
103 105
104 u32 IsValid() const; 106 ValidationResult IsValid() const;
105 107
106 void SetFontRegion(FontRegion value); 108 void SetFontRegion(FontRegion value);
107 void SetFavoriteColor(FavoriteColor value); 109 void SetFavoriteColor(FavoriteColor value);
@@ -134,7 +136,7 @@ public:
134 void SetNoseType(NoseType value); 136 void SetNoseType(NoseType value);
135 void SetNoseScale(u8 value); 137 void SetNoseScale(u8 value);
136 void SetNoseY(u8 value); 138 void SetNoseY(u8 value);
137 void SetMouthType(u8 value); 139 void SetMouthType(MouthType value);
138 void SetMouthColor(CommonColor value); 140 void SetMouthColor(CommonColor value);
139 void SetMouthScale(u8 value); 141 void SetMouthScale(u8 value);
140 void SetMouthAspect(u8 value); 142 void SetMouthAspect(u8 value);
@@ -212,5 +214,6 @@ private:
212 Nickname name{}; 214 Nickname name{};
213}; 215};
214static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size."); 216static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size.");
217static_assert(std::is_trivially_copyable_v<CoreData>, "CoreData type must be trivially copyable.");
215 218
216}; // namespace Service::Mii 219}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp
index 8fce636c7..127221fdb 100644
--- a/src/core/hle/service/mii/types/store_data.cpp
+++ b/src/core/hle/service/mii/types/store_data.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/mii/mii_result.h"
4#include "core/hle/service/mii/mii_util.h" 5#include "core/hle/service/mii/mii_util.h"
5#include "core/hle/service/mii/types/raw_data.h" 6#include "core/hle/service/mii/types/raw_data.h"
6#include "core/hle/service/mii/types/store_data.h" 7#include "core/hle/service/mii/types/store_data.h"
@@ -35,13 +36,13 @@ void StoreData::BuildDefault(u32 mii_index) {
35 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); 36 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
36 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); 37 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
37 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); 38 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
38 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); 39 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y - 3));
39 40
40 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); 41 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
41 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); 42 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
42 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); 43 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
43 44
44 core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); 45 core_data.SetMouthType(static_cast<MouthType>(default_mii.mouth_type));
45 core_data.SetMouthColor( 46 core_data.SetMouthColor(
46 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); 47 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
47 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); 48 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
@@ -75,10 +76,8 @@ void StoreData::BuildDefault(u32 mii_index) {
75 core_data.SetType(static_cast<u8>(default_mii.type)); 76 core_data.SetType(static_cast<u8>(default_mii.type));
76 core_data.SetNickname(default_mii.nickname); 77 core_data.SetNickname(default_mii.nickname);
77 78
78 const auto device_id = MiiUtil::GetDeviceId();
79 create_id = MiiUtil::MakeCreateId(); 79 create_id = MiiUtil::MakeCreateId();
80 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); 80 SetChecksum();
81 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
82} 81}
83 82
84void StoreData::BuildBase(Gender gender) { 83void StoreData::BuildBase(Gender gender) {
@@ -109,13 +108,13 @@ void StoreData::BuildBase(Gender gender) {
109 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect)); 108 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
110 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate)); 109 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
111 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x)); 110 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
112 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y)); 111 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y - 3));
113 112
114 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type)); 113 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
115 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale)); 114 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
116 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y)); 115 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
117 116
118 core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type)); 117 core_data.SetMouthType(static_cast<MouthType>(default_mii.mouth_type));
119 core_data.SetMouthColor( 118 core_data.SetMouthColor(
120 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color))); 119 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
121 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale)); 120 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
@@ -149,37 +148,51 @@ void StoreData::BuildBase(Gender gender) {
149 core_data.SetType(static_cast<u8>(default_mii.type)); 148 core_data.SetType(static_cast<u8>(default_mii.type));
150 core_data.SetNickname(default_mii.nickname); 149 core_data.SetNickname(default_mii.nickname);
151 150
152 const auto device_id = MiiUtil::GetDeviceId();
153 create_id = MiiUtil::MakeCreateId(); 151 create_id = MiiUtil::MakeCreateId();
154 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); 152 SetChecksum();
155 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
156} 153}
157 154
158void StoreData::BuildRandom(Age age, Gender gender, Race race) { 155void StoreData::BuildRandom(Age age, Gender gender, Race race) {
159 core_data.BuildRandom(age, gender, race); 156 core_data.BuildRandom(age, gender, race);
160 const auto device_id = MiiUtil::GetDeviceId();
161 create_id = MiiUtil::MakeCreateId(); 157 create_id = MiiUtil::MakeCreateId();
162 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); 158 SetChecksum();
163 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
164} 159}
165 160
166void StoreData::SetInvalidName() { 161void StoreData::BuildWithCharInfo(const CharInfo& char_info) {
167 const auto& invalid_name = core_data.GetInvalidNickname(); 162 core_data.BuildFromCharInfo(char_info);
163 create_id = MiiUtil::MakeCreateId();
164 SetChecksum();
165}
166
167void StoreData::BuildWithCoreData(const CoreData& in_core_data) {
168 core_data = in_core_data;
169 create_id = MiiUtil::MakeCreateId();
170 SetChecksum();
171}
172
173Result StoreData::Restore() {
174 // TODO: Implement this
175 return ResultNotUpdated;
176}
177
178ValidationResult StoreData::IsValid() const {
179 if (core_data.IsValid() != ValidationResult::NoErrors) {
180 return core_data.IsValid();
181 }
182 if (data_crc != MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData) + sizeof(Common::UUID))) {
183 return ValidationResult::InvalidChecksum;
184 }
168 const auto device_id = MiiUtil::GetDeviceId(); 185 const auto device_id = MiiUtil::GetDeviceId();
169 core_data.SetNickname(invalid_name); 186 if (device_crc != MiiUtil::CalculateDeviceCrc16(device_id, sizeof(StoreData))) {
170 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID)); 187 return ValidationResult::InvalidChecksum;
171 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData)); 188 }
189 return ValidationResult::NoErrors;
172} 190}
173 191
174bool StoreData::IsSpecial() const { 192bool StoreData::IsSpecial() const {
175 return GetType() == 1; 193 return GetType() == 1;
176} 194}
177 195
178u32 StoreData::IsValid() const {
179 // TODO: complete this
180 return 0;
181}
182
183void StoreData::SetFontRegion(FontRegion value) { 196void StoreData::SetFontRegion(FontRegion value) {
184 core_data.SetFontRegion(value); 197 core_data.SetFontRegion(value);
185} 198}
@@ -304,7 +317,7 @@ void StoreData::SetNoseY(u8 value) {
304 core_data.SetNoseY(value); 317 core_data.SetNoseY(value);
305} 318}
306 319
307void StoreData::SetMouthType(u8 value) { 320void StoreData::SetMouthType(MouthType value) {
308 core_data.SetMouthType(value); 321 core_data.SetMouthType(value);
309} 322}
310 323
@@ -380,6 +393,26 @@ void StoreData::SetNickname(Nickname value) {
380 core_data.SetNickname(value); 393 core_data.SetNickname(value);
381} 394}
382 395
396void StoreData::SetInvalidName() {
397 const auto& invalid_name = core_data.GetInvalidNickname();
398 core_data.SetNickname(invalid_name);
399 SetChecksum();
400}
401
402void StoreData::SetChecksum() {
403 SetDataChecksum();
404 SetDeviceChecksum();
405}
406
407void StoreData::SetDataChecksum() {
408 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData) + sizeof(Common::UUID));
409}
410
411void StoreData::SetDeviceChecksum() {
412 const auto device_id = MiiUtil::GetDeviceId();
413 device_crc = MiiUtil::CalculateDeviceCrc16(device_id, sizeof(StoreData));
414}
415
383Common::UUID StoreData::GetCreateId() const { 416Common::UUID StoreData::GetCreateId() const {
384 return create_id; 417 return create_id;
385} 418}
@@ -585,7 +618,7 @@ Nickname StoreData::GetNickname() const {
585} 618}
586 619
587bool StoreData::operator==(const StoreData& data) { 620bool StoreData::operator==(const StoreData& data) {
588 bool is_identical = data.core_data.IsValid() == 0; 621 bool is_identical = data.core_data.IsValid() == ValidationResult::NoErrors;
589 is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data; 622 is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data;
590 is_identical &= GetCreateId() == data.GetCreateId(); 623 is_identical &= GetCreateId() == data.GetCreateId();
591 is_identical &= GetFontRegion() == data.GetFontRegion(); 624 is_identical &= GetFontRegion() == data.GetFontRegion();
diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h
index 224c32cf8..ed5dfb949 100644
--- a/src/core/hle/service/mii/types/store_data.h
+++ b/src/core/hle/service/mii/types/store_data.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/result.h"
6#include "core/hle/service/mii/mii_types.h" 7#include "core/hle/service/mii/mii_types.h"
7#include "core/hle/service/mii/types/core_data.h" 8#include "core/hle/service/mii/types/core_data.h"
8 9
@@ -10,17 +11,16 @@ namespace Service::Mii {
10 11
11class StoreData { 12class StoreData {
12public: 13public:
13 // nn::mii::detail::StoreDataRaw::BuildDefault
14 void BuildDefault(u32 mii_index); 14 void BuildDefault(u32 mii_index);
15 // nn::mii::detail::StoreDataRaw::BuildDefault
16
17 void BuildBase(Gender gender); 15 void BuildBase(Gender gender);
18 // nn::mii::detail::StoreDataRaw::BuildRandom
19 void BuildRandom(Age age, Gender gender, Race race); 16 void BuildRandom(Age age, Gender gender, Race race);
17 void BuildWithCharInfo(const CharInfo& char_info);
18 void BuildWithCoreData(const CoreData& in_core_data);
19 Result Restore();
20 20
21 bool IsSpecial() const; 21 ValidationResult IsValid() const;
22 22
23 u32 IsValid() const; 23 bool IsSpecial() const;
24 24
25 void SetFontRegion(FontRegion value); 25 void SetFontRegion(FontRegion value);
26 void SetFavoriteColor(FavoriteColor value); 26 void SetFavoriteColor(FavoriteColor value);
@@ -53,7 +53,7 @@ public:
53 void SetNoseType(NoseType value); 53 void SetNoseType(NoseType value);
54 void SetNoseScale(u8 value); 54 void SetNoseScale(u8 value);
55 void SetNoseY(u8 value); 55 void SetNoseY(u8 value);
56 void SetMouthType(u8 value); 56 void SetMouthType(MouthType value);
57 void SetMouthColor(CommonColor value); 57 void SetMouthColor(CommonColor value);
58 void SetMouthScale(u8 value); 58 void SetMouthScale(u8 value);
59 void SetMouthAspect(u8 value); 59 void SetMouthAspect(u8 value);
@@ -73,6 +73,9 @@ public:
73 void SetMoleY(u8 value); 73 void SetMoleY(u8 value);
74 void SetNickname(Nickname nickname); 74 void SetNickname(Nickname nickname);
75 void SetInvalidName(); 75 void SetInvalidName();
76 void SetChecksum();
77 void SetDataChecksum();
78 void SetDeviceChecksum();
76 79
77 Common::UUID GetCreateId() const; 80 Common::UUID GetCreateId() const;
78 FontRegion GetFontRegion() const; 81 FontRegion GetFontRegion() const;
@@ -135,6 +138,8 @@ private:
135 u16 device_crc{}; 138 u16 device_crc{};
136}; 139};
137static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size."); 140static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size.");
141static_assert(std::is_trivially_copyable_v<StoreData>,
142 "StoreData type must be trivially copyable.");
138 143
139struct StoreDataElement { 144struct StoreDataElement {
140 StoreData store_data{}; 145 StoreData store_data{};
diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp
index 1c28e0b1b..a019cc9f7 100644
--- a/src/core/hle/service/mii/types/ver3_store_data.cpp
+++ b/src/core/hle/service/mii/types/ver3_store_data.cpp
@@ -22,12 +22,6 @@ void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) {
22void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const { 22void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
23 out_store_data.BuildBase(Gender::Male); 23 out_store_data.BuildBase(Gender::Male);
24 24
25 if (!IsValid()) {
26 return;
27 }
28
29 // TODO: We are ignoring a bunch of data from the mii_v3
30
31 out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value())); 25 out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value()));
32 out_store_data.SetFavoriteColor( 26 out_store_data.SetFavoriteColor(
33 static_cast<FavoriteColor>(mii_information.favorite_color.Value())); 27 static_cast<FavoriteColor>(mii_information.favorite_color.Value()));
@@ -36,65 +30,71 @@ void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
36 30
37 out_store_data.SetNickname(mii_name); 31 out_store_data.SetNickname(mii_name);
38 out_store_data.SetFontRegion( 32 out_store_data.SetFontRegion(
39 static_cast<FontRegion>(static_cast<u8>(region_information.font_region))); 33 static_cast<FontRegion>(static_cast<u8>(region_information.font_region.Value())));
40 34
41 out_store_data.SetFacelineType( 35 out_store_data.SetFacelineType(
42 static_cast<FacelineType>(appearance_bits1.faceline_type.Value())); 36 static_cast<FacelineType>(appearance_bits1.faceline_type.Value()));
43 out_store_data.SetFacelineColor( 37 out_store_data.SetFacelineColor(
44 static_cast<FacelineColor>(appearance_bits1.faceline_color.Value())); 38 RawData::GetFacelineColorFromVer3(appearance_bits1.faceline_color.Value()));
45 out_store_data.SetFacelineWrinkle( 39 out_store_data.SetFacelineWrinkle(
46 static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value())); 40 static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value()));
47 out_store_data.SetFacelineMake( 41 out_store_data.SetFacelineMake(
48 static_cast<FacelineMake>(appearance_bits2.faceline_make.Value())); 42 static_cast<FacelineMake>(appearance_bits2.faceline_make.Value()));
49 43
50 out_store_data.SetHairType(static_cast<HairType>(hair_type)); 44 out_store_data.SetHairType(static_cast<HairType>(hair_type));
51 out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value())); 45 out_store_data.SetHairColor(RawData::GetHairColorFromVer3(appearance_bits3.hair_color.Value()));
52 out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value())); 46 out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value()));
53 47
54 out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value())); 48 out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value()));
55 out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value())); 49 out_store_data.SetEyeColor(RawData::GetEyeColorFromVer3(appearance_bits4.eye_color.Value()));
56 out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale)); 50 out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale.Value()));
57 out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect)); 51 out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect.Value()));
58 out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate)); 52 out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate.Value()));
59 out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x)); 53 out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x.Value()));
60 out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y)); 54 out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y.Value()));
61 55
62 out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value())); 56 out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value()));
63 out_store_data.SetEyebrowColor( 57 out_store_data.SetEyebrowColor(
64 static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value())); 58 RawData::GetHairColorFromVer3(appearance_bits5.eyebrow_color.Value()));
65 out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale)); 59 out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale.Value()));
66 out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect)); 60 out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect.Value()));
67 out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate)); 61 out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate.Value()));
68 out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x)); 62 out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x.Value()));
69 out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y)); 63 out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y.Value() - 3));
70 64
71 out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value())); 65 out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value()));
72 out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale)); 66 out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale.Value()));
73 out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y)); 67 out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y.Value()));
74 68
75 out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type)); 69 out_store_data.SetMouthType(static_cast<MouthType>(appearance_bits7.mouth_type.Value()));
76 out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value())); 70 out_store_data.SetMouthColor(
77 out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale)); 71 RawData::GetMouthColorFromVer3(appearance_bits7.mouth_color.Value()));
78 out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect)); 72 out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale.Value()));
79 out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y)); 73 out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect.Value()));
74 out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y.Value()));
80 75
81 out_store_data.SetMustacheType( 76 out_store_data.SetMustacheType(
82 static_cast<MustacheType>(appearance_bits8.mustache_type.Value())); 77 static_cast<MustacheType>(appearance_bits8.mustache_type.Value()));
83 out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale)); 78 out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale.Value()));
84 out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y)); 79 out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y.Value()));
85 80
86 out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value())); 81 out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value()));
87 out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value())); 82 out_store_data.SetBeardColor(
83 RawData::GetHairColorFromVer3(appearance_bits9.beard_color.Value()));
88 84
85 // Glass type is compatible as it is. It doesn't need a table
89 out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value())); 86 out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value()));
90 out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value())); 87 out_store_data.SetGlassColor(
91 out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale)); 88 RawData::GetGlassColorFromVer3(appearance_bits10.glass_color.Value()));
92 out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y)); 89 out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale.Value()));
90 out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y.Value()));
93 91
94 out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value())); 92 out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value()));
95 out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale)); 93 out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale.Value()));
96 out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x)); 94 out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x.Value()));
97 out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y)); 95 out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y.Value()));
96
97 out_store_data.SetChecksum();
98} 98}
99 99
100void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) { 100void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
@@ -220,7 +220,7 @@ u32 Ver3StoreData::IsValid() const {
220 220
221 is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max)); 221 is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max));
222 is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale); 222 is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale);
223 is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY); 223 is_valid = is_valid && (appearance_bits9.mustache_y <= MaxMustacheY);
224 224
225 is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max)); 225 is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max));
226 is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor); 226 is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor);
@@ -228,7 +228,7 @@ u32 Ver3StoreData::IsValid() const {
228 is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType); 228 is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType);
229 is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2); 229 is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2);
230 is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale); 230 is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale);
231 is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale); 231 is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassY);
232 232
233 is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max)); 233 is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max));
234 is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale); 234 is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale);
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 674d2e4b2..05951d8cb 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -439,6 +439,7 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
439 439
440 device_state = DeviceState::TagMounted; 440 device_state = DeviceState::TagMounted;
441 mount_target = mount_target_; 441 mount_target = mount_target_;
442
442 return ResultSuccess; 443 return ResultSuccess;
443} 444}
444 445
@@ -716,12 +717,13 @@ Result NfcDevice::GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info
716 return ResultRegistrationIsNotInitialized; 717 return ResultRegistrationIsNotInitialized;
717 } 718 }
718 719
719 Service::Mii::MiiManager manager; 720 Mii::StoreData store_data{};
720 const auto& settings = tag_data.settings; 721 const auto& settings = tag_data.settings;
722 tag_data.owner_mii.BuildToStoreData(store_data);
721 723
722 // TODO: Validate and complete this data 724 // TODO: Validate and complete this data
723 register_info = { 725 register_info = {
724 .mii_store_data = {}, 726 .mii_store_data = store_data,
725 .creation_date = settings.init_date.GetWriteDate(), 727 .creation_date = settings.init_date.GetWriteDate(),
726 .amiibo_name = GetAmiiboName(settings), 728 .amiibo_name = GetAmiiboName(settings),
727 .font_region = settings.settings.font_region, 729 .font_region = settings.settings.font_region,
@@ -1372,7 +1374,7 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co
1372 1374
1373 // Convert from utf16 to utf8 1375 // Convert from utf16 to utf8
1374 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); 1376 const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
1375 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); 1377 memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size() - 1);
1376 1378
1377 return amiibo_name; 1379 return amiibo_name;
1378} 1380}
diff --git a/src/core/tools/renderdoc.cpp b/src/core/tools/renderdoc.cpp
index 44d24822a..947fa6cb3 100644
--- a/src/core/tools/renderdoc.cpp
+++ b/src/core/tools/renderdoc.cpp
@@ -7,7 +7,7 @@
7#include "common/dynamic_library.h" 7#include "common/dynamic_library.h"
8#include "core/tools/renderdoc.h" 8#include "core/tools/renderdoc.h"
9 9
10#ifdef WIN32 10#ifdef _WIN32
11#include <windows.h> 11#include <windows.h>
12#else 12#else
13#include <dlfcn.h> 13#include <dlfcn.h>
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 11ced6c38..56307d030 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -140,6 +140,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
140 return PixelFormat::D32_FLOAT; 140 return PixelFormat::D32_FLOAT;
141 case Hash(TextureFormat::Z16, UNORM): 141 case Hash(TextureFormat::Z16, UNORM):
142 return PixelFormat::D16_UNORM; 142 return PixelFormat::D16_UNORM;
143 case Hash(TextureFormat::Z16, UNORM, UINT, UINT, UINT, LINEAR):
144 return PixelFormat::D16_UNORM;
143 case Hash(TextureFormat::Z24S8, UINT, UNORM, UNORM, UNORM, LINEAR): 145 case Hash(TextureFormat::Z24S8, UINT, UNORM, UNORM, UNORM, LINEAR):
144 return PixelFormat::S8_UINT_D24_UNORM; 146 return PixelFormat::S8_UINT_D24_UNORM;
145 case Hash(TextureFormat::Z24S8, UINT, UNORM, UINT, UINT, LINEAR): 147 case Hash(TextureFormat::Z24S8, UINT, UNORM, UINT, UINT, LINEAR):