summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/templates/build-msvc.yml4
-rw-r--r--src/audio_core/audio_out.cpp4
-rw-r--r--src/audio_core/audio_out.h3
-rw-r--r--src/audio_core/audio_renderer.cpp110
-rw-r--r--src/audio_core/audio_renderer.h20
-rw-r--r--src/audio_core/behavior_info.h30
-rw-r--r--src/audio_core/command_generator.h14
-rw-r--r--src/audio_core/common.h2
-rw-r--r--src/audio_core/effect_context.h26
-rw-r--r--src/audio_core/mix_context.h26
-rw-r--r--src/audio_core/sink_context.cpp18
-rw-r--r--src/audio_core/sink_context.h17
-rw-r--r--src/audio_core/stream.cpp10
-rw-r--r--src/audio_core/stream.h21
-rw-r--r--src/common/logging/backend.cpp14
-rw-r--r--src/core/core.cpp60
-rw-r--r--src/core/core.h177
-rw-r--r--src/core/cpu_manager.cpp24
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp81
-rw-r--r--src/core/file_sys/patch_manager.h13
-rw-r--r--src/core/file_sys/romfs_factory.cpp6
-rw-r--r--src/core/file_sys/submission_package.cpp6
-rw-r--r--src/core/file_sys/submission_package.h4
-rw-r--r--src/core/frontend/input.h6
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/acc/acc.cpp6
-rw-r--r--src/core/hle/service/am/am.cpp65
-rw-r--r--src/core/hle/service/am/am.h12
-rw-r--r--src/core/hle/service/am/applet_ae.cpp21
-rw-r--r--src/core/hle/service/am/applet_ae.h4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp14
-rw-r--r--src/core/hle/service/am/applet_oe.h4
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp7
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h7
-rw-r--r--src/core/hle/service/ns/ns.cpp24
-rw-r--r--src/core/hle/service/ns/ns.h28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h36
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp157
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h110
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp75
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h109
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp192
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h55
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp184
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h153
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp88
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp43
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h110
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp42
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp70
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h24
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp119
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h68
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp199
-rw-r--r--src/core/hle/service/nvdrv/interface.h6
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h86
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp97
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h23
-rw-r--r--src/core/hle/service/service.cpp20
-rw-r--r--src/core/hle/service/service.h21
-rw-r--r--src/core/hle/service/vi/vi.cpp57
-rw-r--r--src/core/hle/service/vi/vi.h7
-rw-r--r--src/core/hle/service/vi/vi_m.cpp3
-rw-r--r--src/core/hle/service/vi/vi_m.h4
-rw-r--r--src/core/hle/service/vi/vi_s.cpp3
-rw-r--r--src/core/hle/service/vi/vi_s.h4
-rw-r--r--src/core/hle/service/vi/vi_u.cpp3
-rw-r--r--src/core/hle/service/vi/vi_u.h4
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp6
-rw-r--r--src/core/loader/loader.cpp29
-rw-r--r--src/core/loader/loader.h11
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/loader/nsp.cpp22
-rw-r--r--src/core/loader/nsp.h14
-rw-r--r--src/core/loader/xci.cpp19
-rw-r--r--src/core/loader/xci.h14
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp9
-rw-r--r--src/core/telemetry_session.h18
-rw-r--r--src/input_common/CMakeLists.txt4
-rwxr-xr-xsrc/input_common/analog_from_button.cpp122
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp4
-rw-r--r--src/input_common/sdl/sdl.h2
-rw-r--r--src/input_common/sdl/sdl_impl.cpp7
-rw-r--r--src/input_common/touch_from_button.cpp3
-rw-r--r--src/input_common/udp/client.cpp10
-rw-r--r--src/input_common/udp/protocol.h11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/shader/async_shaders.cpp32
-rw-r--r--src/video_core/shader/decode/image.cpp4
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui46
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp7
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp6
-rw-r--r--src/yuzu/game_list_worker.cpp36
-rw-r--r--src/yuzu/main.cpp52
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
-rw-r--r--src/yuzu_tester/yuzu.cpp4
110 files changed, 2223 insertions, 1455 deletions
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml
index d85a949aa..0b4bbd341 100644
--- a/.ci/templates/build-msvc.yml
+++ b/.ci/templates/build-msvc.yml
@@ -4,9 +4,11 @@ parameters:
4 version: '' 4 version: ''
5 5
6steps: 6steps:
7- script: choco install vulkan-sdk
8 displayName: 'Install vulkan-sdk'
7- script: python -m pip install --upgrade pip conan 9- script: python -m pip install --upgrade pip conan
8 displayName: 'Install conan' 10 displayName: 'Install conan'
9- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. 11- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
10 displayName: 'Configure CMake' 12 displayName: 'Configure CMake'
11- task: MSBuild@1 13- task: MSBuild@1
12 displayName: 'Build' 14 displayName: 'Build'
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 8619a3f03..fe3a898ad 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
43 return stream->GetTagsAndReleaseBuffers(max_count); 43 return stream->GetTagsAndReleaseBuffers(max_count);
44} 44}
45 45
46std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
47 return stream->GetTagsAndReleaseBuffers();
48}
49
46void AudioOut::StartStream(StreamPtr stream) { 50void AudioOut::StartStream(StreamPtr stream) {
47 stream->Play(); 51 stream->Play();
48} 52}
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index b07588287..6ce08cd0d 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -31,6 +31,9 @@ public:
31 /// Returns a vector of recently released buffers specified by tag for the specified stream 31 /// Returns a vector of recently released buffers specified by tag for the specified stream
32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); 32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
33 33
34 /// Returns a vector of all recently released buffers specified by tag for the specified stream
35 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
36
34 /// Starts an audio stream for playback 37 /// Starts an audio stream for playback
35 void StartStream(StreamPtr stream); 38 void StartStream(StreamPtr stream);
36 39
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index a7e851bb8..e1ded84e0 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <limits>
5#include <vector> 6#include <vector>
6 7
7#include "audio_core/audio_out.h" 8#include "audio_core/audio_out.h"
@@ -14,6 +15,59 @@
14#include "core/memory.h" 15#include "core/memory.h"
15#include "core/settings.h" 16#include "core/settings.h"
16 17
18namespace {
19[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
20 return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
21 s32{std::numeric_limits<s16>::max()}));
22}
23
24[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
25 // Mix 50% from left and 50% from right channel
26 constexpr float l_mix_amount = 50.0f / 100.0f;
27 constexpr float r_mix_amount = 50.0f / 100.0f;
28 return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
29 (static_cast<float>(r_channel) * r_mix_amount)));
30}
31
32[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
33 s16 fc_channel,
34 [[maybe_unused]] s16 lf_channel,
35 s16 bl_channel, s16 br_channel) {
36 // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
37 // are mixed to be 36.94%
38
39 constexpr float front_mix_amount = 36.94f / 100.0f;
40 constexpr float center_mix_amount = 26.12f / 100.0f;
41 constexpr float back_mix_amount = 36.94f / 100.0f;
42
43 // Mix 50% from left and 50% from right channel
44 const auto left = front_mix_amount * static_cast<float>(fl_channel) +
45 center_mix_amount * static_cast<float>(fc_channel) +
46 back_mix_amount * static_cast<float>(bl_channel);
47
48 const auto right = front_mix_amount * static_cast<float>(fr_channel) +
49 center_mix_amount * static_cast<float>(fc_channel) +
50 back_mix_amount * static_cast<float>(br_channel);
51
52 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
53}
54
55[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
56 s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
57 const std::array<float_le, 4>& coeff) {
58 const auto left =
59 static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
60 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
61
62 const auto right =
63 static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
64 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
65
66 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
67}
68
69} // namespace
70
17namespace AudioCore { 71namespace AudioCore {
18AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 72AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
19 AudioCommon::AudioRendererParameter params, 73 AudioCommon::AudioRendererParameter params,
@@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
62 return stream->GetState(); 116 return stream->GetState();
63} 117}
64 118
65static constexpr s16 ClampToS16(s32 value) {
66 return static_cast<s16>(std::clamp(value, -32768, 32767));
67}
68
69ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, 119ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
70 std::vector<u8>& output_params) { 120 std::vector<u8>& output_params) {
71 121
@@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
104 } 154 }
105 } 155 }
106 156
107 auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, 157 const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
108 splitter_context, effect_context); 158 splitter_context, effect_context);
109 159
110 if (mix_result.IsError()) { 160 if (mix_result.IsError()) {
111 LOG_ERROR(Audio, "Failed to update mix parameters"); 161 LOG_ERROR(Audio, "Failed to update mix parameters");
@@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
194 for (std::size_t i = 0; i < BUFFER_SIZE; i++) { 244 for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
195 if (channel_count == 1) { 245 if (channel_count == 1) {
196 const auto sample = ClampToS16(mix_buffers[0][i]); 246 const auto sample = ClampToS16(mix_buffers[0][i]);
197 buffer[i * stream_channel_count + 0] = sample; 247
198 if (stream_channel_count > 1) { 248 // Place sample in all channels
199 buffer[i * stream_channel_count + 1] = sample; 249 for (u32 channel = 0; channel < stream_channel_count; channel++) {
250 buffer[i * stream_channel_count + channel] = sample;
200 } 251 }
252
201 if (stream_channel_count == 6) { 253 if (stream_channel_count == 6) {
202 buffer[i * stream_channel_count + 2] = sample; 254 // Output stream has a LF channel, mute it!
203 buffer[i * stream_channel_count + 4] = sample; 255 buffer[i * stream_channel_count + 3] = 0;
204 buffer[i * stream_channel_count + 5] = sample;
205 } 256 }
257
206 } else if (channel_count == 2) { 258 } else if (channel_count == 2) {
207 const auto l_sample = ClampToS16(mix_buffers[0][i]); 259 const auto l_sample = ClampToS16(mix_buffers[0][i]);
208 const auto r_sample = ClampToS16(mix_buffers[1][i]); 260 const auto r_sample = ClampToS16(mix_buffers[1][i]);
209 if (stream_channel_count == 1) { 261 if (stream_channel_count == 1) {
210 buffer[i * stream_channel_count + 0] = l_sample; 262 buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
211 } else if (stream_channel_count == 2) { 263 } else if (stream_channel_count == 2) {
212 buffer[i * stream_channel_count + 0] = l_sample; 264 buffer[i * stream_channel_count + 0] = l_sample;
213 buffer[i * stream_channel_count + 1] = r_sample; 265 buffer[i * stream_channel_count + 1] = r_sample;
@@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
215 buffer[i * stream_channel_count + 0] = l_sample; 267 buffer[i * stream_channel_count + 0] = l_sample;
216 buffer[i * stream_channel_count + 1] = r_sample; 268 buffer[i * stream_channel_count + 1] = r_sample;
217 269
218 buffer[i * stream_channel_count + 2] = 270 // Combine both left and right channels to the center channel
219 ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); 271 buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
220 272
221 buffer[i * stream_channel_count + 4] = l_sample; 273 buffer[i * stream_channel_count + 4] = l_sample;
222 buffer[i * stream_channel_count + 5] = r_sample; 274 buffer[i * stream_channel_count + 5] = r_sample;
@@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
231 const auto br_sample = ClampToS16(mix_buffers[5][i]); 283 const auto br_sample = ClampToS16(mix_buffers[5][i]);
232 284
233 if (stream_channel_count == 1) { 285 if (stream_channel_count == 1) {
234 buffer[i * stream_channel_count + 0] = fc_sample; 286 // Games seem to ignore the center channel half the time, we use the front left
287 // and right channel for mixing as that's where majority of the audio goes
288 buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
235 } else if (stream_channel_count == 2) { 289 } else if (stream_channel_count == 2) {
236 buffer[i * stream_channel_count + 0] = 290 // Mix all channels into 2 channels
237 static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + 291 if (sink_context.HasDownMixingCoefficients()) {
238 0.2612f * static_cast<float>(fc_sample) + 292 const auto [left, right] = Mix6To2WithCoefficients(
239 0.3694f * static_cast<float>(bl_sample)); 293 fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
240 buffer[i * stream_channel_count + 1] = 294 sink_context.GetDownmixCoefficients());
241 static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + 295 buffer[i * stream_channel_count + 0] = left;
242 0.2612f * static_cast<float>(fc_sample) + 296 buffer[i * stream_channel_count + 1] = right;
243 0.3694f * static_cast<float>(br_sample)); 297 } else {
298 const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
299 lf_sample, bl_sample, br_sample);
300 buffer[i * stream_channel_count + 0] = left;
301 buffer[i * stream_channel_count + 1] = right;
302 }
244 } else if (stream_channel_count == 6) { 303 } else if (stream_channel_count == 6) {
304 // Pass through
245 buffer[i * stream_channel_count + 0] = fl_sample; 305 buffer[i * stream_channel_count + 0] = fl_sample;
246 buffer[i * stream_channel_count + 1] = fr_sample; 306 buffer[i * stream_channel_count + 1] = fr_sample;
247 buffer[i * stream_channel_count + 2] = fc_sample; 307 buffer[i * stream_channel_count + 2] = fc_sample;
@@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
259} 319}
260 320
261void AudioRenderer::ReleaseAndQueueBuffers() { 321void AudioRenderer::ReleaseAndQueueBuffers() {
262 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)}; 322 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
263 for (const auto& tag : released_buffers) { 323 for (const auto& tag : released_buffers) {
264 QueueMixedBuffer(tag); 324 QueueMixedBuffer(tag);
265 } 325 }
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 2fd93e058..a85219045 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -36,16 +36,10 @@ class Memory;
36} 36}
37 37
38namespace AudioCore { 38namespace AudioCore {
39using DSPStateHolder = std::array<VoiceState*, 6>; 39using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
40 40
41class AudioOut; 41class AudioOut;
42 42
43struct RendererInfo {
44 u64_le elasped_frame_count{};
45 INSERT_PADDING_WORDS(2);
46};
47static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
48
49class AudioRenderer { 43class AudioRenderer {
50public: 44public:
51 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 45 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
@@ -53,14 +47,14 @@ public:
53 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 47 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
54 ~AudioRenderer(); 48 ~AudioRenderer();
55 49
56 ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, 50 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
57 std::vector<u8>& output_params); 51 std::vector<u8>& output_params);
58 void QueueMixedBuffer(Buffer::Tag tag); 52 void QueueMixedBuffer(Buffer::Tag tag);
59 void ReleaseAndQueueBuffers(); 53 void ReleaseAndQueueBuffers();
60 u32 GetSampleRate() const; 54 [[nodiscard]] u32 GetSampleRate() const;
61 u32 GetSampleCount() const; 55 [[nodiscard]] u32 GetSampleCount() const;
62 u32 GetMixBufferCount() const; 56 [[nodiscard]] u32 GetMixBufferCount() const;
63 Stream::State GetStreamState() const; 57 [[nodiscard]] Stream::State GetStreamState() const;
64 58
65private: 59private:
66 BehaviorInfo behavior_info{}; 60 BehaviorInfo behavior_info{};
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
index 512a4ebe3..5a96bf75e 100644
--- a/src/audio_core/behavior_info.h
+++ b/src/audio_core/behavior_info.h
@@ -43,22 +43,22 @@ public:
43 void ClearError(); 43 void ClearError();
44 void UpdateFlags(u64_le dest_flags); 44 void UpdateFlags(u64_le dest_flags);
45 void SetUserRevision(u32_le revision); 45 void SetUserRevision(u32_le revision);
46 u32_le GetUserRevision() const; 46 [[nodiscard]] u32_le GetUserRevision() const;
47 u32_le GetProcessRevision() const; 47 [[nodiscard]] u32_le GetProcessRevision() const;
48 48
49 bool IsAdpcmLoopContextBugFixed() const; 49 [[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
50 bool IsSplitterSupported() const; 50 [[nodiscard]] bool IsSplitterSupported() const;
51 bool IsLongSizePreDelaySupported() const; 51 [[nodiscard]] bool IsLongSizePreDelaySupported() const;
52 bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; 52 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
53 bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; 53 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
54 bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; 54 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
55 bool IsElapsedFrameCountSupported() const; 55 [[nodiscard]] bool IsElapsedFrameCountSupported() const;
56 bool IsMemoryPoolForceMappingEnabled() const; 56 [[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
57 bool IsFlushVoiceWaveBuffersSupported() const; 57 [[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
58 bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const; 58 [[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
59 bool IsVoicePitchAndSrcSkippedSupported() const; 59 [[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
60 bool IsMixInParameterDirtyOnlyUpdateSupported() const; 60 [[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
61 bool IsSplitterBugFixed() const; 61 [[nodiscard]] bool IsSplitterBugFixed() const;
62 void CopyErrorInfo(OutParams& dst); 62 void CopyErrorInfo(OutParams& dst);
63 63
64private: 64private:
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
index 53e57748b..87ece00c4 100644
--- a/src/audio_core/command_generator.h
+++ b/src/audio_core/command_generator.h
@@ -39,13 +39,13 @@ public:
39 void PreCommand(); 39 void PreCommand();
40 void PostCommand(); 40 void PostCommand();
41 41
42 s32* GetChannelMixBuffer(s32 channel); 42 [[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
43 const s32* GetChannelMixBuffer(s32 channel) const; 43 [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
44 s32* GetMixBuffer(std::size_t index); 44 [[nodiscard]] s32* GetMixBuffer(std::size_t index);
45 const s32* GetMixBuffer(std::size_t index) const; 45 [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
46 std::size_t GetMixChannelBufferOffset(s32 channel) const; 46 [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
47 47
48 std::size_t GetTotalMixBufferCount() const; 48 [[nodiscard]] std::size_t GetTotalMixBufferCount() const;
49 49
50private: 50private:
51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel); 51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
@@ -73,7 +73,7 @@ private:
73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
76 ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); 76 [[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
77 77
78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, 78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
79 u32 sample_count, u32 write_offset, u32 write_count); 79 u32 sample_count, u32 write_offset, u32 write_count);
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 7b4a1e9e8..ec59a3ba9 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -22,7 +22,7 @@ constexpr std::size_t MAX_CHANNEL_COUNT = 6;
22constexpr std::size_t MAX_WAVE_BUFFERS = 4; 22constexpr std::size_t MAX_WAVE_BUFFERS = 4;
23constexpr std::size_t MAX_SAMPLE_HISTORY = 4; 23constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
24constexpr u32 STREAM_SAMPLE_RATE = 48000; 24constexpr u32 STREAM_SAMPLE_RATE = 48000;
25constexpr u32 STREAM_NUM_CHANNELS = 6; 25constexpr u32 STREAM_NUM_CHANNELS = 2;
26constexpr s32 NO_SPLITTER = -1; 26constexpr s32 NO_SPLITTER = -1;
27constexpr s32 NO_MIX = 0x7fffffff; 27constexpr s32 NO_MIX = 0x7fffffff;
28constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); 28constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
index 2c4ce53ef..03c5a0f04 100644
--- a/src/audio_core/effect_context.h
+++ b/src/audio_core/effect_context.h
@@ -189,11 +189,11 @@ public:
189 189
190 virtual void Update(EffectInfo::InParams& in_params) = 0; 190 virtual void Update(EffectInfo::InParams& in_params) = 0;
191 virtual void UpdateForCommandGeneration() = 0; 191 virtual void UpdateForCommandGeneration() = 0;
192 UsageState GetUsage() const; 192 [[nodiscard]] UsageState GetUsage() const;
193 EffectType GetType() const; 193 [[nodiscard]] EffectType GetType() const;
194 bool IsEnabled() const; 194 [[nodiscard]] bool IsEnabled() const;
195 s32 GetMixID() const; 195 [[nodiscard]] s32 GetMixID() const;
196 s32 GetProcessingOrder() const; 196 [[nodiscard]] s32 GetProcessingOrder() const;
197 197
198protected: 198protected:
199 UsageState usage{UsageState::Invalid}; 199 UsageState usage{UsageState::Invalid};
@@ -257,10 +257,10 @@ public:
257 257
258 void Update(EffectInfo::InParams& in_params) override; 258 void Update(EffectInfo::InParams& in_params) override;
259 void UpdateForCommandGeneration() override; 259 void UpdateForCommandGeneration() override;
260 VAddr GetSendInfo() const; 260 [[nodiscard]] VAddr GetSendInfo() const;
261 VAddr GetSendBuffer() const; 261 [[nodiscard]] VAddr GetSendBuffer() const;
262 VAddr GetRecvInfo() const; 262 [[nodiscard]] VAddr GetRecvInfo() const;
263 VAddr GetRecvBuffer() const; 263 [[nodiscard]] VAddr GetRecvBuffer() const;
264 264
265private: 265private:
266 VAddr send_info{}; 266 VAddr send_info{};
@@ -309,10 +309,10 @@ public:
309 explicit EffectContext(std::size_t effect_count); 309 explicit EffectContext(std::size_t effect_count);
310 ~EffectContext(); 310 ~EffectContext();
311 311
312 std::size_t GetCount() const; 312 [[nodiscard]] std::size_t GetCount() const;
313 EffectBase* GetInfo(std::size_t i); 313 [[nodiscard]] EffectBase* GetInfo(std::size_t i);
314 EffectBase* RetargetEffect(std::size_t i, EffectType effect); 314 [[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
315 const EffectBase* GetInfo(std::size_t i) const; 315 [[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
316 316
317private: 317private:
318 std::size_t effect_count{}; 318 std::size_t effect_count{};
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h
index 6a588eeb4..68bc673c6 100644
--- a/src/audio_core/mix_context.h
+++ b/src/audio_core/mix_context.h
@@ -62,17 +62,17 @@ public:
62 ServerMixInfo(); 62 ServerMixInfo();
63 ~ServerMixInfo(); 63 ~ServerMixInfo();
64 64
65 const ServerMixInfo::InParams& GetInParams() const; 65 [[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
66 ServerMixInfo::InParams& GetInParams(); 66 [[nodiscard]] ServerMixInfo::InParams& GetInParams();
67 67
68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, 68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
69 BehaviorInfo& behavior_info, SplitterContext& splitter_context, 69 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
70 EffectContext& effect_context); 70 EffectContext& effect_context);
71 bool HasAnyConnection() const; 71 [[nodiscard]] bool HasAnyConnection() const;
72 void Cleanup(); 72 void Cleanup();
73 void SetEffectCount(std::size_t count); 73 void SetEffectCount(std::size_t count);
74 void ResetEffectProcessingOrder(); 74 void ResetEffectProcessingOrder();
75 s32 GetEffectOrder(std::size_t i) const; 75 [[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
76 76
77private: 77private:
78 std::vector<s32> effect_processing_order; 78 std::vector<s32> effect_processing_order;
@@ -91,15 +91,15 @@ public:
91 void SortInfo(); 91 void SortInfo();
92 bool TsortInfo(SplitterContext& splitter_context); 92 bool TsortInfo(SplitterContext& splitter_context);
93 93
94 std::size_t GetCount() const; 94 [[nodiscard]] std::size_t GetCount() const;
95 ServerMixInfo& GetInfo(std::size_t i); 95 [[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
96 const ServerMixInfo& GetInfo(std::size_t i) const; 96 [[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
97 ServerMixInfo& GetSortedInfo(std::size_t i); 97 [[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
98 const ServerMixInfo& GetSortedInfo(std::size_t i) const; 98 [[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
99 ServerMixInfo& GetFinalMixInfo(); 99 [[nodiscard]] ServerMixInfo& GetFinalMixInfo();
100 const ServerMixInfo& GetFinalMixInfo() const; 100 [[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
101 EdgeMatrix& GetEdgeMatrix(); 101 [[nodiscard]] EdgeMatrix& GetEdgeMatrix();
102 const EdgeMatrix& GetEdgeMatrix() const; 102 [[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
103 103
104private: 104private:
105 void CalcMixBufferOffset(); 105 void CalcMixBufferOffset();
diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp
index 0882b411a..b29b47890 100644
--- a/src/audio_core/sink_context.cpp
+++ b/src/audio_core/sink_context.cpp
@@ -12,10 +12,16 @@ std::size_t SinkContext::GetCount() const {
12 return sink_count; 12 return sink_count;
13} 13}
14 14
15void SinkContext::UpdateMainSink(SinkInfo::InParams& in) { 15void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
16 ASSERT(in.type == SinkTypes::Device);
17
18 has_downmix_coefs = in.device.down_matrix_enabled;
19 if (has_downmix_coefs) {
20 downmix_coefficients = in.device.down_matrix_coef;
21 }
16 in_use = in.in_use; 22 in_use = in.in_use;
17 use_count = in.device.input_count; 23 use_count = in.device.input_count;
18 std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT); 24 buffers = in.device.input;
19} 25}
20 26
21bool SinkContext::InUse() const { 27bool SinkContext::InUse() const {
@@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
28 return buffer_ret; 34 return buffer_ret;
29} 35}
30 36
37bool SinkContext::HasDownMixingCoefficients() const {
38 return has_downmix_coefs;
39}
40
41const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
42 return downmix_coefficients;
43}
44
31} // namespace AudioCore 45} // namespace AudioCore
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
index d7aa72ba7..e2e7880b7 100644
--- a/src/audio_core/sink_context.h
+++ b/src/audio_core/sink_context.h
@@ -11,6 +11,8 @@
11 11
12namespace AudioCore { 12namespace AudioCore {
13 13
14using DownmixCoefficients = std::array<float_le, 4>;
15
14enum class SinkTypes : u8 { 16enum class SinkTypes : u8 {
15 Invalid = 0, 17 Invalid = 0,
16 Device = 1, 18 Device = 1,
@@ -50,7 +52,7 @@ public:
50 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; 52 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
51 INSERT_UNION_PADDING_BYTES(1); 53 INSERT_UNION_PADDING_BYTES(1);
52 bool down_matrix_enabled; 54 bool down_matrix_enabled;
53 std::array<float_le, 4> down_matrix_coef; 55 DownmixCoefficients down_matrix_coef;
54 }; 56 };
55 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size"); 57 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
56 58
@@ -74,16 +76,21 @@ public:
74 explicit SinkContext(std::size_t sink_count); 76 explicit SinkContext(std::size_t sink_count);
75 ~SinkContext(); 77 ~SinkContext();
76 78
77 std::size_t GetCount() const; 79 [[nodiscard]] std::size_t GetCount() const;
80
81 void UpdateMainSink(const SinkInfo::InParams& in);
82 [[nodiscard]] bool InUse() const;
83 [[nodiscard]] std::vector<u8> OutputBuffers() const;
78 84
79 void UpdateMainSink(SinkInfo::InParams& in); 85 [[nodiscard]] bool HasDownMixingCoefficients() const;
80 bool InUse() const; 86 [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
81 std::vector<u8> OutputBuffers() const;
82 87
83private: 88private:
84 bool in_use{false}; 89 bool in_use{false};
85 s32 use_count{}; 90 s32 use_count{};
86 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; 91 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
87 std::size_t sink_count{}; 92 std::size_t sink_count{};
93 bool has_downmix_coefs{false};
94 DownmixCoefficients downmix_coefficients{};
88}; 95};
89} // namespace AudioCore 96} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 4bbb1e0c4..41bc2f4d6 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -136,4 +136,14 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count)
136 return tags; 136 return tags;
137} 137}
138 138
139std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
140 std::vector<Buffer::Tag> tags;
141 tags.reserve(released_buffers.size());
142 while (!released_buffers.empty()) {
143 tags.push_back(released_buffers.front()->GetTag());
144 released_buffers.pop();
145 }
146 return tags;
147}
148
139} // namespace AudioCore 149} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 6437b8591..71c2d0b4f 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -57,37 +57,40 @@ public:
57 bool QueueBuffer(BufferPtr&& buffer); 57 bool QueueBuffer(BufferPtr&& buffer);
58 58
59 /// Returns true if the audio stream contains a buffer with the specified tag 59 /// Returns true if the audio stream contains a buffer with the specified tag
60 bool ContainsBuffer(Buffer::Tag tag) const; 60 [[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
61 61
62 /// Returns a vector of recently released buffers specified by tag 62 /// Returns a vector of recently released buffers specified by tag
63 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count); 63 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
64
65 /// Returns a vector of all recently released buffers specified by tag
66 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
64 67
65 void SetVolume(float volume); 68 void SetVolume(float volume);
66 69
67 float GetVolume() const { 70 [[nodiscard]] float GetVolume() const {
68 return game_volume; 71 return game_volume;
69 } 72 }
70 73
71 /// Returns true if the stream is currently playing 74 /// Returns true if the stream is currently playing
72 bool IsPlaying() const { 75 [[nodiscard]] bool IsPlaying() const {
73 return state == State::Playing; 76 return state == State::Playing;
74 } 77 }
75 78
76 /// Returns the number of queued buffers 79 /// Returns the number of queued buffers
77 std::size_t GetQueueSize() const { 80 [[nodiscard]] std::size_t GetQueueSize() const {
78 return queued_buffers.size(); 81 return queued_buffers.size();
79 } 82 }
80 83
81 /// Gets the sample rate 84 /// Gets the sample rate
82 u32 GetSampleRate() const { 85 [[nodiscard]] u32 GetSampleRate() const {
83 return sample_rate; 86 return sample_rate;
84 } 87 }
85 88
86 /// Gets the number of channels 89 /// Gets the number of channels
87 u32 GetNumChannels() const; 90 [[nodiscard]] u32 GetNumChannels() const;
88 91
89 /// Get the state 92 /// Get the state
90 State GetState() const; 93 [[nodiscard]] State GetState() const;
91 94
92private: 95private:
93 /// Plays the next queued buffer in the audio stream, starting playback if necessary 96 /// Plays the next queued buffer in the audio stream, starting playback if necessary
@@ -97,7 +100,7 @@ private:
97 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {}); 100 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
98 101
99 /// Gets the number of core cycles when the specified buffer will be released 102 /// Gets the number of core cycles when the specified buffer will be released
100 std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const; 103 [[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
101 104
102 u32 sample_rate; ///< Sample rate of the stream 105 u32 sample_rate; ///< Sample rate of the stream
103 Format format; ///< Format of the stream 106 Format format; ///< Format of the stream
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 7859344b9..631f64d05 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -23,6 +23,7 @@
23#include "common/logging/text_formatter.h" 23#include "common/logging/text_formatter.h"
24#include "common/string_util.h" 24#include "common/string_util.h"
25#include "common/threadsafe_queue.h" 25#include "common/threadsafe_queue.h"
26#include "core/settings.h"
26 27
27namespace Log { 28namespace Log {
28 29
@@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
152void FileBackend::Write(const Entry& entry) { 153void FileBackend::Write(const Entry& entry) {
153 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 154 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
154 // know) 155 // know)
155 constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; 156 constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
156 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 157 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
158
159 if (!file.IsOpen()) {
160 return;
161 }
162
163 if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
164 return;
165 } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
157 return; 166 return;
158 } 167 }
168
159 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); 169 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
160 if (entry.log_level >= Level::Error) { 170 if (entry.log_level >= Level::Error) {
161 file.Flush(); 171 file.Flush();
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 242796008..7ca3652af 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -145,7 +145,7 @@ struct System::Impl {
145 } 145 }
146 146
147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
148 LOG_DEBUG(HW_Memory, "initialized OK"); 148 LOG_DEBUG(Core, "initialized OK");
149 149
150 device_memory = std::make_unique<Core::DeviceMemory>(); 150 device_memory = std::make_unique<Core::DeviceMemory>();
151 151
@@ -187,7 +187,7 @@ struct System::Impl {
187 187
188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 188 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
189 189
190 Service::Init(service_manager, system); 190 services = std::make_unique<Service::Services>(service_manager, system);
191 GDBStub::DeferStart(); 191 GDBStub::DeferStart();
192 192
193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); 193 interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
@@ -208,9 +208,11 @@ struct System::Impl {
208 return ResultStatus::Success; 208 return ResultStatus::Success;
209 } 209 }
210 210
211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
212 const std::string& filepath) { 212 std::size_t program_index) {
213 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
214 program_index);
215
214 if (!app_loader) { 216 if (!app_loader) {
215 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 217 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
216 return ResultStatus::ErrorGetLoader; 218 return ResultStatus::ErrorGetLoader;
@@ -224,7 +226,7 @@ struct System::Impl {
224 return init_result; 226 return init_result;
225 } 227 }
226 228
227 telemetry_session->AddInitialInfo(*app_loader); 229 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
228 auto main_process = 230 auto main_process =
229 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); 231 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
230 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); 232 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
@@ -296,7 +298,7 @@ struct System::Impl {
296 298
297 // Shutdown emulation session 299 // Shutdown emulation session
298 GDBStub::Shutdown(); 300 GDBStub::Shutdown();
299 Service::Shutdown(); 301 services.reset();
300 service_manager.reset(); 302 service_manager.reset();
301 cheat_engine.reset(); 303 cheat_engine.reset();
302 telemetry_session.reset(); 304 telemetry_session.reset();
@@ -306,8 +308,8 @@ struct System::Impl {
306 cpu_manager.Shutdown(); 308 cpu_manager.Shutdown();
307 309
308 // Shutdown kernel and core timing 310 // Shutdown kernel and core timing
309 kernel.Shutdown();
310 core_timing.Shutdown(); 311 core_timing.Shutdown();
312 kernel.Shutdown();
311 313
312 // Close app loader 314 // Close app loader
313 app_loader.reset(); 315 app_loader.reset();
@@ -338,7 +340,7 @@ struct System::Impl {
338 Service::Glue::ApplicationLaunchProperty launch{}; 340 Service::Glue::ApplicationLaunchProperty launch{};
339 launch.title_id = process.GetTitleID(); 341 launch.title_id = process.GetTitleID();
340 342
341 FileSys::PatchManager pm{launch.title_id}; 343 FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider};
342 launch.version = pm.GetGameVersion().value_or(0); 344 launch.version = pm.GetGameVersion().value_or(0);
343 345
344 // TODO(DarkLordZach): When FSController/Game Card Support is added, if 346 // TODO(DarkLordZach): When FSController/Game Card Support is added, if
@@ -398,6 +400,9 @@ struct System::Impl {
398 /// Service manager 400 /// Service manager
399 std::shared_ptr<Service::SM::ServiceManager> service_manager; 401 std::shared_ptr<Service::SM::ServiceManager> service_manager;
400 402
403 /// Services
404 std::unique_ptr<Service::Services> services;
405
401 /// Telemetry session for this emulation session 406 /// Telemetry session for this emulation session
402 std::unique_ptr<Core::TelemetrySession> telemetry_session; 407 std::unique_ptr<Core::TelemetrySession> telemetry_session;
403 408
@@ -413,6 +418,8 @@ struct System::Impl {
413 bool is_multicore{}; 418 bool is_multicore{};
414 bool is_async_gpu{}; 419 bool is_async_gpu{};
415 420
421 ExecuteProgramCallback execute_program_callback;
422
416 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 423 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
417 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; 424 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
418}; 425};
@@ -444,8 +451,13 @@ void System::InvalidateCpuInstructionCaches() {
444 impl->kernel.InvalidateAllInstructionCaches(); 451 impl->kernel.InvalidateAllInstructionCaches();
445} 452}
446 453
447System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 454void System::Shutdown() {
448 return impl->Load(*this, emu_window, filepath); 455 impl->Shutdown();
456}
457
458System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
459 std::size_t program_index) {
460 return impl->Load(*this, emu_window, filepath, program_index);
449} 461}
450 462
451bool System::IsPoweredOn() const { 463bool System::IsPoweredOn() const {
@@ -632,7 +644,11 @@ const std::string& System::GetStatusDetails() const {
632 return impl->status_details; 644 return impl->status_details;
633} 645}
634 646
635Loader::AppLoader& System::GetAppLoader() const { 647Loader::AppLoader& System::GetAppLoader() {
648 return *impl->app_loader;
649}
650
651const Loader::AppLoader& System::GetAppLoader() const {
636 return *impl->app_loader; 652 return *impl->app_loader;
637} 653}
638 654
@@ -748,14 +764,6 @@ const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
748 return impl->build_id; 764 return impl->build_id;
749} 765}
750 766
751System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
752 return impl->Init(*this, emu_window);
753}
754
755void System::Shutdown() {
756 impl->Shutdown();
757}
758
759Service::SM::ServiceManager& System::ServiceManager() { 767Service::SM::ServiceManager& System::ServiceManager() {
760 return *impl->service_manager; 768 return *impl->service_manager;
761} 769}
@@ -786,4 +794,16 @@ bool System::IsMulticore() const {
786 return impl->is_multicore; 794 return impl->is_multicore;
787} 795}
788 796
797void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
798 impl->execute_program_callback = std::move(callback);
799}
800
801void System::ExecuteProgram(std::size_t program_index) {
802 if (impl->execute_program_callback) {
803 impl->execute_program_callback(program_index);
804 } else {
805 LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
806 }
807}
808
789} // namespace Core 809} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index 6db896bae..f642befc0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
@@ -144,19 +145,19 @@ public:
144 * Run the OS and Application 145 * Run the OS and Application
145 * This function will start emulation and run the relevant devices 146 * This function will start emulation and run the relevant devices
146 */ 147 */
147 ResultStatus Run(); 148 [[nodiscard]] ResultStatus Run();
148 149
149 /** 150 /**
150 * Pause the OS and Application 151 * Pause the OS and Application
151 * This function will pause emulation and stop the relevant devices 152 * This function will pause emulation and stop the relevant devices
152 */ 153 */
153 ResultStatus Pause(); 154 [[nodiscard]] ResultStatus Pause();
154 155
155 /** 156 /**
156 * Step the CPU one instruction 157 * Step the CPU one instruction
157 * @return Result status, indicating whether or not the operation succeeded. 158 * @return Result status, indicating whether or not the operation succeeded.
158 */ 159 */
159 ResultStatus SingleStep(); 160 [[nodiscard]] ResultStatus SingleStep();
160 161
161 /** 162 /**
162 * Invalidate the CPU instruction caches 163 * Invalidate the CPU instruction caches
@@ -173,22 +174,24 @@ public:
173 * @param emu_window Reference to the host-system window used for video output and keyboard 174 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 175 * input.
175 * @param filepath String path to the executable application to load on the host file system. 176 * @param filepath String path to the executable application to load on the host file system.
177 * @param program_index Specifies the index within the container of the program to launch.
176 * @returns ResultStatus code, indicating if the operation succeeded. 178 * @returns ResultStatus code, indicating if the operation succeeded.
177 */ 179 */
178 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); 180 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
181 std::size_t program_index = 0);
179 182
180 /** 183 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 184 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
182 * application). 185 * application).
183 * @returns True if the emulated system is powered on, otherwise false. 186 * @returns True if the emulated system is powered on, otherwise false.
184 */ 187 */
185 bool IsPoweredOn() const; 188 [[nodiscard]] bool IsPoweredOn() const;
186 189
187 /// Gets a reference to the telemetry session for this emulation session. 190 /// Gets a reference to the telemetry session for this emulation session.
188 Core::TelemetrySession& TelemetrySession(); 191 [[nodiscard]] Core::TelemetrySession& TelemetrySession();
189 192
190 /// Gets a reference to the telemetry session for this emulation session. 193 /// Gets a reference to the telemetry session for this emulation session.
191 const Core::TelemetrySession& TelemetrySession() const; 194 [[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
192 195
193 /// Prepare the core emulation for a reschedule 196 /// Prepare the core emulation for a reschedule
194 void PrepareReschedule(); 197 void PrepareReschedule();
@@ -197,185 +200,178 @@ public:
197 void PrepareReschedule(u32 core_index); 200 void PrepareReschedule(u32 core_index);
198 201
199 /// Gets and resets core performance statistics 202 /// Gets and resets core performance statistics
200 PerfStatsResults GetAndResetPerfStats(); 203 [[nodiscard]] PerfStatsResults GetAndResetPerfStats();
201 204
202 /// Gets an ARM interface to the CPU core that is currently running 205 /// Gets an ARM interface to the CPU core that is currently running
203 ARM_Interface& CurrentArmInterface(); 206 [[nodiscard]] ARM_Interface& CurrentArmInterface();
204 207
205 /// Gets an ARM interface to the CPU core that is currently running 208 /// Gets an ARM interface to the CPU core that is currently running
206 const ARM_Interface& CurrentArmInterface() const; 209 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
207 210
208 /// Gets the index of the currently running CPU core 211 /// Gets the index of the currently running CPU core
209 std::size_t CurrentCoreIndex() const; 212 [[nodiscard]] std::size_t CurrentCoreIndex() const;
210 213
211 /// Gets the scheduler for the CPU core that is currently running 214 /// Gets the scheduler for the CPU core that is currently running
212 Kernel::Scheduler& CurrentScheduler(); 215 [[nodiscard]] Kernel::Scheduler& CurrentScheduler();
213 216
214 /// Gets the scheduler for the CPU core that is currently running 217 /// Gets the scheduler for the CPU core that is currently running
215 const Kernel::Scheduler& CurrentScheduler() const; 218 [[nodiscard]] const Kernel::Scheduler& CurrentScheduler() const;
216 219
217 /// Gets the physical core for the CPU core that is currently running 220 /// Gets the physical core for the CPU core that is currently running
218 Kernel::PhysicalCore& CurrentPhysicalCore(); 221 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
219 222
220 /// Gets the physical core for the CPU core that is currently running 223 /// Gets the physical core for the CPU core that is currently running
221 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 224 [[nodiscard]] const Kernel::PhysicalCore& CurrentPhysicalCore() const;
222 225
223 /// Gets a reference to an ARM interface for the CPU core with the specified index 226 /// Gets a reference to an ARM interface for the CPU core with the specified index
224 ARM_Interface& ArmInterface(std::size_t core_index); 227 [[nodiscard]] ARM_Interface& ArmInterface(std::size_t core_index);
225 228
226 /// Gets a const reference to an ARM interface from the CPU core with the specified index 229 /// Gets a const reference to an ARM interface from the CPU core with the specified index
227 const ARM_Interface& ArmInterface(std::size_t core_index) const; 230 [[nodiscard]] const ARM_Interface& ArmInterface(std::size_t core_index) const;
228 231
229 CpuManager& GetCpuManager(); 232 /// Gets a reference to the underlying CPU manager.
233 [[nodiscard]] CpuManager& GetCpuManager();
230 234
231 const CpuManager& GetCpuManager() const; 235 /// Gets a const reference to the underlying CPU manager
236 [[nodiscard]] const CpuManager& GetCpuManager() const;
232 237
233 /// Gets a reference to the exclusive monitor 238 /// Gets a reference to the exclusive monitor
234 ExclusiveMonitor& Monitor(); 239 [[nodiscard]] ExclusiveMonitor& Monitor();
235 240
236 /// Gets a constant reference to the exclusive monitor 241 /// Gets a constant reference to the exclusive monitor
237 const ExclusiveMonitor& Monitor() const; 242 [[nodiscard]] const ExclusiveMonitor& Monitor() const;
238 243
239 /// Gets a mutable reference to the system memory instance. 244 /// Gets a mutable reference to the system memory instance.
240 Core::Memory::Memory& Memory(); 245 [[nodiscard]] Core::Memory::Memory& Memory();
241 246
242 /// Gets a constant reference to the system memory instance. 247 /// Gets a constant reference to the system memory instance.
243 const Core::Memory::Memory& Memory() const; 248 [[nodiscard]] const Core::Memory::Memory& Memory() const;
244 249
245 /// Gets a mutable reference to the GPU interface 250 /// Gets a mutable reference to the GPU interface
246 Tegra::GPU& GPU(); 251 [[nodiscard]] Tegra::GPU& GPU();
247 252
248 /// Gets an immutable reference to the GPU interface. 253 /// Gets an immutable reference to the GPU interface.
249 const Tegra::GPU& GPU() const; 254 [[nodiscard]] const Tegra::GPU& GPU() const;
250 255
251 /// Gets a mutable reference to the renderer. 256 /// Gets a mutable reference to the renderer.
252 VideoCore::RendererBase& Renderer(); 257 [[nodiscard]] VideoCore::RendererBase& Renderer();
253 258
254 /// Gets an immutable reference to the renderer. 259 /// Gets an immutable reference to the renderer.
255 const VideoCore::RendererBase& Renderer() const; 260 [[nodiscard]] const VideoCore::RendererBase& Renderer() const;
256 261
257 /// Gets the scheduler for the CPU core with the specified index 262 /// Gets the scheduler for the CPU core with the specified index
258 Kernel::Scheduler& Scheduler(std::size_t core_index); 263 [[nodiscard]] Kernel::Scheduler& Scheduler(std::size_t core_index);
259 264
260 /// Gets the scheduler for the CPU core with the specified index 265 /// Gets the scheduler for the CPU core with the specified index
261 const Kernel::Scheduler& Scheduler(std::size_t core_index) const; 266 [[nodiscard]] const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
262 267
263 /// Gets the global scheduler 268 /// Gets the global scheduler
264 Kernel::GlobalScheduler& GlobalScheduler(); 269 [[nodiscard]] Kernel::GlobalScheduler& GlobalScheduler();
265 270
266 /// Gets the global scheduler 271 /// Gets the global scheduler
267 const Kernel::GlobalScheduler& GlobalScheduler() const; 272 [[nodiscard]] const Kernel::GlobalScheduler& GlobalScheduler() const;
268 273
269 /// Gets the manager for the guest device memory 274 /// Gets the manager for the guest device memory
270 Core::DeviceMemory& DeviceMemory(); 275 [[nodiscard]] Core::DeviceMemory& DeviceMemory();
271 276
272 /// Gets the manager for the guest device memory 277 /// Gets the manager for the guest device memory
273 const Core::DeviceMemory& DeviceMemory() const; 278 [[nodiscard]] const Core::DeviceMemory& DeviceMemory() const;
274 279
275 /// Provides a pointer to the current process 280 /// Provides a pointer to the current process
276 Kernel::Process* CurrentProcess(); 281 [[nodiscard]] Kernel::Process* CurrentProcess();
277 282
278 /// Provides a constant pointer to the current process. 283 /// Provides a constant pointer to the current process.
279 const Kernel::Process* CurrentProcess() const; 284 [[nodiscard]] const Kernel::Process* CurrentProcess() const;
280 285
281 /// Provides a reference to the core timing instance. 286 /// Provides a reference to the core timing instance.
282 Timing::CoreTiming& CoreTiming(); 287 [[nodiscard]] Timing::CoreTiming& CoreTiming();
283 288
284 /// Provides a constant reference to the core timing instance. 289 /// Provides a constant reference to the core timing instance.
285 const Timing::CoreTiming& CoreTiming() const; 290 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
286 291
287 /// Provides a reference to the interrupt manager instance. 292 /// Provides a reference to the interrupt manager instance.
288 Core::Hardware::InterruptManager& InterruptManager(); 293 [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
289 294
290 /// Provides a constant reference to the interrupt manager instance. 295 /// Provides a constant reference to the interrupt manager instance.
291 const Core::Hardware::InterruptManager& InterruptManager() const; 296 [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
292 297
293 /// Provides a reference to the kernel instance. 298 /// Provides a reference to the kernel instance.
294 Kernel::KernelCore& Kernel(); 299 [[nodiscard]] Kernel::KernelCore& Kernel();
295 300
296 /// Provides a constant reference to the kernel instance. 301 /// Provides a constant reference to the kernel instance.
297 const Kernel::KernelCore& Kernel() const; 302 [[nodiscard]] const Kernel::KernelCore& Kernel() const;
298 303
299 /// Provides a reference to the internal PerfStats instance. 304 /// Provides a reference to the internal PerfStats instance.
300 Core::PerfStats& GetPerfStats(); 305 [[nodiscard]] Core::PerfStats& GetPerfStats();
301 306
302 /// Provides a constant reference to the internal PerfStats instance. 307 /// Provides a constant reference to the internal PerfStats instance.
303 const Core::PerfStats& GetPerfStats() const; 308 [[nodiscard]] const Core::PerfStats& GetPerfStats() const;
304 309
305 /// Provides a reference to the frame limiter; 310 /// Provides a reference to the frame limiter;
306 Core::FrameLimiter& FrameLimiter(); 311 [[nodiscard]] Core::FrameLimiter& FrameLimiter();
307 312
308 /// Provides a constant referent to the frame limiter 313 /// Provides a constant referent to the frame limiter
309 const Core::FrameLimiter& FrameLimiter() const; 314 [[nodiscard]] const Core::FrameLimiter& FrameLimiter() const;
310 315
311 /// Gets the name of the current game 316 /// Gets the name of the current game
312 Loader::ResultStatus GetGameName(std::string& out) const; 317 [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
313 318
314 void SetStatus(ResultStatus new_status, const char* details); 319 void SetStatus(ResultStatus new_status, const char* details);
315 320
316 const std::string& GetStatusDetails() const; 321 [[nodiscard]] const std::string& GetStatusDetails() const;
317 322
318 Loader::AppLoader& GetAppLoader() const; 323 [[nodiscard]] Loader::AppLoader& GetAppLoader();
324 [[nodiscard]] const Loader::AppLoader& GetAppLoader() const;
319 325
320 Service::SM::ServiceManager& ServiceManager(); 326 [[nodiscard]] Service::SM::ServiceManager& ServiceManager();
321 const Service::SM::ServiceManager& ServiceManager() const; 327 [[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const;
322 328
323 void SetFilesystem(FileSys::VirtualFilesystem vfs); 329 void SetFilesystem(FileSys::VirtualFilesystem vfs);
324 330
325 FileSys::VirtualFilesystem GetFilesystem() const; 331 [[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
326 332
327 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, 333 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
328 const std::array<u8, 0x20>& build_id, VAddr main_region_begin, 334 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
329 u64 main_region_size); 335 u64 main_region_size);
330 336
331 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 337 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
332
333 void SetDefaultAppletFrontendSet(); 338 void SetDefaultAppletFrontendSet();
334 339
335 Service::AM::Applets::AppletManager& GetAppletManager(); 340 [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager();
336 341 [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
337 const Service::AM::Applets::AppletManager& GetAppletManager() const;
338 342
339 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); 343 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
340 344
341 FileSys::ContentProvider& GetContentProvider(); 345 [[nodiscard]] FileSys::ContentProvider& GetContentProvider();
342 346 [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const;
343 const FileSys::ContentProvider& GetContentProvider() const;
344 347
345 Service::FileSystem::FileSystemController& GetFileSystemController(); 348 [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController();
346 349 [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const;
347 const Service::FileSystem::FileSystemController& GetFileSystemController() const;
348 350
349 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 351 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
350 FileSys::ContentProvider* provider); 352 FileSys::ContentProvider* provider);
351 353
352 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); 354 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
353 355
354 const Reporter& GetReporter() const; 356 [[nodiscard]] const Reporter& GetReporter() const;
355
356 Service::Glue::ARPManager& GetARPManager();
357 357
358 const Service::Glue::ARPManager& GetARPManager() const; 358 [[nodiscard]] Service::Glue::ARPManager& GetARPManager();
359 [[nodiscard]] const Service::Glue::ARPManager& GetARPManager() const;
359 360
360 Service::APM::Controller& GetAPMController(); 361 [[nodiscard]] Service::APM::Controller& GetAPMController();
362 [[nodiscard]] const Service::APM::Controller& GetAPMController() const;
361 363
362 const Service::APM::Controller& GetAPMController() const; 364 [[nodiscard]] Service::LM::Manager& GetLogManager();
365 [[nodiscard]] const Service::LM::Manager& GetLogManager() const;
363 366
364 Service::LM::Manager& GetLogManager(); 367 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
365 368 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
366 const Service::LM::Manager& GetLogManager() const;
367
368 Service::Time::TimeManager& GetTimeManager();
369
370 const Service::Time::TimeManager& GetTimeManager() const;
371 369
372 void SetExitLock(bool locked); 370 void SetExitLock(bool locked);
373 371 [[nodiscard]] bool GetExitLock() const;
374 bool GetExitLock() const;
375 372
376 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id); 373 void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
377 374 [[nodiscard]] const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
378 const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
379 375
380 /// Register a host thread as an emulated CPU Core. 376 /// Register a host thread as an emulated CPU Core.
381 void RegisterCoreThread(std::size_t id); 377 void RegisterCoreThread(std::size_t id);
@@ -390,18 +386,27 @@ public:
390 void ExitDynarmicProfile(); 386 void ExitDynarmicProfile();
391 387
392 /// Tells if system is running on multicore. 388 /// Tells if system is running on multicore.
393 bool IsMulticore() const; 389 [[nodiscard]] bool IsMulticore() const;
394 390
395private: 391 /// Type used for the frontend to designate a callback for System to re-launch the application
396 System(); 392 /// using a specified program index.
393 using ExecuteProgramCallback = std::function<void(std::size_t)>;
397 394
398 /** 395 /**
399 * Initialize the emulated system. 396 * Registers a callback from the frontend for System to re-launch the application using a
400 * @param emu_window Reference to the host-system window used for video output and keyboard 397 * specified program index.
401 * input. 398 * @param callback Callback from the frontend to relaunch the application.
402 * @return ResultStatus code, indicating if the operation succeeded. 399 */
400 void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
401
402 /**
403 * Instructs the frontend to re-launch the application using the specified program_index.
404 * @param program_index Specifies the index within the application of the program to launch.
403 */ 405 */
404 ResultStatus Init(Frontend::EmuWindow& emu_window); 406 void ExecuteProgram(std::size_t program_index);
407
408private:
409 System();
405 410
406 struct Impl; 411 struct Impl;
407 std::unique_ptr<Impl> impl; 412 std::unique_ptr<Impl> impl;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 983210197..100e90d82 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/fiber.h" 5#include "common/fiber.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/scope_exit.h"
7#include "common/thread.h" 8#include "common/thread.h"
8#include "core/arm/exclusive_monitor.h" 9#include "core/arm/exclusive_monitor.h"
9#include "core/core.h" 10#include "core/core.h"
@@ -343,6 +344,16 @@ void CpuManager::RunThread(std::size_t core) {
343 data.initialized = true; 344 data.initialized = true;
344 const bool sc_sync = !is_async_gpu && !is_multicore; 345 const bool sc_sync = !is_async_gpu && !is_multicore;
345 bool sc_sync_first_use = sc_sync; 346 bool sc_sync_first_use = sc_sync;
347
348 // Cleanup
349 SCOPE_EXIT({
350 data.host_context->Exit();
351 data.enter_barrier.reset();
352 data.exit_barrier.reset();
353 data.initialized = false;
354 MicroProfileOnThreadExit();
355 });
356
346 /// Running 357 /// Running
347 while (running_mode) { 358 while (running_mode) {
348 data.is_running = false; 359 data.is_running = false;
@@ -351,6 +362,12 @@ void CpuManager::RunThread(std::size_t core) {
351 system.GPU().ObtainContext(); 362 system.GPU().ObtainContext();
352 sc_sync_first_use = false; 363 sc_sync_first_use = false;
353 } 364 }
365
366 // Abort if emulation was killed before the session really starts
367 if (!system.IsPoweredOn()) {
368 return;
369 }
370
354 auto& scheduler = system.Kernel().CurrentScheduler(); 371 auto& scheduler = system.Kernel().CurrentScheduler();
355 Kernel::Thread* current_thread = scheduler.GetCurrentThread(); 372 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
356 data.is_running = true; 373 data.is_running = true;
@@ -360,13 +377,6 @@ void CpuManager::RunThread(std::size_t core) {
360 data.exit_barrier->Wait(); 377 data.exit_barrier->Wait();
361 data.is_paused = false; 378 data.is_paused = false;
362 } 379 }
363 /// Time to cleanup
364 data.host_context->Exit();
365 data.enter_barrier.reset();
366 data.exit_barrier.reset();
367 data.initialized = false;
368
369 MicroProfileOnThreadExit();
370} 380}
371 381
372} // namespace Core 382} // namespace Core
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 956da68f7..8dee5590b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_) 32XCI::XCI(VirtualFile file_, std::size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
62 } 62 }
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index);
66 67
67 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
68 program = 69 program =
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 2d0a0f285..4960e90fe 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file); 81 explicit XCI(VirtualFile file, std::size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 807b05821..e9d1607d0 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -112,7 +112,10 @@ bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
112} 112}
113} // Anonymous namespace 113} // Anonymous namespace
114 114
115PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 115PatchManager::PatchManager(u64 title_id_,
116 const Service::FileSystem::FileSystemController& fs_controller_,
117 const ContentProvider& content_provider_)
118 : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
116 119
117PatchManager::~PatchManager() = default; 120PatchManager::~PatchManager() = default;
118 121
@@ -128,34 +131,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
128 131
129 if (Settings::values.dump_exefs) { 132 if (Settings::values.dump_exefs) {
130 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); 133 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
131 const auto dump_dir = 134 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
132 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
133 if (dump_dir != nullptr) { 135 if (dump_dir != nullptr) {
134 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); 136 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
135 VfsRawCopyD(exefs, exefs_dir); 137 VfsRawCopyD(exefs, exefs_dir);
136 } 138 }
137 } 139 }
138 140
139 const auto& installed = Core::System::GetInstance().GetContentProvider();
140
141 const auto& disabled = Settings::values.disabled_addons[title_id]; 141 const auto& disabled = Settings::values.disabled_addons[title_id];
142 const auto update_disabled = 142 const auto update_disabled =
143 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); 143 std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
144 144
145 // Game Updates 145 // Game Updates
146 const auto update_tid = GetUpdateTitleID(title_id); 146 const auto update_tid = GetUpdateTitleID(title_id);
147 const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); 147 const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
148 148
149 if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && 149 if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
150 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { 150 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
151 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 151 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
152 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 152 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
153 exefs = update->GetExeFS(); 153 exefs = update->GetExeFS();
154 } 154 }
155 155
156 // LayeredExeFS 156 // LayeredExeFS
157 const auto load_dir = 157 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
158 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
159 if (load_dir != nullptr && load_dir->GetSize() > 0) { 158 if (load_dir != nullptr && load_dir->GetSize() > 0) {
160 auto patch_dirs = load_dir->GetSubdirectories(); 159 auto patch_dirs = load_dir->GetSubdirectories();
161 std::sort( 160 std::sort(
@@ -241,8 +240,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
241 if (Settings::values.dump_nso) { 240 if (Settings::values.dump_nso) {
242 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, 241 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
243 title_id); 242 title_id);
244 const auto dump_dir = 243 const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
245 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
246 if (dump_dir != nullptr) { 244 if (dump_dir != nullptr) {
247 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 245 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
248 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); 246 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -254,8 +252,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
254 252
255 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); 253 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
256 254
257 const auto load_dir = 255 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
258 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
259 if (load_dir == nullptr) { 256 if (load_dir == nullptr) {
260 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 257 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
261 return nso; 258 return nso;
@@ -298,8 +295,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
298 295
299 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); 296 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
300 297
301 const auto load_dir = 298 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
302 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
303 if (load_dir == nullptr) { 299 if (load_dir == nullptr) {
304 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 300 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
305 return false; 301 return false;
@@ -313,8 +309,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
313} 309}
314 310
315std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 311std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
316 const Core::System& system, const BuildID& build_id_) const { 312 const BuildID& build_id_) const {
317 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); 313 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
318 if (load_dir == nullptr) { 314 if (load_dir == nullptr) {
319 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 315 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
320 return {}; 316 return {};
@@ -347,9 +343,9 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
347 return out; 343 return out;
348} 344}
349 345
350static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 346static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
351 const auto load_dir = 347 const Service::FileSystem::FileSystemController& fs_controller) {
352 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); 348 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
353 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 349 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
354 load_dir == nullptr || load_dir->GetSize() <= 0) { 350 load_dir == nullptr || load_dir->GetSize() <= 0) {
355 return; 351 return;
@@ -411,19 +407,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
411 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", 407 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
412 title_id, static_cast<u8>(type)); 408 title_id, static_cast<u8>(type));
413 409
414 if (type == ContentRecordType::Program || type == ContentRecordType::Data) 410 if (type == ContentRecordType::Program || type == ContentRecordType::Data) {
415 LOG_INFO(Loader, "{}", log_string); 411 LOG_INFO(Loader, "{}", log_string);
416 else 412 } else {
417 LOG_DEBUG(Loader, "{}", log_string); 413 LOG_DEBUG(Loader, "{}", log_string);
414 }
418 415
419 if (romfs == nullptr) 416 if (romfs == nullptr) {
420 return romfs; 417 return romfs;
421 418 }
422 const auto& installed = Core::System::GetInstance().GetContentProvider();
423 419
424 // Game Updates 420 // Game Updates
425 const auto update_tid = GetUpdateTitleID(title_id); 421 const auto update_tid = GetUpdateTitleID(title_id);
426 const auto update = installed.GetEntryRaw(update_tid, type); 422 const auto update = content_provider.GetEntryRaw(update_tid, type);
427 423
428 const auto& disabled = Settings::values.disabled_addons[title_id]; 424 const auto& disabled = Settings::values.disabled_addons[title_id];
429 const auto update_disabled = 425 const auto update_disabled =
@@ -434,7 +430,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
434 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 430 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
435 new_nca->GetRomFS() != nullptr) { 431 new_nca->GetRomFS() != nullptr) {
436 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", 432 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
437 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 433 FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
438 romfs = new_nca->GetRomFS(); 434 romfs = new_nca->GetRomFS();
439 } 435 }
440 } else if (!update_disabled && update_raw != nullptr) { 436 } else if (!update_disabled && update_raw != nullptr) {
@@ -447,7 +443,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
447 } 443 }
448 444
449 // LayeredFS 445 // LayeredFS
450 ApplyLayeredFS(romfs, title_id, type); 446 ApplyLayeredFS(romfs, title_id, type, fs_controller);
451 447
452 return romfs; 448 return romfs;
453} 449}
@@ -458,12 +454,11 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
458 } 454 }
459 455
460 std::map<std::string, std::string, std::less<>> out; 456 std::map<std::string, std::string, std::less<>> out;
461 const auto& installed = Core::System::GetInstance().GetContentProvider();
462 const auto& disabled = Settings::values.disabled_addons[title_id]; 457 const auto& disabled = Settings::values.disabled_addons[title_id];
463 458
464 // Game Updates 459 // Game Updates
465 const auto update_tid = GetUpdateTitleID(title_id); 460 const auto update_tid = GetUpdateTitleID(title_id);
466 PatchManager update{update_tid}; 461 PatchManager update{update_tid, fs_controller, content_provider};
467 const auto metadata = update.GetControlMetadata(); 462 const auto metadata = update.GetControlMetadata();
468 const auto& nacp = metadata.first; 463 const auto& nacp = metadata.first;
469 464
@@ -474,8 +469,8 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
474 if (nacp != nullptr) { 469 if (nacp != nullptr) {
475 out.insert_or_assign(update_label, nacp->GetVersionString()); 470 out.insert_or_assign(update_label, nacp->GetVersionString());
476 } else { 471 } else {
477 if (installed.HasEntry(update_tid, ContentRecordType::Program)) { 472 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
478 const auto meta_ver = installed.GetEntryVersion(update_tid); 473 const auto meta_ver = content_provider.GetEntryVersion(update_tid);
479 if (meta_ver.value_or(0) == 0) { 474 if (meta_ver.value_or(0) == 0) {
480 out.insert_or_assign(update_label, ""); 475 out.insert_or_assign(update_label, "");
481 } else { 476 } else {
@@ -487,8 +482,7 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
487 } 482 }
488 483
489 // General Mods (LayeredFS and IPS) 484 // General Mods (LayeredFS and IPS)
490 const auto mod_dir = 485 const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
491 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
492 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 486 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
493 for (const auto& mod : mod_dir->GetSubdirectories()) { 487 for (const auto& mod : mod_dir->GetSubdirectories()) {
494 std::string types; 488 std::string types;
@@ -532,13 +526,15 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
532 } 526 }
533 527
534 // DLC 528 // DLC
535 const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); 529 const auto dlc_entries =
530 content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
536 std::vector<ContentProviderEntry> dlc_match; 531 std::vector<ContentProviderEntry> dlc_match;
537 dlc_match.reserve(dlc_entries.size()); 532 dlc_match.reserve(dlc_entries.size());
538 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 533 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
539 [this, &installed](const ContentProviderEntry& entry) { 534 [this](const ContentProviderEntry& entry) {
540 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && 535 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
541 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 536 content_provider.GetEntry(entry)->GetStatus() ==
537 Loader::ResultStatus::Success;
542 }); 538 });
543 if (!dlc_match.empty()) { 539 if (!dlc_match.empty()) {
544 // Ensure sorted so DLC IDs show in order. 540 // Ensure sorted so DLC IDs show in order.
@@ -559,19 +555,16 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
559} 555}
560 556
561std::optional<u32> PatchManager::GetGameVersion() const { 557std::optional<u32> PatchManager::GetGameVersion() const {
562 const auto& installed = Core::System::GetInstance().GetContentProvider();
563 const auto update_tid = GetUpdateTitleID(title_id); 558 const auto update_tid = GetUpdateTitleID(title_id);
564 if (installed.HasEntry(update_tid, ContentRecordType::Program)) { 559 if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
565 return installed.GetEntryVersion(update_tid); 560 return content_provider.GetEntryVersion(update_tid);
566 } 561 }
567 562
568 return installed.GetEntryVersion(title_id); 563 return content_provider.GetEntryVersion(title_id);
569} 564}
570 565
571PatchManager::Metadata PatchManager::GetControlMetadata() const { 566PatchManager::Metadata PatchManager::GetControlMetadata() const {
572 const auto& installed = Core::System::GetInstance().GetContentProvider(); 567 const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
573
574 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
575 if (base_control_nca == nullptr) { 568 if (base_control_nca == nullptr) {
576 return {}; 569 return {};
577 } 570 }
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 1f28c6241..fb1853035 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -17,8 +17,13 @@ namespace Core {
17class System; 17class System;
18} 18}
19 19
20namespace Service::FileSystem {
21class FileSystemController;
22}
23
20namespace FileSys { 24namespace FileSys {
21 25
26class ContentProvider;
22class NCA; 27class NCA;
23class NACP; 28class NACP;
24 29
@@ -29,7 +34,9 @@ public:
29 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; 34 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
30 using PatchVersionNames = std::map<std::string, std::string, std::less<>>; 35 using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
31 36
32 explicit PatchManager(u64 title_id); 37 explicit PatchManager(u64 title_id_,
38 const Service::FileSystem::FileSystemController& fs_controller_,
39 const ContentProvider& content_provider_);
33 ~PatchManager(); 40 ~PatchManager();
34 41
35 [[nodiscard]] u64 GetTitleID() const; 42 [[nodiscard]] u64 GetTitleID() const;
@@ -50,7 +57,7 @@ public:
50 57
51 // Creates a CheatList object with all 58 // Creates a CheatList object with all
52 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( 59 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
53 const Core::System& system, const BuildID& build_id) const; 60 const BuildID& build_id) const;
54 61
55 // Currently tracked RomFS patches: 62 // Currently tracked RomFS patches:
56 // - Game Updates 63 // - Game Updates
@@ -80,6 +87,8 @@ private:
80 const std::string& build_id) const; 87 const std::string& build_id) const;
81 88
82 u64 title_id; 89 u64 title_id;
90 const Service::FileSystem::FileSystemController& fs_controller;
91 const ContentProvider& content_provider;
83}; 92};
84 93
85} // namespace FileSys 94} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index e967a254e..987199747 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -37,10 +37,12 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
37} 37}
38 38
39ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const { 39ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
40 if (!updatable) 40 if (!updatable) {
41 return MakeResult<VirtualFile>(file); 41 return MakeResult<VirtualFile>(file);
42 }
42 43
43 const PatchManager patch_manager(current_process_title_id); 44 const PatchManager patch_manager{current_process_title_id, filesystem_controller,
45 content_provider};
44 return MakeResult<VirtualFile>( 46 return MakeResult<VirtualFile>(
45 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); 47 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
46} 48}
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 90641d23b..c05735ddd 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,8 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_) 23NSP::NSP(VirtualFile file_, std::size_t program_index)
24 : file(std::move(file_)), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 26 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 27 status = pfs->GetStatus();
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id); 149 const auto title_id_iter = ncas.find(title_id + program_index);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index c70a11b5b..54581a6f3 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
27 27
28class NSP : public ReadOnlyVfsDirectory { 28class NSP : public ReadOnlyVfsDirectory {
29public: 29public:
30 explicit NSP(VirtualFile file); 30 explicit NSP(VirtualFile file, std::size_t program_index = 0);
31 ~NSP() override; 31 ~NSP() override;
32 32
33 Loader::ResultStatus GetStatus() const; 33 Loader::ResultStatus GetStatus() const;
@@ -69,6 +69,8 @@ private:
69 69
70 VirtualFile file; 70 VirtualFile file;
71 71
72 const std::size_t program_index;
73
72 bool extracted = false; 74 bool extracted = false;
73 Loader::ResultStatus status; 75 Loader::ResultStatus status;
74 std::map<u64, Loader::ResultStatus> program_status; 76 std::map<u64, Loader::ResultStatus> program_status;
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 25ac5af46..11c2e96ca 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -30,10 +30,12 @@ public:
30 virtual StatusType GetStatus() const { 30 virtual StatusType GetStatus() const {
31 return {}; 31 return {};
32 } 32 }
33 virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { 33 virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
34 return {}; 34 return {};
35 } 35 }
36 virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const { 36 virtual bool SetRumblePlay([[maybe_unused]] f32 amp_low, [[maybe_unused]] f32 freq_low,
37 [[maybe_unused]] f32 amp_high,
38 [[maybe_unused]] f32 freq_high) const {
37 return {}; 39 return {};
38 } 40 }
39}; 41};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index bafd1ced7..e3b770d66 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -681,7 +681,7 @@ static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
681} 681}
682 682
683/// Used to output a message on a debug hardware unit - does nothing on a retail unit 683/// Used to output a message on a debug hardware unit - does nothing on a retail unit
684static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { 684static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
685 if (len == 0) { 685 if (len == 0) {
686 return; 686 return;
687 } 687 }
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index ded52ea0b..c2c11dbcb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -742,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
742 bool is_locked = false; 742 bool is_locked = false;
743 743
744 if (res != Loader::ResultStatus::Success) { 744 if (res != Loader::ResultStatus::Success) {
745 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; 745 const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
746 auto nacp_unique = pm.GetControlMetadata().first; 746 system.GetFileSystemController(),
747 system.GetContentProvider()};
748 const auto nacp_unique = pm.GetControlMetadata().first;
747 749
748 if (nacp_unique != nullptr) { 750 if (nacp_unique != nullptr) {
749 is_locked = nacp_unique->GetUserAccountSwitchLock(); 751 is_locked = nacp_unique->GetUserAccountSwitchLock();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index eb097738a..703a9b234 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -246,9 +246,8 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
246 246
247IDebugFunctions::~IDebugFunctions() = default; 247IDebugFunctions::~IDebugFunctions() = default;
248 248
249ISelfController::ISelfController(Core::System& system, 249ISelfController::ISelfController(Core::System& system, NVFlinger::NVFlinger& nvflinger)
250 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 250 : ServiceFramework("ISelfController"), system(system), nvflinger(nvflinger) {
251 : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
252 // clang-format off 251 // clang-format off
253 static const FunctionInfo functions[] = { 252 static const FunctionInfo functions[] = {
254 {0, &ISelfController::Exit, "Exit"}, 253 {0, &ISelfController::Exit, "Exit"},
@@ -458,8 +457,8 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
458 457
459 // TODO(Subv): Find out how AM determines the display to use, for now just 458 // TODO(Subv): Find out how AM determines the display to use, for now just
460 // create the layer in the Default display. 459 // create the layer in the Default display.
461 const auto display_id = nvflinger->OpenDisplay("Default"); 460 const auto display_id = nvflinger.OpenDisplay("Default");
462 const auto layer_id = nvflinger->CreateLayer(*display_id); 461 const auto layer_id = nvflinger.CreateLayer(*display_id);
463 462
464 IPC::ResponseBuilder rb{ctx, 4}; 463 IPC::ResponseBuilder rb{ctx, 4};
465 rb.Push(RESULT_SUCCESS); 464 rb.Push(RESULT_SUCCESS);
@@ -476,8 +475,8 @@ void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestConte
476 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse 475 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
477 // side effects. 476 // side effects.
478 // TODO: Support multiple layers 477 // TODO: Support multiple layers
479 const auto display_id = nvflinger->OpenDisplay("Default"); 478 const auto display_id = nvflinger.OpenDisplay("Default");
480 const auto layer_id = nvflinger->CreateLayer(*display_id); 479 const auto layer_id = nvflinger.CreateLayer(*display_id);
481 480
482 IPC::ResponseBuilder rb{ctx, 4}; 481 IPC::ResponseBuilder rb{ctx, 4};
483 rb.Push(RESULT_SUCCESS); 482 rb.Push(RESULT_SUCCESS);
@@ -1189,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1189 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, 1188 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
1190 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, 1189 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
1191 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, 1190 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
1192 {120, nullptr, "ExecuteProgram"}, 1191 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
1193 {121, nullptr, "ClearUserChannel"}, 1192 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
1194 {122, nullptr, "UnpopToUserChannel"}, 1193 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
1195 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, 1194 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1195 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1196 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
@@ -1381,13 +1380,16 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
1381 const auto res = [this] { 1380 const auto res = [this] {
1382 const auto title_id = system.CurrentProcess()->GetTitleID(); 1381 const auto title_id = system.CurrentProcess()->GetTitleID();
1383 1382
1384 FileSys::PatchManager pm{title_id}; 1383 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
1384 system.GetContentProvider()};
1385 auto res = pm.GetControlMetadata(); 1385 auto res = pm.GetControlMetadata();
1386 if (res.first != nullptr) { 1386 if (res.first != nullptr) {
1387 return res; 1387 return res;
1388 } 1388 }
1389 1389
1390 FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; 1390 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
1391 system.GetFileSystemController(),
1392 system.GetContentProvider()};
1391 return pm_update.GetControlMetadata(); 1393 return pm_update.GetControlMetadata();
1392 }(); 1394 }();
1393 1395
@@ -1415,13 +1417,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
1415 const auto res = [this] { 1417 const auto res = [this] {
1416 const auto title_id = system.CurrentProcess()->GetTitleID(); 1418 const auto title_id = system.CurrentProcess()->GetTitleID();
1417 1419
1418 FileSys::PatchManager pm{title_id}; 1420 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
1421 system.GetContentProvider()};
1419 auto res = pm.GetControlMetadata(); 1422 auto res = pm.GetControlMetadata();
1420 if (res.first != nullptr) { 1423 if (res.first != nullptr) {
1421 return res; 1424 return res;
1422 } 1425 }
1423 1426
1424 FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; 1427 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
1428 system.GetFileSystemController(),
1429 system.GetContentProvider()};
1425 return pm_update.GetControlMetadata(); 1430 return pm_update.GetControlMetadata();
1426 }(); 1431 }();
1427 1432
@@ -1556,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1556 rb.Push<u32>(0); 1561 rb.Push<u32>(0);
1557} 1562}
1558 1563
1564void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
1565 LOG_WARNING(Service_AM, "(STUBBED) called");
1566
1567 IPC::RequestParser rp{ctx};
1568 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
1569 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
1570 const auto program_index = rp.Pop<u64>();
1571
1572 IPC::ResponseBuilder rb{ctx, 2};
1573 rb.Push(RESULT_SUCCESS);
1574
1575 system.ExecuteProgram(program_index);
1576}
1577
1578void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
1579 LOG_WARNING(Service_AM, "(STUBBED) called");
1580
1581 IPC::ResponseBuilder rb{ctx, 2};
1582 rb.Push(RESULT_SUCCESS);
1583}
1584
1585void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
1586 LOG_WARNING(Service_AM, "(STUBBED) called");
1587
1588 IPC::ResponseBuilder rb{ctx, 2};
1589 rb.Push(RESULT_SUCCESS);
1590}
1591
1559void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { 1592void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1560 LOG_WARNING(Service_AM, "(STUBBED) called"); 1593 LOG_WARNING(Service_AM, "(STUBBED) called");
1561 1594
@@ -1580,8 +1613,8 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe
1580 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable); 1613 rb.PushCopyObjects(friend_invitation_storage_channel_event.readable);
1581} 1614}
1582 1615
1583void InstallInterfaces(SM::ServiceManager& service_manager, 1616void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1584 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { 1617 Core::System& system) {
1585 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); 1618 auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
1586 // Needed on game boot 1619 // Needed on game boot
1587 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); 1620 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index bcc06affe..af97c303a 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -121,8 +121,7 @@ public:
121 121
122class ISelfController final : public ServiceFramework<ISelfController> { 122class ISelfController final : public ServiceFramework<ISelfController> {
123public: 123public:
124 explicit ISelfController(Core::System& system_, 124 explicit ISelfController(Core::System& system_, NVFlinger::NVFlinger& nvflinger_);
125 std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
126 ~ISelfController() override; 125 ~ISelfController() override;
127 126
128private: 127private:
@@ -156,7 +155,7 @@ private:
156 }; 155 };
157 156
158 Core::System& system; 157 Core::System& system;
159 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 158 NVFlinger::NVFlinger& nvflinger;
160 Kernel::EventPair launchable_event; 159 Kernel::EventPair launchable_event;
161 Kernel::EventPair accumulated_suspended_tick_changed_event; 160 Kernel::EventPair accumulated_suspended_tick_changed_event;
162 161
@@ -288,6 +287,9 @@ private:
288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 287 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 288 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
290 void ExecuteProgram(Kernel::HLERequestContext& ctx);
291 void ClearUserChannel(Kernel::HLERequestContext& ctx);
292 void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
291 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); 293 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
292 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 294 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
293 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 295 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
@@ -332,7 +334,7 @@ public:
332}; 334};
333 335
334/// Registers all AM services with the specified service manager. 336/// Registers all AM services with the specified service manager.
335void InstallInterfaces(SM::ServiceManager& service_manager, 337void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
336 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); 338 Core::System& system);
337 339
338} // namespace Service::AM 340} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index be23ca747..7de506b70 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -13,10 +13,10 @@ namespace Service::AM {
13 13
14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
15public: 15public:
16 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 16 explicit ILibraryAppletProxy(NVFlinger::NVFlinger& nvflinger,
17 std::shared_ptr<AppletMessageQueue> msg_queue, 17 std::shared_ptr<AppletMessageQueue> msg_queue,
18 Core::System& system) 18 Core::System& system)
19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(nvflinger),
20 msg_queue(std::move(msg_queue)), system(system) { 20 msg_queue(std::move(msg_queue)), system(system) {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
@@ -109,16 +109,16 @@ private:
109 rb.PushIpcInterface<IApplicationFunctions>(system); 109 rb.PushIpcInterface<IApplicationFunctions>(system);
110 } 110 }
111 111
112 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 112 NVFlinger::NVFlinger& nvflinger;
113 std::shared_ptr<AppletMessageQueue> msg_queue; 113 std::shared_ptr<AppletMessageQueue> msg_queue;
114 Core::System& system; 114 Core::System& system;
115}; 115};
116 116
117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
118public: 118public:
119 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 119 explicit ISystemAppletProxy(NVFlinger::NVFlinger& nvflinger,
120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
121 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 121 : ServiceFramework("ISystemAppletProxy"), nvflinger(nvflinger),
122 msg_queue(std::move(msg_queue)), system(system) { 122 msg_queue(std::move(msg_queue)), system(system) {
123 // clang-format off 123 // clang-format off
124 static const FunctionInfo functions[] = { 124 static const FunctionInfo functions[] = {
@@ -220,7 +220,8 @@ private:
220 rb.Push(RESULT_SUCCESS); 220 rb.Push(RESULT_SUCCESS);
221 rb.PushIpcInterface<IApplicationCreator>(); 221 rb.PushIpcInterface<IApplicationCreator>();
222 } 222 }
223 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 223
224 NVFlinger::NVFlinger& nvflinger;
224 std::shared_ptr<AppletMessageQueue> msg_queue; 225 std::shared_ptr<AppletMessageQueue> msg_queue;
225 Core::System& system; 226 Core::System& system;
226}; 227};
@@ -249,10 +250,10 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
249 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); 250 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
250} 251}
251 252
252AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 253AppletAE::AppletAE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
253 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 254 Core::System& system)
254 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), 255 : ServiceFramework("appletAE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
255 msg_queue(std::move(msg_queue)), system(system) { 256 system(system) {
256 // clang-format off 257 // clang-format off
257 static const FunctionInfo functions[] = { 258 static const FunctionInfo functions[] = {
258 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 259 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 2e3e45915..761844a1f 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletAE final : public ServiceFramework<AppletAE> { 24class AppletAE final : public ServiceFramework<AppletAE> {
25public: 25public:
26 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletAE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletAE() override; 28 ~AppletAE() override;
29 29
@@ -34,7 +34,7 @@ private:
34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); 34 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); 35 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
36 36
37 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 37 NVFlinger::NVFlinger& nvflinger;
38 std::shared_ptr<AppletMessageQueue> msg_queue; 38 std::shared_ptr<AppletMessageQueue> msg_queue;
39 Core::System& system; 39 Core::System& system;
40}; 40};
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index a2ffaa440..7bed86ec4 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,9 +12,9 @@ namespace Service::AM {
12 12
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 15 explicit IApplicationProxy(NVFlinger::NVFlinger& nvflinger,
16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 16 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), 17 : ServiceFramework("IApplicationProxy"), nvflinger(nvflinger),
18 msg_queue(std::move(msg_queue)), system(system) { 18 msg_queue(std::move(msg_queue)), system(system) {
19 // clang-format off 19 // clang-format off
20 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
@@ -98,7 +98,7 @@ private:
98 rb.PushIpcInterface<IApplicationFunctions>(system); 98 rb.PushIpcInterface<IApplicationFunctions>(system);
99 } 99 }
100 100
101 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 101 NVFlinger::NVFlinger& nvflinger;
102 std::shared_ptr<AppletMessageQueue> msg_queue; 102 std::shared_ptr<AppletMessageQueue> msg_queue;
103 Core::System& system; 103 Core::System& system;
104}; 104};
@@ -111,10 +111,10 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system); 111 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
112} 112}
113 113
114AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 114AppletOE::AppletOE(NVFlinger::NVFlinger& nvflinger, std::shared_ptr<AppletMessageQueue> msg_queue,
115 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) 115 Core::System& system)
116 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), 116 : ServiceFramework("appletOE"), nvflinger(nvflinger), msg_queue(std::move(msg_queue)),
117 msg_queue(std::move(msg_queue)), system(system) { 117 system(system) {
118 static const FunctionInfo functions[] = { 118 static const FunctionInfo functions[] = {
119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 119 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
120 }; 120 };
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 758da792d..88906d354 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -23,7 +23,7 @@ class AppletMessageQueue;
23 23
24class AppletOE final : public ServiceFramework<AppletOE> { 24class AppletOE final : public ServiceFramework<AppletOE> {
25public: 25public:
26 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletOE(NVFlinger::NVFlinger& nvflinger,
27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); 27 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
28 ~AppletOE() override; 28 ~AppletOE() override;
29 29
@@ -32,7 +32,7 @@ public:
32private: 32private:
33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 33 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
34 34
35 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 35 NVFlinger::NVFlinger& nvflinger;
36 std::shared_ptr<AppletMessageQueue> msg_queue; 36 std::shared_ptr<AppletMessageQueue> msg_queue;
37 Core::System& system; 37 Core::System& system;
38}; 38};
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index e58b2c518..173b36da4 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -164,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
164 rb.Push(RESULT_SUCCESS); 164 rb.Push(RESULT_SUCCESS);
165 165
166 const auto title_id = system.CurrentProcess()->GetTitleID(); 166 const auto title_id = system.CurrentProcess()->GetTitleID();
167 FileSys::PatchManager pm{title_id}; 167 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
168 system.GetContentProvider()};
168 169
169 const auto res = pm.GetControlMetadata(); 170 const auto res = pm.GetControlMetadata();
170 if (res.first == nullptr) { 171 if (res.first == nullptr) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3cdef4888..2e53cae5b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -455,7 +455,9 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
455 const auto res = system.GetAppLoader().ReadControlData(nacp); 455 const auto res = system.GetAppLoader().ReadControlData(nacp);
456 456
457 if (res != Loader::ResultStatus::Success) { 457 if (res != Loader::ResultStatus::Success) {
458 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; 458 const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
459 system.GetFileSystemController(),
460 system.GetContentProvider()};
459 const auto metadata = pm.GetControlMetadata(); 461 const auto metadata = pm.GetControlMetadata();
460 const auto& nacp_unique = metadata.first; 462 const auto& nacp_unique = metadata.first;
461 463
@@ -728,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
728void InstallInterfaces(Core::System& system) { 730void InstallInterfaces(Core::System& system) {
729 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); 731 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
730 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); 732 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
731 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) 733 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(),
734 system.GetReporter())
732 ->InstallAsService(system.ServiceManager()); 735 ->InstallAsService(system.ServiceManager());
733} 736}
734 737
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 649128be4..031c6dbf6 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -650,8 +650,10 @@ private:
650 u64 next_entry_index = 0; 650 u64 next_entry_index = 0;
651}; 651};
652 652
653FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) 653FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
654 : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { 654 const Core::Reporter& reporter_)
655 : ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_},
656 reporter(reporter_) {
655 // clang-format off 657 // clang-format off
656 static const FunctionInfo functions[] = { 658 static const FunctionInfo functions[] = {
657 {0, nullptr, "OpenFileSystem"}, 659 {0, nullptr, "OpenFileSystem"},
@@ -968,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
968 return; 970 return;
969 } 971 }
970 972
971 FileSys::PatchManager pm{title_id}; 973 const FileSys::PatchManager pm{title_id, fsc, content_provider};
972 974
973 auto storage = std::make_shared<IStorage>( 975 auto storage = std::make_shared<IStorage>(
974 pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); 976 pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4964e874e..6c7239e6a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -12,8 +12,9 @@ class Reporter;
12} 12}
13 13
14namespace FileSys { 14namespace FileSys {
15class ContentProvider;
15class FileSystemBackend; 16class FileSystemBackend;
16} 17} // namespace FileSys
17 18
18namespace Service::FileSystem { 19namespace Service::FileSystem {
19 20
@@ -32,7 +33,8 @@ enum class LogMode : u32 {
32 33
33class FSP_SRV final : public ServiceFramework<FSP_SRV> { 34class FSP_SRV final : public ServiceFramework<FSP_SRV> {
34public: 35public:
35 explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); 36 explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
37 const Core::Reporter& reporter_);
36 ~FSP_SRV() override; 38 ~FSP_SRV() override;
37 39
38private: 40private:
@@ -55,6 +57,7 @@ private:
55 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); 57 void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
56 58
57 FileSystemController& fsc; 59 FileSystemController& fsc;
60 const FileSys::ContentProvider& content_provider;
58 61
59 FileSys::VirtualFile romfs; 62 FileSys::VirtualFile romfs;
60 u64 current_process_id = 0; 63 u64 current_process_id = 0;
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 58ee1f712..2594e6839 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
6#include "core/file_sys/control_metadata.h" 7#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h" 8#include "core/file_sys/patch_manager.h"
8#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
@@ -29,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro
29 30
30IAccountProxyInterface::~IAccountProxyInterface() = default; 31IAccountProxyInterface::~IAccountProxyInterface() = default;
31 32
32IApplicationManagerInterface::IApplicationManagerInterface() 33IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
33 : ServiceFramework{"IApplicationManagerInterface"} { 34 : ServiceFramework{"IApplicationManagerInterface"}, system{system_} {
34 // clang-format off 35 // clang-format off
35 static const FunctionInfo functions[] = { 36 static const FunctionInfo functions[] = {
36 {0, nullptr, "ListApplicationRecord"}, 37 {0, nullptr, "ListApplicationRecord"},
@@ -298,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
298 299
299 const auto size = ctx.GetWriteBufferSize(); 300 const auto size = ctx.GetWriteBufferSize();
300 301
301 const FileSys::PatchManager pm{title_id}; 302 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
303 system.GetContentProvider()};
302 const auto control = pm.GetControlMetadata(); 304 const auto control = pm.GetControlMetadata();
303 305
304 std::vector<u8> out; 306 std::vector<u8> out;
@@ -538,14 +540,14 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
538 540
539IFactoryResetInterface::~IFactoryResetInterface() = default; 541IFactoryResetInterface::~IFactoryResetInterface() = default;
540 542
541NS::NS(const char* name) : ServiceFramework{name} { 543NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} {
542 // clang-format off 544 // clang-format off
543 static const FunctionInfo functions[] = { 545 static const FunctionInfo functions[] = {
544 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 546 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
545 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 547 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
546 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, 548 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
547 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, 549 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
548 {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"}, 550 {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
549 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, 551 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
550 {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"}, 552 {7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
551 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, 553 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
@@ -558,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
558NS::~NS() = default; 560NS::~NS() = default;
559 561
560std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const { 562std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
561 return GetInterface<IApplicationManagerInterface>(); 563 return GetInterface<IApplicationManagerInterface>(system);
562} 564}
563 565
564class NS_DEV final : public ServiceFramework<NS_DEV> { 566class NS_DEV final : public ServiceFramework<NS_DEV> {
@@ -678,11 +680,11 @@ public:
678 680
679void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 681void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
680 682
681 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); 683 std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager);
682 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); 684 std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager);
683 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); 685 std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager);
684 std::make_shared<NS>("ns:rt")->InstallAsService(service_manager); 686 std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager);
685 std::make_shared<NS>("ns:web")->InstallAsService(service_manager); 687 std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager);
686 688
687 std::make_shared<NS_DEV>()->InstallAsService(service_manager); 689 std::make_shared<NS_DEV>()->InstallAsService(service_manager);
688 std::make_shared<NS_SU>()->InstallAsService(service_manager); 690 std::make_shared<NS_SU>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index c2554b878..c90ccd755 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,6 +6,10 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service { 13namespace Service {
10 14
11namespace FileSystem { 15namespace FileSystem {
@@ -22,7 +26,7 @@ public:
22 26
23class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { 27class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
24public: 28public:
25 explicit IApplicationManagerInterface(); 29 explicit IApplicationManagerInterface(Core::System& system_);
26 ~IApplicationManagerInterface() override; 30 ~IApplicationManagerInterface() override;
27 31
28 ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages); 32 ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
@@ -32,6 +36,8 @@ private:
32 void GetApplicationControlData(Kernel::HLERequestContext& ctx); 36 void GetApplicationControlData(Kernel::HLERequestContext& ctx);
33 void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx); 37 void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
34 void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx); 38 void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
39
40 Core::System& system;
35}; 41};
36 42
37class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { 43class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -72,13 +78,13 @@ public:
72 78
73class NS final : public ServiceFramework<NS> { 79class NS final : public ServiceFramework<NS> {
74public: 80public:
75 explicit NS(const char* name); 81 explicit NS(const char* name, Core::System& system_);
76 ~NS() override; 82 ~NS() override;
77 83
78 std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const; 84 std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
79 85
80private: 86private:
81 template <typename T> 87 template <typename T, typename... Args>
82 void PushInterface(Kernel::HLERequestContext& ctx) { 88 void PushInterface(Kernel::HLERequestContext& ctx) {
83 LOG_DEBUG(Service_NS, "called"); 89 LOG_DEBUG(Service_NS, "called");
84 90
@@ -87,13 +93,23 @@ private:
87 rb.PushIpcInterface<T>(); 93 rb.PushIpcInterface<T>();
88 } 94 }
89 95
90 template <typename T> 96 void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) {
91 std::shared_ptr<T> GetInterface() const { 97 LOG_DEBUG(Service_NS, "called");
98
99 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
100 rb.Push(RESULT_SUCCESS);
101 rb.PushIpcInterface<IApplicationManagerInterface>(system);
102 }
103
104 template <typename T, typename... Args>
105 std::shared_ptr<T> GetInterface(Args&&... args) const {
92 static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, 106 static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
93 "Not a base of ServiceFrameworkBase"); 107 "Not a base of ServiceFrameworkBase");
94 108
95 return std::make_shared<T>(); 109 return std::make_shared<T>(std::forward<Args>(args)...);
96 } 110 }
111
112 Core::System& system;
97}; 113};
98 114
99/// Registers all NS services with the specified service manager. 115/// Registers all NS services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 0240d6643..5681599ba 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -24,25 +24,37 @@ public:
24 explicit nvdevice(Core::System& system) : system{system} {} 24 explicit nvdevice(Core::System& system) : system{system} {}
25 virtual ~nvdevice() = default; 25 virtual ~nvdevice() = default;
26 26
27 union Ioctl { 27 /**
28 u32_le raw; 28 * Handles an ioctl1 request.
29 BitField<0, 8, u32> cmd; 29 * @param command The ioctl command id.
30 BitField<8, 8, u32> group; 30 * @param input A buffer containing the input data for the ioctl.
31 BitField<16, 14, u32> length; 31 * @param output A buffer where the output data will be written to.
32 BitField<30, 1, u32> is_in; 32 * @returns The result code of the ioctl.
33 BitField<31, 1, u32> is_out; 33 */
34 }; 34 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
35 std::vector<u8>& output) = 0;
36
37 /**
38 * Handles an ioctl2 request.
39 * @param command The ioctl command id.
40 * @param input A buffer containing the input data for the ioctl.
41 * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
42 * @param output A buffer where the output data will be written to.
43 * @returns The result code of the ioctl.
44 */
45 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
46 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
35 47
36 /** 48 /**
37 * Handles an ioctl request. 49 * Handles an ioctl3 request.
38 * @param command The ioctl command id. 50 * @param command The ioctl command id.
39 * @param input A buffer containing the input data for the ioctl. 51 * @param input A buffer containing the input data for the ioctl.
40 * @param output A buffer where the output data will be written to. 52 * @param output A buffer where the output data will be written to.
53 * @param inline_output A buffer where the inlined output data will be written to.
41 * @returns The result code of the ioctl. 54 * @returns The result code of the ioctl.
42 */ 55 */
43 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 56 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
44 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 57 std::vector<u8>& inline_output) = 0;
45 IoctlVersion version) = 0;
46 58
47protected: 59protected:
48 Core::System& system; 60 Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 3f7b8e670..ce615c758 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -18,11 +18,22 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 18 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
19nvdisp_disp0 ::~nvdisp_disp0() = default; 19nvdisp_disp0 ::~nvdisp_disp0() = default;
20 20
21u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 std::vector<u8>& output) {
23 IoctlVersion version) { 23 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
24 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 24 return NvResult::NotImplemented;
25 return 0; 25}
26
27NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
28 const std::vector<u8>& inline_input, std::vector<u8>& output) {
29 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
30 return NvResult::NotImplemented;
31}
32
33NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
34 std::vector<u8>& inline_output) {
35 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
36 return NvResult::NotImplemented;
26} 37}
27 38
28void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 39void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 6fcdeee84..55a33b7e4 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,9 +20,11 @@ public:
20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 24 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
25 IoctlVersion version) override; 25 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
26 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
27 std::vector<u8>& inline_output) override;
26 28
27 /// Performs a screen flip, drawing the buffer pointed to by the handle. 29 /// Performs a screen flip, drawing the buffer pointed to by the handle.
28 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 30 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index f2529a12e..6b062e10e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -17,59 +17,77 @@
17 17
18namespace Service::Nvidia::Devices { 18namespace Service::Nvidia::Devices {
19 19
20namespace NvErrCodes {
21constexpr u32 Success{};
22constexpr u32 OutOfMemory{static_cast<u32>(-12)};
23constexpr u32 InvalidInput{static_cast<u32>(-22)};
24} // namespace NvErrCodes
25
26nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 20nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
27 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 21 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
28nvhost_as_gpu::~nvhost_as_gpu() = default; 22nvhost_as_gpu::~nvhost_as_gpu() = default;
29 23
30u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 24NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
31 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 25 std::vector<u8>& output) {
32 IoctlVersion version) { 26 switch (command.group) {
33 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 27 case 'A':
34 command.raw, input.size(), output.size()); 28 switch (command.cmd) {
35 29 case 0x1:
36 switch (static_cast<IoctlCommand>(command.raw)) { 30 return BindChannel(input, output);
37 case IoctlCommand::IocInitalizeExCommand: 31 case 0x2:
38 return InitalizeEx(input, output); 32 return AllocateSpace(input, output);
39 case IoctlCommand::IocAllocateSpaceCommand: 33 case 0x3:
40 return AllocateSpace(input, output); 34 return FreeSpace(input, output);
41 case IoctlCommand::IocMapBufferExCommand: 35 case 0x5:
42 return MapBufferEx(input, output); 36 return UnmapBuffer(input, output);
43 case IoctlCommand::IocBindChannelCommand: 37 case 0x6:
44 return BindChannel(input, output); 38 return MapBufferEx(input, output);
45 case IoctlCommand::IocGetVaRegionsCommand: 39 case 0x8:
46 return GetVARegions(input, output); 40 return GetVARegions(input, output);
47 case IoctlCommand::IocUnmapBufferCommand: 41 case 0x9:
48 return UnmapBuffer(input, output); 42 return InitalizeEx(input, output);
49 case IoctlCommand::IocFreeSpaceCommand: 43 case 0x14:
50 return FreeSpace(input, output); 44 return Remap(input, output);
45 default:
46 break;
47 }
48 break;
51 default: 49 default:
52 break; 50 break;
53 } 51 }
54 52
55 if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { 53 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
56 return Remap(input, output); 54 return NvResult::NotImplemented;
57 } 55}
56
57NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
58 const std::vector<u8>& inline_input, std::vector<u8>& output) {
59 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
60 return NvResult::NotImplemented;
61}
58 62
59 UNIMPLEMENTED_MSG("Unimplemented ioctl command"); 63NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
60 return 0; 64 std::vector<u8>& inline_output) {
65 switch (command.group) {
66 case 'A':
67 switch (command.cmd) {
68 case 0x8:
69 return GetVARegions(input, output, inline_output);
70 default:
71 break;
72 }
73 break;
74 default:
75 break;
76 }
77 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
78 return NvResult::NotImplemented;
61} 79}
62 80
63u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { 81NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
64 IoctlInitalizeEx params{}; 82 IoctlInitalizeEx params{};
65 std::memcpy(&params, input.data(), input.size()); 83 std::memcpy(&params, input.data(), input.size());
66 84
67 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 85 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
68 86
69 return 0; 87 return NvResult::Success;
70} 88}
71 89
72u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { 90NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
73 IoctlAllocSpace params{}; 91 IoctlAllocSpace params{};
74 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
75 93
@@ -83,17 +101,17 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
83 params.offset = system.GPU().MemoryManager().Allocate(size, params.align); 101 params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
84 } 102 }
85 103
86 auto result{NvErrCodes::Success}; 104 auto result = NvResult::Success;
87 if (!params.offset) { 105 if (!params.offset) {
88 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); 106 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
89 result = NvErrCodes::OutOfMemory; 107 result = NvResult::InsufficientMemory;
90 } 108 }
91 109
92 std::memcpy(output.data(), &params, output.size()); 110 std::memcpy(output.data(), &params, output.size());
93 return result; 111 return result;
94} 112}
95 113
96u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 114NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
97 IoctlFreeSpace params{}; 115 IoctlFreeSpace params{};
98 std::memcpy(&params, input.data(), input.size()); 116 std::memcpy(&params, input.data(), input.size());
99 117
@@ -104,15 +122,15 @@ u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& outp
104 static_cast<std::size_t>(params.pages) * params.page_size); 122 static_cast<std::size_t>(params.pages) * params.page_size);
105 123
106 std::memcpy(output.data(), &params, output.size()); 124 std::memcpy(output.data(), &params, output.size());
107 return NvErrCodes::Success; 125 return NvResult::Success;
108} 126}
109 127
110u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 128NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
111 const auto num_entries = input.size() / sizeof(IoctlRemapEntry); 129 const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
112 130
113 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 131 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
114 132
115 auto result{NvErrCodes::Success}; 133 auto result = NvResult::Success;
116 std::vector<IoctlRemapEntry> entries(num_entries); 134 std::vector<IoctlRemapEntry> entries(num_entries);
117 std::memcpy(entries.data(), input.data(), input.size()); 135 std::memcpy(entries.data(), input.data(), input.size());
118 136
@@ -123,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
123 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; 141 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
124 if (!object) { 142 if (!object) {
125 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); 143 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
126 result = NvErrCodes::InvalidInput; 144 result = NvResult::InvalidState;
127 break; 145 break;
128 } 146 }
129 147
@@ -134,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
134 152
135 if (!addr) { 153 if (!addr) {
136 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); 154 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
137 result = NvErrCodes::InvalidInput; 155 result = NvResult::InvalidState;
138 break; 156 break;
139 } 157 }
140 } 158 }
@@ -143,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
143 return result; 161 return result;
144} 162}
145 163
146u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
147 IoctlMapBufferEx params{}; 165 IoctlMapBufferEx params{};
148 std::memcpy(&params, input.data(), input.size()); 166 std::memcpy(&params, input.data(), input.size());
149 167
@@ -157,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
157 if (!object) { 175 if (!object) {
158 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); 176 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
159 std::memcpy(output.data(), &params, output.size()); 177 std::memcpy(output.data(), &params, output.size());
160 return NvErrCodes::InvalidInput; 178 return NvResult::InvalidState;
161 } 179 }
162 180
163 // The real nvservices doesn't make a distinction between handles and ids, and 181 // The real nvservices doesn't make a distinction between handles and ids, and
@@ -184,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
184 params.mapping_size, params.offset); 202 params.mapping_size, params.offset);
185 203
186 std::memcpy(output.data(), &params, output.size()); 204 std::memcpy(output.data(), &params, output.size());
187 return NvErrCodes::InvalidInput; 205 return NvResult::InvalidState;
188 } 206 }
189 207
190 std::memcpy(output.data(), &params, output.size()); 208 std::memcpy(output.data(), &params, output.size());
191 return NvErrCodes::Success; 209 return NvResult::Success;
192 } else { 210 } else {
193 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); 211 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
194 212
195 std::memcpy(output.data(), &params, output.size()); 213 std::memcpy(output.data(), &params, output.size());
196 return NvErrCodes::InvalidInput; 214 return NvResult::InvalidState;
197 } 215 }
198 } 216 }
199 217
@@ -213,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
213 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); 231 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
214 } 232 }
215 233
216 auto result{NvErrCodes::Success}; 234 auto result = NvResult::Success;
217 if (!params.offset) { 235 if (!params.offset) {
218 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); 236 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
219 result = NvErrCodes::InvalidInput; 237 result = NvResult::InvalidState;
220 } else { 238 } else {
221 AddBufferMap(params.offset, size, physical_address, is_alloc); 239 AddBufferMap(params.offset, size, physical_address, is_alloc);
222 } 240 }
@@ -225,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
225 return result; 243 return result;
226} 244}
227 245
228u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 246NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
229 IoctlUnmapBuffer params{}; 247 IoctlUnmapBuffer params{};
230 std::memcpy(&params, input.data(), input.size()); 248 std::memcpy(&params, input.data(), input.size());
231 249
@@ -238,20 +256,42 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
238 } 256 }
239 257
240 std::memcpy(output.data(), &params, output.size()); 258 std::memcpy(output.data(), &params, output.size());
241 return NvErrCodes::Success; 259 return NvResult::Success;
242} 260}
243 261
244u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 262NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
245 IoctlBindChannel params{}; 263 IoctlBindChannel params{};
246 std::memcpy(&params, input.data(), input.size()); 264 std::memcpy(&params, input.data(), input.size());
247 265 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
248 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
249 266
250 channel = params.fd; 267 channel = params.fd;
251 return 0; 268 return NvResult::Success;
269}
270
271NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
272 IoctlGetVaRegions params{};
273 std::memcpy(&params, input.data(), input.size());
274
275 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
276 params.buf_size);
277
278 params.buf_size = 0x30;
279 params.regions[0].offset = 0x04000000;
280 params.regions[0].page_size = 0x1000;
281 params.regions[0].pages = 0x3fbfff;
282
283 params.regions[1].offset = 0x04000000;
284 params.regions[1].page_size = 0x10000;
285 params.regions[1].pages = 0x1bffff;
286
287 // TODO(ogniK): This probably can stay stubbed but should add support way way later
288
289 std::memcpy(output.data(), &params, output.size());
290 return NvResult::Success;
252} 291}
253 292
254u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 293NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
294 std::vector<u8>& inline_output) {
255 IoctlGetVaRegions params{}; 295 IoctlGetVaRegions params{};
256 std::memcpy(&params, input.data(), input.size()); 296 std::memcpy(&params, input.data(), input.size());
257 297
@@ -270,7 +310,8 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
270 // TODO(ogniK): This probably can stay stubbed but should add support way way later 310 // TODO(ogniK): This probably can stay stubbed but should add support way way later
271 311
272 std::memcpy(output.data(), &params, output.size()); 312 std::memcpy(output.data(), &params, output.size());
273 return 0; 313 std::memcpy(inline_output.data(), &params.regions, inline_output.size());
314 return NvResult::Success;
274} 315}
275 316
276std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { 317std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index fcdb40d93..08035fa0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -30,9 +30,11 @@ public:
30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 30 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
31 ~nvhost_as_gpu() override; 31 ~nvhost_as_gpu() override;
32 32
33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 33 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
34 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 34 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
35 IoctlVersion version) override; 35 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
36 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
37 std::vector<u8>& inline_output) override;
36 38
37private: 39private:
38 class BufferMap final { 40 class BufferMap final {
@@ -74,32 +76,21 @@ private:
74 bool is_allocated{}; 76 bool is_allocated{};
75 }; 77 };
76 78
77 enum class IoctlCommand : u32_le {
78 IocInitalizeExCommand = 0x40284109,
79 IocAllocateSpaceCommand = 0xC0184102,
80 IocRemapCommand = 0x00000014,
81 IocMapBufferExCommand = 0xC0284106,
82 IocBindChannelCommand = 0x40044101,
83 IocGetVaRegionsCommand = 0xC0404108,
84 IocUnmapBufferCommand = 0xC0084105,
85 IocFreeSpaceCommand = 0xC0104103,
86 };
87
88 struct IoctlInitalizeEx { 79 struct IoctlInitalizeEx {
89 u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default 80 u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
90 s32_le as_fd; // ignored; passes 0 81 s32_le as_fd{}; // ignored; passes 0
91 u32_le flags; // passes 0 82 u32_le flags{}; // passes 0
92 u32_le reserved; // ignored; passes 0 83 u32_le reserved{}; // ignored; passes 0
93 u64_le unk0; 84 u64_le unk0{};
94 u64_le unk1; 85 u64_le unk1{};
95 u64_le unk2; 86 u64_le unk2{};
96 }; 87 };
97 static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size"); 88 static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
98 89
99 struct IoctlAllocSpace { 90 struct IoctlAllocSpace {
100 u32_le pages; 91 u32_le pages{};
101 u32_le page_size; 92 u32_le page_size{};
102 AddressSpaceFlags flags; 93 AddressSpaceFlags flags{};
103 INSERT_PADDING_WORDS(1); 94 INSERT_PADDING_WORDS(1);
104 union { 95 union {
105 u64_le offset; 96 u64_le offset;
@@ -109,70 +100,73 @@ private:
109 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); 100 static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
110 101
111 struct IoctlFreeSpace { 102 struct IoctlFreeSpace {
112 u64_le offset; 103 u64_le offset{};
113 u32_le pages; 104 u32_le pages{};
114 u32_le page_size; 105 u32_le page_size{};
115 }; 106 };
116 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); 107 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
117 108
118 struct IoctlRemapEntry { 109 struct IoctlRemapEntry {
119 u16_le flags; 110 u16_le flags{};
120 u16_le kind; 111 u16_le kind{};
121 u32_le nvmap_handle; 112 u32_le nvmap_handle{};
122 u32_le map_offset; 113 u32_le map_offset{};
123 u32_le offset; 114 u32_le offset{};
124 u32_le pages; 115 u32_le pages{};
125 }; 116 };
126 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); 117 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
127 118
128 struct IoctlMapBufferEx { 119 struct IoctlMapBufferEx {
129 AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable 120 AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
130 u32_le kind; // -1 is default 121 u32_le kind{}; // -1 is default
131 u32_le nvmap_handle; 122 u32_le nvmap_handle{};
132 u32_le page_size; // 0 means don't care 123 u32_le page_size{}; // 0 means don't care
133 s64_le buffer_offset; 124 s64_le buffer_offset{};
134 u64_le mapping_size; 125 u64_le mapping_size{};
135 s64_le offset; 126 s64_le offset{};
136 }; 127 };
137 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); 128 static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
138 129
139 struct IoctlUnmapBuffer { 130 struct IoctlUnmapBuffer {
140 s64_le offset; 131 s64_le offset{};
141 }; 132 };
142 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); 133 static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
143 134
144 struct IoctlBindChannel { 135 struct IoctlBindChannel {
145 u32_le fd; 136 s32_le fd{};
146 }; 137 };
147 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); 138 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
148 139
149 struct IoctlVaRegion { 140 struct IoctlVaRegion {
150 u64_le offset; 141 u64_le offset{};
151 u32_le page_size; 142 u32_le page_size{};
152 INSERT_PADDING_WORDS(1); 143 INSERT_PADDING_WORDS(1);
153 u64_le pages; 144 u64_le pages{};
154 }; 145 };
155 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); 146 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
156 147
157 struct IoctlGetVaRegions { 148 struct IoctlGetVaRegions {
158 u64_le buf_addr; // (contained output user ptr on linux, ignored) 149 u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
159 u32_le buf_size; // forced to 2*sizeof(struct va_region) 150 u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
160 u32_le reserved; 151 u32_le reserved{};
161 IoctlVaRegion regions[2]; 152 IoctlVaRegion regions[2]{};
162 }; 153 };
163 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, 154 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
164 "IoctlGetVaRegions is incorrect size"); 155 "IoctlGetVaRegions is incorrect size");
165 156
166 u32 channel{}; 157 s32 channel{};
158
159 NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
160 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
161 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
162 NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
163 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
164 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
165 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
167 166
168 u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
169 u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
170 u32 Remap(const std::vector<u8>& input, std::vector<u8>& output); 169 std::vector<u8>& inline_output);
171 u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
173 u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
174 u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
175 u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
176 170
177 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 171 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
178 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 172 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 8356a8139..d90cf90a8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -20,41 +20,54 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} 20 : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
21nvhost_ctrl::~nvhost_ctrl() = default; 21nvhost_ctrl::~nvhost_ctrl() = default;
22 22
23u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 23NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 24 switch (command.group) {
25 IoctlVersion version) { 25 case 0x0:
26 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 26 switch (command.cmd) {
27 command.raw, input.size(), output.size()); 27 case 0x1b:
28 28 return NvOsGetConfigU32(input, output);
29 switch (static_cast<IoctlCommand>(command.raw)) { 29 case 0x1c:
30 case IoctlCommand::IocGetConfigCommand: 30 return IocCtrlClearEventWait(input, output);
31 return NvOsGetConfigU32(input, output); 31 case 0x1d:
32 case IoctlCommand::IocCtrlEventWaitCommand: 32 return IocCtrlEventWait(input, output, false);
33 return IocCtrlEventWait(input, output, false, ctrl); 33 case 0x1e:
34 case IoctlCommand::IocCtrlEventWaitAsyncCommand: 34 return IocCtrlEventWait(input, output, true);
35 return IocCtrlEventWait(input, output, true, ctrl); 35 case 0x1f:
36 case IoctlCommand::IocCtrlEventRegisterCommand: 36 return IocCtrlEventRegister(input, output);
37 return IocCtrlEventRegister(input, output); 37 case 0x20:
38 case IoctlCommand::IocCtrlEventUnregisterCommand: 38 return IocCtrlEventUnregister(input, output);
39 return IocCtrlEventUnregister(input, output); 39 }
40 case IoctlCommand::IocCtrlClearEventWaitCommand: 40 break;
41 return IocCtrlClearEventWait(input, output);
42 default: 41 default:
43 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 42 break;
44 return 0;
45 } 43 }
44
45 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
46 return NvResult::NotImplemented;
47}
48
49NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
50 const std::vector<u8>& inline_input, std::vector<u8>& output) {
51 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
52 return NvResult::NotImplemented;
53}
54
55NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
56 std::vector<u8>& inline_output) {
57 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
58 return NvResult::NotImplemented;
46} 59}
47 60
48u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 61NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
49 IocGetConfigParams params{}; 62 IocGetConfigParams params{};
50 std::memcpy(&params, input.data(), sizeof(params)); 63 std::memcpy(&params, input.data(), sizeof(params));
51 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), 64 LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
52 params.param_str.data()); 65 params.param_str.data());
53 return 0x30006; // Returns error on production mode 66 return NvResult::ConfigVarNotFound; // Returns error on production mode
54} 67}
55 68
56u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 69NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
57 bool is_async, IoctlCtrl& ctrl) { 70 bool is_async) {
58 IocCtrlEventWaitParams params{}; 71 IocCtrlEventWaitParams params{};
59 std::memcpy(&params, input.data(), sizeof(params)); 72 std::memcpy(&params, input.data(), sizeof(params));
60 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 73 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -126,10 +139,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
126 params.value |= event_id; 139 params.value |= event_id;
127 event.event.writable->Clear(); 140 event.event.writable->Clear();
128 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 141 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
129 if (!is_async && ctrl.fresh_call) { 142 if (!is_async) {
130 ctrl.must_delay = true;
131 ctrl.timeout = params.timeout;
132 ctrl.event_id = event_id;
133 return NvResult::Timeout; 143 return NvResult::Timeout;
134 } 144 }
135 std::memcpy(output.data(), &params, sizeof(params)); 145 std::memcpy(output.data(), &params, sizeof(params));
@@ -139,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
139 return NvResult::BadParameter; 149 return NvResult::BadParameter;
140} 150}
141 151
142u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 152NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
143 IocCtrlEventRegisterParams params{}; 153 IocCtrlEventRegisterParams params{};
144 std::memcpy(&params, input.data(), sizeof(params)); 154 std::memcpy(&params, input.data(), sizeof(params));
145 const u32 event_id = params.user_event_id & 0x00FF; 155 const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
154 return NvResult::Success; 164 return NvResult::Success;
155} 165}
156 166
157u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { 167NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
168 std::vector<u8>& output) {
158 IocCtrlEventUnregisterParams params{}; 169 IocCtrlEventUnregisterParams params{};
159 std::memcpy(&params, input.data(), sizeof(params)); 170 std::memcpy(&params, input.data(), sizeof(params));
160 const u32 event_id = params.user_event_id & 0x00FF; 171 const u32 event_id = params.user_event_id & 0x00FF;
@@ -169,7 +180,7 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
169 return NvResult::Success; 180 return NvResult::Success;
170} 181}
171 182
172u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 183NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
173 IocCtrlEventSignalParams params{}; 184 IocCtrlEventSignalParams params{};
174 std::memcpy(&params, input.data(), sizeof(params)); 185 std::memcpy(&params, input.data(), sizeof(params));
175 186
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 24ad96cb9..c5aa1362a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -18,132 +18,113 @@ public:
18 SyncpointManager& syncpoint_manager); 18 SyncpointManager& syncpoint_manager);
19 ~nvhost_ctrl() override; 19 ~nvhost_ctrl() override;
20 20
21 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
23 IoctlVersion version) override; 23 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
24 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
25 std::vector<u8>& inline_output) override;
24 26
25private: 27private:
26 enum class IoctlCommand : u32_le {
27 IocSyncptReadCommand = 0xC0080014,
28 IocSyncptIncrCommand = 0x40040015,
29 IocSyncptWaitCommand = 0xC00C0016,
30 IocModuleMutexCommand = 0x40080017,
31 IocModuleRegRDWRCommand = 0xC0180018,
32 IocSyncptWaitexCommand = 0xC0100019,
33 IocSyncptReadMaxCommand = 0xC008001A,
34 IocGetConfigCommand = 0xC183001B,
35 IocCtrlClearEventWaitCommand = 0xC004001C,
36 IocCtrlEventWaitCommand = 0xC010001D,
37 IocCtrlEventWaitAsyncCommand = 0xC010001E,
38 IocCtrlEventRegisterCommand = 0xC004001F,
39 IocCtrlEventUnregisterCommand = 0xC0040020,
40 IocCtrlEventKillCommand = 0x40080021,
41 };
42 struct IocSyncptReadParams { 28 struct IocSyncptReadParams {
43 u32_le id; 29 u32_le id{};
44 u32_le value; 30 u32_le value{};
45 }; 31 };
46 static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size"); 32 static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
47 33
48 struct IocSyncptIncrParams { 34 struct IocSyncptIncrParams {
49 u32_le id; 35 u32_le id{};
50 }; 36 };
51 static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size"); 37 static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
52 38
53 struct IocSyncptWaitParams { 39 struct IocSyncptWaitParams {
54 u32_le id; 40 u32_le id{};
55 u32_le thresh; 41 u32_le thresh{};
56 s32_le timeout; 42 s32_le timeout{};
57 }; 43 };
58 static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size"); 44 static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
59 45
60 struct IocModuleMutexParams { 46 struct IocModuleMutexParams {
61 u32_le id; 47 u32_le id{};
62 u32_le lock; // (0 = unlock and 1 = lock) 48 u32_le lock{}; // (0 = unlock and 1 = lock)
63 }; 49 };
64 static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size"); 50 static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
65 51
66 struct IocModuleRegRDWRParams { 52 struct IocModuleRegRDWRParams {
67 u32_le id; 53 u32_le id{};
68 u32_le num_offsets; 54 u32_le num_offsets{};
69 u32_le block_size; 55 u32_le block_size{};
70 u32_le offsets; 56 u32_le offsets{};
71 u32_le values; 57 u32_le values{};
72 u32_le write; 58 u32_le write{};
73 }; 59 };
74 static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size"); 60 static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
75 61
76 struct IocSyncptWaitexParams { 62 struct IocSyncptWaitexParams {
77 u32_le id; 63 u32_le id{};
78 u32_le thresh; 64 u32_le thresh{};
79 s32_le timeout; 65 s32_le timeout{};
80 u32_le value; 66 u32_le value{};
81 }; 67 };
82 static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size"); 68 static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
83 69
84 struct IocSyncptReadMaxParams { 70 struct IocSyncptReadMaxParams {
85 u32_le id; 71 u32_le id{};
86 u32_le value; 72 u32_le value{};
87 }; 73 };
88 static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size"); 74 static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
89 75
90 struct IocGetConfigParams { 76 struct IocGetConfigParams {
91 std::array<char, 0x41> domain_str; 77 std::array<char, 0x41> domain_str{};
92 std::array<char, 0x41> param_str; 78 std::array<char, 0x41> param_str{};
93 std::array<char, 0x101> config_str; 79 std::array<char, 0x101> config_str{};
94 }; 80 };
95 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 81 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
96 82
97 struct IocCtrlEventSignalParams { 83 struct IocCtrlEventSignalParams {
98 u32_le event_id; 84 u32_le event_id{};
99 }; 85 };
100 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 86 static_assert(sizeof(IocCtrlEventSignalParams) == 4,
101 "IocCtrlEventSignalParams is incorrect size"); 87 "IocCtrlEventSignalParams is incorrect size");
102 88
103 struct IocCtrlEventWaitParams { 89 struct IocCtrlEventWaitParams {
104 u32_le syncpt_id; 90 u32_le syncpt_id{};
105 u32_le threshold; 91 u32_le threshold{};
106 s32_le timeout; 92 s32_le timeout{};
107 u32_le value; 93 u32_le value{};
108 }; 94 };
109 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); 95 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
110 96
111 struct IocCtrlEventWaitAsyncParams { 97 struct IocCtrlEventWaitAsyncParams {
112 u32_le syncpt_id; 98 u32_le syncpt_id{};
113 u32_le threshold; 99 u32_le threshold{};
114 u32_le timeout; 100 u32_le timeout{};
115 u32_le value; 101 u32_le value{};
116 }; 102 };
117 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, 103 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
118 "IocCtrlEventWaitAsyncParams is incorrect size"); 104 "IocCtrlEventWaitAsyncParams is incorrect size");
119 105
120 struct IocCtrlEventRegisterParams { 106 struct IocCtrlEventRegisterParams {
121 u32_le user_event_id; 107 u32_le user_event_id{};
122 }; 108 };
123 static_assert(sizeof(IocCtrlEventRegisterParams) == 4, 109 static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
124 "IocCtrlEventRegisterParams is incorrect size"); 110 "IocCtrlEventRegisterParams is incorrect size");
125 111
126 struct IocCtrlEventUnregisterParams { 112 struct IocCtrlEventUnregisterParams {
127 u32_le user_event_id; 113 u32_le user_event_id{};
128 }; 114 };
129 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, 115 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
130 "IocCtrlEventUnregisterParams is incorrect size"); 116 "IocCtrlEventUnregisterParams is incorrect size");
131 117
132 struct IocCtrlEventKill { 118 struct IocCtrlEventKill {
133 u64_le user_events; 119 u64_le user_events{};
134 }; 120 };
135 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 121 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
136 122
137 u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 123 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
138 124 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
139 u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, 125 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
140 IoctlCtrl& ctrl); 126 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
141 127 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
142 u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
143
144 u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
145
146 u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
147 128
148 EventInterface& events_interface; 129 EventInterface& events_interface;
149 SyncpointManager& syncpoint_manager; 130 SyncpointManager& syncpoint_manager;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index fba89e7a6..2d7ea433c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,39 +15,66 @@ namespace Service::Nvidia::Devices {
15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 const std::vector<u8>& input2, std::vector<u8>& output, 19 std::vector<u8>& output) {
20 std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) { 20 switch (command.group) {
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 case 'G':
22 command.raw, input.size(), output.size()); 22 switch (command.cmd) {
23 23 case 0x1:
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 return ZCullGetCtxSize(input, output);
25 case IoctlCommand::IocGetCharacteristicsCommand: 25 case 0x2:
26 return GetCharacteristics(input, output, output2, version); 26 return ZCullGetInfo(input, output);
27 case IoctlCommand::IocGetTPCMasksCommand: 27 case 0x3:
28 return GetTPCMasks(input, output, output2, version); 28 return ZBCSetTable(input, output);
29 case IoctlCommand::IocGetActiveSlotMaskCommand: 29 case 0x4:
30 return GetActiveSlotMask(input, output); 30 return ZBCQueryTable(input, output);
31 case IoctlCommand::IocZcullGetCtxSizeCommand: 31 case 0x5:
32 return ZCullGetCtxSize(input, output); 32 return GetCharacteristics(input, output);
33 case IoctlCommand::IocZcullGetInfo: 33 case 0x6:
34 return ZCullGetInfo(input, output); 34 return GetTPCMasks(input, output);
35 case IoctlCommand::IocZbcSetTable: 35 case 0x7:
36 return ZBCSetTable(input, output); 36 return FlushL2(input, output);
37 case IoctlCommand::IocZbcQueryTable: 37 case 0x14:
38 return ZBCQueryTable(input, output); 38 return GetActiveSlotMask(input, output);
39 case IoctlCommand::IocFlushL2: 39 case 0x1c:
40 return FlushL2(input, output); 40 return GetGpuTime(input, output);
41 case IoctlCommand::IocGetGpuTime: 41 default:
42 return GetGpuTime(input, output); 42 break;
43 }
44 break;
45 }
46 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return NvResult::NotImplemented;
48}
49
50NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
53 return NvResult::NotImplemented;
54}
55
56NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
57 std::vector<u8>& output, std::vector<u8>& inline_output) {
58 switch (command.group) {
59 case 'G':
60 switch (command.cmd) {
61 case 0x5:
62 return GetCharacteristics(input, output, inline_output);
63 case 0x6:
64 return GetTPCMasks(input, output, inline_output);
65 default:
66 break;
67 }
68 break;
43 default: 69 default:
44 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 70 break;
45 return 0;
46 } 71 }
72 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
73 return NvResult::NotImplemented;
47} 74}
48 75
49u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 76NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
50 std::vector<u8>& output2, IoctlVersion version) { 77 std::vector<u8>& output) {
51 LOG_DEBUG(Service_NVDRV, "called"); 78 LOG_DEBUG(Service_NVDRV, "called");
52 IoctlCharacteristics params{}; 79 IoctlCharacteristics params{};
53 std::memcpy(&params, input.data(), input.size()); 80 std::memcpy(&params, input.data(), input.size());
@@ -88,36 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
88 params.gc.gr_compbit_store_base_hw = 0x0; 115 params.gc.gr_compbit_store_base_hw = 0x0;
89 params.gpu_characteristics_buf_size = 0xA0; 116 params.gpu_characteristics_buf_size = 0xA0;
90 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 117 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
118 std::memcpy(output.data(), &params, output.size());
119 return NvResult::Success;
120}
91 121
92 if (version == IoctlVersion::Version3) { 122NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
93 std::memcpy(output.data(), input.data(), output.size()); 123 std::vector<u8>& inline_output) {
94 std::memcpy(output2.data(), &params.gc, output2.size()); 124 LOG_DEBUG(Service_NVDRV, "called");
95 } else { 125 IoctlCharacteristics params{};
96 std::memcpy(output.data(), &params, output.size()); 126 std::memcpy(&params, input.data(), input.size());
97 } 127 params.gc.arch = 0x120;
98 return 0; 128 params.gc.impl = 0xb;
129 params.gc.rev = 0xa1;
130 params.gc.num_gpc = 0x1;
131 params.gc.l2_cache_size = 0x40000;
132 params.gc.on_board_video_memory_size = 0x0;
133 params.gc.num_tpc_per_gpc = 0x2;
134 params.gc.bus_type = 0x20;
135 params.gc.big_page_size = 0x20000;
136 params.gc.compression_page_size = 0x20000;
137 params.gc.pde_coverage_bit_count = 0x1B;
138 params.gc.available_big_page_sizes = 0x30000;
139 params.gc.gpc_mask = 0x1;
140 params.gc.sm_arch_sm_version = 0x503;
141 params.gc.sm_arch_spa_version = 0x503;
142 params.gc.sm_arch_warp_count = 0x80;
143 params.gc.gpu_va_bit_count = 0x28;
144 params.gc.reserved = 0x0;
145 params.gc.flags = 0x55;
146 params.gc.twod_class = 0x902D;
147 params.gc.threed_class = 0xB197;
148 params.gc.compute_class = 0xB1C0;
149 params.gc.gpfifo_class = 0xB06F;
150 params.gc.inline_to_memory_class = 0xA140;
151 params.gc.dma_copy_class = 0xB0B5;
152 params.gc.max_fbps_count = 0x1;
153 params.gc.fbp_en_mask = 0x0;
154 params.gc.max_ltc_per_fbp = 0x2;
155 params.gc.max_lts_per_ltc = 0x1;
156 params.gc.max_tex_per_tpc = 0x0;
157 params.gc.max_gpc_count = 0x1;
158 params.gc.rop_l2_en_mask_0 = 0x21D70;
159 params.gc.rop_l2_en_mask_1 = 0x0;
160 params.gc.chipname = 0x6230326D67;
161 params.gc.gr_compbit_store_base_hw = 0x0;
162 params.gpu_characteristics_buf_size = 0xA0;
163 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
164
165 std::memcpy(output.data(), input.data(), output.size());
166 std::memcpy(inline_output.data(), &params.gc, inline_output.size());
167 return NvResult::Success;
99} 168}
100 169
101u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, 170NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
102 std::vector<u8>& output2, IoctlVersion version) {
103 IoctlGpuGetTpcMasksArgs params{}; 171 IoctlGpuGetTpcMasksArgs params{};
104 std::memcpy(&params, input.data(), input.size()); 172 std::memcpy(&params, input.data(), input.size());
105 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size); 173 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
106 if (params.mask_buffer_size != 0) { 174 if (params.mask_buffer_size != 0) {
107 params.tcp_mask = 3; 175 params.tcp_mask = 3;
108 } 176 }
177 std::memcpy(output.data(), &params, output.size());
178 return NvResult::Success;
179}
109 180
110 if (version == IoctlVersion::Version3) { 181NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
111 std::memcpy(output.data(), input.data(), output.size()); 182 std::vector<u8>& inline_output) {
112 std::memcpy(output2.data(), &params.tcp_mask, output2.size()); 183 IoctlGpuGetTpcMasksArgs params{};
113 } else { 184 std::memcpy(&params, input.data(), input.size());
114 std::memcpy(output.data(), &params, output.size()); 185 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
186 if (params.mask_buffer_size != 0) {
187 params.tcp_mask = 3;
115 } 188 }
116 189 std::memcpy(output.data(), &params, output.size());
117 return 0; 190 std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
191 return NvResult::Success;
118} 192}
119 193
120u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 194NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
121 LOG_DEBUG(Service_NVDRV, "called"); 195 LOG_DEBUG(Service_NVDRV, "called");
122 196
123 IoctlActiveSlotMask params{}; 197 IoctlActiveSlotMask params{};
@@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
127 params.slot = 0x07; 201 params.slot = 0x07;
128 params.mask = 0x01; 202 params.mask = 0x01;
129 std::memcpy(output.data(), &params, output.size()); 203 std::memcpy(output.data(), &params, output.size());
130 return 0; 204 return NvResult::Success;
131} 205}
132 206
133u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 207NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
134 LOG_DEBUG(Service_NVDRV, "called"); 208 LOG_DEBUG(Service_NVDRV, "called");
135 209
136 IoctlZcullGetCtxSize params{}; 210 IoctlZcullGetCtxSize params{};
@@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
139 } 213 }
140 params.size = 0x1; 214 params.size = 0x1;
141 std::memcpy(output.data(), &params, output.size()); 215 std::memcpy(output.data(), &params, output.size());
142 return 0; 216 return NvResult::Success;
143} 217}
144 218
145u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 219NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
146 LOG_DEBUG(Service_NVDRV, "called"); 220 LOG_DEBUG(Service_NVDRV, "called");
147 221
148 IoctlNvgpuGpuZcullGetInfoArgs params{}; 222 IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
162 params.subregion_height_align_pixels = 0x40; 236 params.subregion_height_align_pixels = 0x40;
163 params.subregion_count = 0x10; 237 params.subregion_count = 0x10;
164 std::memcpy(output.data(), &params, output.size()); 238 std::memcpy(output.data(), &params, output.size());
165 return 0; 239 return NvResult::Success;
166} 240}
167 241
168u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) { 242NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
169 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 243 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
170 244
171 IoctlZbcSetTable params{}; 245 IoctlZbcSetTable params{};
172 std::memcpy(&params, input.data(), input.size()); 246 std::memcpy(&params, input.data(), input.size());
173 // TODO(ogniK): What does this even actually do? 247 // TODO(ogniK): What does this even actually do?
174 std::memcpy(output.data(), &params, output.size()); 248 std::memcpy(output.data(), &params, output.size());
175 return 0; 249 return NvResult::Success;
176} 250}
177 251
178u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) { 252NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
179 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 253 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
180 254
181 IoctlZbcQueryTable params{}; 255 IoctlZbcQueryTable params{};
182 std::memcpy(&params, input.data(), input.size()); 256 std::memcpy(&params, input.data(), input.size());
183 // TODO : To implement properly 257 // TODO : To implement properly
184 std::memcpy(output.data(), &params, output.size()); 258 std::memcpy(output.data(), &params, output.size());
185 return 0; 259 return NvResult::Success;
186} 260}
187 261
188u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) { 262NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
189 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 263 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
190 264
191 IoctlFlushL2 params{}; 265 IoctlFlushL2 params{};
192 std::memcpy(&params, input.data(), input.size()); 266 std::memcpy(&params, input.data(), input.size());
193 // TODO : To implement properly 267 // TODO : To implement properly
194 std::memcpy(output.data(), &params, output.size()); 268 std::memcpy(output.data(), &params, output.size());
195 return 0; 269 return NvResult::Success;
196} 270}
197 271
198u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) { 272NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
199 LOG_DEBUG(Service_NVDRV, "called"); 273 LOG_DEBUG(Service_NVDRV, "called");
200 274
201 IoctlGetGpuTime params{}; 275 IoctlGetGpuTime params{};
202 std::memcpy(&params, input.data(), input.size()); 276 std::memcpy(&params, input.data(), input.size());
203 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count()); 277 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
204 std::memcpy(output.data(), &params, output.size()); 278 std::memcpy(output.data(), &params, output.size());
205 return 0; 279 return NvResult::Success;
206} 280}
207 281
208} // namespace Service::Nvidia::Devices 282} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index ef60f72ce..137b88238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,32 +16,13 @@ public:
16 explicit nvhost_ctrl_gpu(Core::System& system); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
21 IoctlVersion version) override; 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output) override;
22 24
23private: 25private:
24 enum class IoctlCommand : u32_le {
25 IocGetCharacteristicsCommand = 0xC0B04705,
26 IocGetTPCMasksCommand = 0xC0184706,
27 IocGetActiveSlotMaskCommand = 0x80084714,
28 IocZcullGetCtxSizeCommand = 0x80044701,
29 IocZcullGetInfo = 0x80284702,
30 IocZbcSetTable = 0x402C4703,
31 IocZbcQueryTable = 0xC0344704,
32 IocFlushL2 = 0x40084707,
33 IocInvalICache = 0x4008470D,
34 IocSetMmudebugMode = 0x4008470E,
35 IocSetSmDebugMode = 0x4010470F,
36 IocWaitForPause = 0xC0084710,
37 IocGetTcpExceptionEnStatus = 0x80084711,
38 IocNumVsms = 0x80084712,
39 IocVsmsMapping = 0xC0044713,
40 IocGetErrorChannelUserData = 0xC008471B,
41 IocGetGpuTime = 0xC010471C,
42 IocGetCpuTimeCorrelationInfo = 0xC108471D,
43 };
44
45 struct IoctlGpuCharacteristics { 26 struct IoctlGpuCharacteristics {
46 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) 27 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
47 u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B) 28 u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -159,17 +140,21 @@ private:
159 }; 140 };
160 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size"); 141 static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
161 142
162 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 143 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
163 std::vector<u8>& output2, IoctlVersion version); 144 NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
164 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2, 145 std::vector<u8>& inline_output);
165 IoctlVersion version); 146
166 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
167 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 148 NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
168 u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); 149 std::vector<u8>& inline_output);
169 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 150
170 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 151 NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
171 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 152 NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 153 NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
154 NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
155 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
156 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
157 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
173}; 158};
174 159
175} // namespace Service::Nvidia::Devices 160} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b1d9d55b5..af8b3d9f1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -23,107 +23,132 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
23 23
24nvhost_gpu::~nvhost_gpu() = default; 24nvhost_gpu::~nvhost_gpu() = default;
25 25
26u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 26NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
27 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 27 switch (command.group) {
28 IoctlVersion version) { 28 case 0x0:
29 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 29 switch (command.cmd) {
30 command.raw, input.size(), output.size()); 30 case 0x3:
31 31 return GetWaitbase(input, output);
32 switch (static_cast<IoctlCommand>(command.raw)) { 32 default:
33 case IoctlCommand::IocSetNVMAPfdCommand: 33 break;
34 return SetNVMAPfd(input, output); 34 }
35 case IoctlCommand::IocSetClientDataCommand: 35 break;
36 return SetClientData(input, output); 36 case 'H':
37 case IoctlCommand::IocGetClientDataCommand: 37 switch (command.cmd) {
38 return GetClientData(input, output); 38 case 0x1:
39 case IoctlCommand::IocZCullBind: 39 return SetNVMAPfd(input, output);
40 return ZCullBind(input, output); 40 case 0x3:
41 case IoctlCommand::IocSetErrorNotifierCommand: 41 return ChannelSetTimeout(input, output);
42 return SetErrorNotifier(input, output); 42 case 0x8:
43 case IoctlCommand::IocChannelSetPriorityCommand: 43 return SubmitGPFIFOBase(input, output, false);
44 return SetChannelPriority(input, output); 44 case 0x9:
45 case IoctlCommand::IocAllocGPFIFOEx2Command: 45 return AllocateObjectContext(input, output);
46 return AllocGPFIFOEx2(input, output); 46 case 0xb:
47 case IoctlCommand::IocAllocObjCtxCommand: 47 return ZCullBind(input, output);
48 return AllocateObjectContext(input, output); 48 case 0xc:
49 case IoctlCommand::IocChannelGetWaitbaseCommand: 49 return SetErrorNotifier(input, output);
50 return GetWaitbase(input, output); 50 case 0xd:
51 case IoctlCommand::IocChannelSetTimeoutCommand: 51 return SetChannelPriority(input, output);
52 return ChannelSetTimeout(input, output); 52 case 0x1a:
53 case IoctlCommand::IocChannelSetTimeslice: 53 return AllocGPFIFOEx2(input, output);
54 return ChannelSetTimeslice(input, output); 54 case 0x1b:
55 default: 55 return SubmitGPFIFOBase(input, output, true);
56 case 0x1d:
57 return ChannelSetTimeslice(input, output);
58 default:
59 break;
60 }
61 break;
62 case 'G':
63 switch (command.cmd) {
64 case 0x14:
65 return SetClientData(input, output);
66 case 0x15:
67 return GetClientData(input, output);
68 default:
69 break;
70 }
56 break; 71 break;
57 } 72 }
73 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
74 return NvResult::NotImplemented;
75};
58 76
59 if (command.group == NVGPU_IOCTL_MAGIC) { 77NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
60 if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { 78 const std::vector<u8>& inline_input, std::vector<u8>& output) {
61 return SubmitGPFIFO(input, output); 79 switch (command.group) {
62 } 80 case 'H':
63 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { 81 switch (command.cmd) {
64 return KickoffPB(input, output, input2, version); 82 case 0x1b:
83 return SubmitGPFIFOBase(input, inline_input, output);
65 } 84 }
85 break;
66 } 86 }
87 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
88 return NvResult::NotImplemented;
89}
67 90
68 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 91NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
69 return 0; 92 std::vector<u8>& inline_output) {
70}; 93 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
94 return NvResult::NotImplemented;
95}
71 96
72u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 97NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
73 IoctlSetNvmapFD params{}; 98 IoctlSetNvmapFD params{};
74 std::memcpy(&params, input.data(), input.size()); 99 std::memcpy(&params, input.data(), input.size());
75 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 100 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
76 101
77 nvmap_fd = params.nvmap_fd; 102 nvmap_fd = params.nvmap_fd;
78 return 0; 103 return NvResult::Success;
79} 104}
80 105
81u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 106NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
82 LOG_DEBUG(Service_NVDRV, "called"); 107 LOG_DEBUG(Service_NVDRV, "called");
83 108
84 IoctlClientData params{}; 109 IoctlClientData params{};
85 std::memcpy(&params, input.data(), input.size()); 110 std::memcpy(&params, input.data(), input.size());
86 user_data = params.data; 111 user_data = params.data;
87 return 0; 112 return NvResult::Success;
88} 113}
89 114
90u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { 115NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
91 LOG_DEBUG(Service_NVDRV, "called"); 116 LOG_DEBUG(Service_NVDRV, "called");
92 117
93 IoctlClientData params{}; 118 IoctlClientData params{};
94 std::memcpy(&params, input.data(), input.size()); 119 std::memcpy(&params, input.data(), input.size());
95 params.data = user_data; 120 params.data = user_data;
96 std::memcpy(output.data(), &params, output.size()); 121 std::memcpy(output.data(), &params, output.size());
97 return 0; 122 return NvResult::Success;
98} 123}
99 124
100u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { 125NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
101 std::memcpy(&zcull_params, input.data(), input.size()); 126 std::memcpy(&zcull_params, input.data(), input.size());
102 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va, 127 LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
103 zcull_params.mode); 128 zcull_params.mode);
104 129
105 std::memcpy(output.data(), &zcull_params, output.size()); 130 std::memcpy(output.data(), &zcull_params, output.size());
106 return 0; 131 return NvResult::Success;
107} 132}
108 133
109u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { 134NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
110 IoctlSetErrorNotifier params{}; 135 IoctlSetErrorNotifier params{};
111 std::memcpy(&params, input.data(), input.size()); 136 std::memcpy(&params, input.data(), input.size());
112 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset, 137 LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
113 params.size, params.mem); 138 params.size, params.mem);
114 139
115 std::memcpy(output.data(), &params, output.size()); 140 std::memcpy(output.data(), &params, output.size());
116 return 0; 141 return NvResult::Success;
117} 142}
118 143
119u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { 144NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
120 std::memcpy(&channel_priority, input.data(), input.size()); 145 std::memcpy(&channel_priority, input.data(), input.size());
121 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority); 146 LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
122 147
123 return 0; 148 return NvResult::Success;
124} 149}
125 150
126u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { 151NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
127 IoctlAllocGpfifoEx2 params{}; 152 IoctlAllocGpfifoEx2 params{};
128 std::memcpy(&params, input.data(), input.size()); 153 std::memcpy(&params, input.data(), input.size());
129 LOG_WARNING(Service_NVDRV, 154 LOG_WARNING(Service_NVDRV,
@@ -137,10 +162,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
137 params.fence_out = channel_fence; 162 params.fence_out = channel_fence;
138 163
139 std::memcpy(output.data(), &params, output.size()); 164 std::memcpy(output.data(), &params, output.size());
140 return 0; 165 return NvResult::Success;
141} 166}
142 167
143u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { 168NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
144 IoctlAllocObjCtx params{}; 169 IoctlAllocObjCtx params{};
145 std::memcpy(&params, input.data(), input.size()); 170 std::memcpy(&params, input.data(), input.size());
146 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num, 171 LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -148,7 +173,7 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
148 173
149 params.obj_id = 0x0; 174 params.obj_id = 0x0;
150 std::memcpy(output.data(), &params, output.size()); 175 std::memcpy(output.data(), &params, output.size());
151 return 0; 176 return NvResult::Success;
152} 177}
153 178
154static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 179static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
@@ -192,8 +217,8 @@ static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence
192 return result; 217 return result;
193} 218}
194 219
195u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 220NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
196 Tegra::CommandList&& entries) { 221 Tegra::CommandList&& entries) {
197 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, 222 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
198 params.num_entries, params.flags.raw); 223 params.num_entries, params.flags.raw);
199 224
@@ -227,69 +252,70 @@ u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& out
227 } 252 }
228 253
229 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 254 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
230 return 0; 255 return NvResult::Success;
231} 256}
232 257
233u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 258NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
259 bool kickoff) {
234 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 260 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
235 UNIMPLEMENTED(); 261 UNIMPLEMENTED();
262 return NvResult::InvalidSize;
236 } 263 }
237 IoctlSubmitGpfifo params{}; 264 IoctlSubmitGpfifo params{};
238 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 265 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
239
240 Tegra::CommandList entries(params.num_entries); 266 Tegra::CommandList entries(params.num_entries);
241 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], 267
242 params.num_entries * sizeof(Tegra::CommandListHeader)); 268 if (kickoff) {
269 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
270 params.num_entries * sizeof(Tegra::CommandListHeader));
271 } else {
272 std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
273 params.num_entries * sizeof(Tegra::CommandListHeader));
274 }
243 275
244 return SubmitGPFIFOImpl(params, output, std::move(entries)); 276 return SubmitGPFIFOImpl(params, output, std::move(entries));
245} 277}
246 278
247u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 279NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
248 const std::vector<u8>& input2, IoctlVersion version) { 280 const std::vector<u8>& input_inline,
281 std::vector<u8>& output) {
249 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 282 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
250 UNIMPLEMENTED(); 283 UNIMPLEMENTED();
284 return NvResult::InvalidSize;
251 } 285 }
252 IoctlSubmitGpfifo params{}; 286 IoctlSubmitGpfifo params{};
253 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 287 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
254
255 Tegra::CommandList entries(params.num_entries); 288 Tegra::CommandList entries(params.num_entries);
256 if (version == IoctlVersion::Version2) { 289 std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
257 std::memcpy(entries.command_lists.data(), input2.data(),
258 params.num_entries * sizeof(Tegra::CommandListHeader));
259 } else {
260 system.Memory().ReadBlock(params.address, entries.command_lists.data(),
261 params.num_entries * sizeof(Tegra::CommandListHeader));
262 }
263
264 return SubmitGPFIFOImpl(params, output, std::move(entries)); 290 return SubmitGPFIFOImpl(params, output, std::move(entries));
265} 291}
266 292
267u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 293NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
268 IoctlGetWaitbase params{}; 294 IoctlGetWaitbase params{};
269 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 295 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
270 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); 296 LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
271 297
272 params.value = 0; // Seems to be hard coded at 0 298 params.value = 0; // Seems to be hard coded at 0
273 std::memcpy(output.data(), &params, output.size()); 299 std::memcpy(output.data(), &params, output.size());
274 return 0; 300 return NvResult::Success;
275} 301}
276 302
277u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 303NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
278 IoctlChannelSetTimeout params{}; 304 IoctlChannelSetTimeout params{};
279 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout)); 305 std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
280 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout); 306 LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
281 307
282 return 0; 308 return NvResult::Success;
283} 309}
284 310
285u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { 311NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
286 IoctlSetTimeslice params{}; 312 IoctlSetTimeslice params{};
287 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice)); 313 std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
288 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); 314 LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
289 315
290 channel_timeslice = params.timeslice; 316 channel_timeslice = params.timeslice;
291 317
292 return 0; 318 return NvResult::Success;
293} 319}
294 320
295} // namespace Service::Nvidia::Devices 321} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index a252fc06d..e0298b4fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -20,43 +20,19 @@ class SyncpointManager;
20namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
21 21
22class nvmap; 22class nvmap;
23constexpr u32 NVGPU_IOCTL_MAGIC('H');
24constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
25constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
26
27class nvhost_gpu final : public nvdevice { 23class nvhost_gpu final : public nvdevice {
28public: 24public:
29 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, 25 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
30 SyncpointManager& syncpoint_manager); 26 SyncpointManager& syncpoint_manager);
31 ~nvhost_gpu() override; 27 ~nvhost_gpu() override;
32 28
33 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 29 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
34 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 30 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
35 IoctlVersion version) override; 31 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
32 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
33 std::vector<u8>& inline_output) override;
36 34
37private: 35private:
38 enum class IoctlCommand : u32_le {
39 IocSetNVMAPfdCommand = 0x40044801,
40 IocAllocGPFIFOCommand = 0x40084805,
41 IocSetClientDataCommand = 0x40084714,
42 IocGetClientDataCommand = 0x80084715,
43 IocZCullBind = 0xc010480b,
44 IocSetErrorNotifierCommand = 0xC018480C,
45 IocChannelSetPriorityCommand = 0x4004480D,
46 IocEnableCommand = 0x0000480E,
47 IocDisableCommand = 0x0000480F,
48 IocPreemptCommand = 0x00004810,
49 IocForceResetCommand = 0x00004811,
50 IocEventIdControlCommand = 0x40084812,
51 IocGetErrorNotificationCommand = 0xC0104817,
52 IocAllocGPFIFOExCommand = 0x40204818,
53 IocAllocGPFIFOEx2Command = 0xC020481A,
54 IocAllocObjCtxCommand = 0xC0104809,
55 IocChannelGetWaitbaseCommand = 0xC0080003,
56 IocChannelSetTimeoutCommand = 0x40044803,
57 IocChannelSetTimeslice = 0xC004481D,
58 };
59
60 enum class CtxObjects : u32_le { 36 enum class CtxObjects : u32_le {
61 Ctx2D = 0x902D, 37 Ctx2D = 0x902D,
62 Ctx3D = 0xB197, 38 Ctx3D = 0xB197,
@@ -67,63 +43,63 @@ private:
67 }; 43 };
68 44
69 struct IoctlSetNvmapFD { 45 struct IoctlSetNvmapFD {
70 u32_le nvmap_fd; 46 s32_le nvmap_fd{};
71 }; 47 };
72 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 48 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
73 49
74 struct IoctlChannelSetTimeout { 50 struct IoctlChannelSetTimeout {
75 u32_le timeout; 51 u32_le timeout{};
76 }; 52 };
77 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size"); 53 static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
78 54
79 struct IoctlAllocGPFIFO { 55 struct IoctlAllocGPFIFO {
80 u32_le num_entries; 56 u32_le num_entries{};
81 u32_le flags; 57 u32_le flags{};
82 }; 58 };
83 static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size"); 59 static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
84 60
85 struct IoctlClientData { 61 struct IoctlClientData {
86 u64_le data; 62 u64_le data{};
87 }; 63 };
88 static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size"); 64 static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
89 65
90 struct IoctlZCullBind { 66 struct IoctlZCullBind {
91 u64_le gpu_va; 67 u64_le gpu_va{};
92 u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf 68 u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
93 INSERT_PADDING_WORDS(1); 69 INSERT_PADDING_WORDS(1);
94 }; 70 };
95 static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size"); 71 static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
96 72
97 struct IoctlSetErrorNotifier { 73 struct IoctlSetErrorNotifier {
98 u64_le offset; 74 u64_le offset{};
99 u64_le size; 75 u64_le size{};
100 u32_le mem; // nvmap object handle 76 u32_le mem{}; // nvmap object handle
101 INSERT_PADDING_WORDS(1); 77 INSERT_PADDING_WORDS(1);
102 }; 78 };
103 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); 79 static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
104 80
105 struct IoctlChannelSetPriority { 81 struct IoctlChannelSetPriority {
106 u32_le priority; 82 u32_le priority{};
107 }; 83 };
108 static_assert(sizeof(IoctlChannelSetPriority) == 4, 84 static_assert(sizeof(IoctlChannelSetPriority) == 4,
109 "IoctlChannelSetPriority is incorrect size"); 85 "IoctlChannelSetPriority is incorrect size");
110 86
111 struct IoctlSetTimeslice { 87 struct IoctlSetTimeslice {
112 u32_le timeslice; 88 u32_le timeslice{};
113 }; 89 };
114 static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size"); 90 static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
115 91
116 struct IoctlEventIdControl { 92 struct IoctlEventIdControl {
117 u32_le cmd; // 0=disable, 1=enable, 2=clear 93 u32_le cmd{}; // 0=disable, 1=enable, 2=clear
118 u32_le id; 94 u32_le id{};
119 }; 95 };
120 static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size"); 96 static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
121 97
122 struct IoctlGetErrorNotification { 98 struct IoctlGetErrorNotification {
123 u64_le timestamp; 99 u64_le timestamp{};
124 u32_le info32; 100 u32_le info32{};
125 u16_le info16; 101 u16_le info16{};
126 u16_le status; // always 0xFFFF 102 u16_le status{}; // always 0xFFFF
127 }; 103 };
128 static_assert(sizeof(IoctlGetErrorNotification) == 16, 104 static_assert(sizeof(IoctlGetErrorNotification) == 16,
129 "IoctlGetErrorNotification is incorrect size"); 105 "IoctlGetErrorNotification is incorrect size");
@@ -131,39 +107,39 @@ private:
131 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 107 static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
132 108
133 struct IoctlAllocGpfifoEx { 109 struct IoctlAllocGpfifoEx {
134 u32_le num_entries; 110 u32_le num_entries{};
135 u32_le flags; 111 u32_le flags{};
136 u32_le unk0; 112 u32_le unk0{};
137 u32_le unk1; 113 u32_le unk1{};
138 u32_le unk2; 114 u32_le unk2{};
139 u32_le unk3; 115 u32_le unk3{};
140 u32_le unk4; 116 u32_le unk4{};
141 u32_le unk5; 117 u32_le unk5{};
142 }; 118 };
143 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); 119 static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
144 120
145 struct IoctlAllocGpfifoEx2 { 121 struct IoctlAllocGpfifoEx2 {
146 u32_le num_entries; // in 122 u32_le num_entries{}; // in
147 u32_le flags; // in 123 u32_le flags{}; // in
148 u32_le unk0; // in (1 works) 124 u32_le unk0{}; // in (1 works)
149 Fence fence_out; // out 125 Fence fence_out{}; // out
150 u32_le unk1; // in 126 u32_le unk1{}; // in
151 u32_le unk2; // in 127 u32_le unk2{}; // in
152 u32_le unk3; // in 128 u32_le unk3{}; // in
153 }; 129 };
154 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); 130 static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
155 131
156 struct IoctlAllocObjCtx { 132 struct IoctlAllocObjCtx {
157 u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, 133 u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
158 // 0xB06F=channel_gpfifo 134 // 0xB06F=channel_gpfifo
159 u32_le flags; 135 u32_le flags{};
160 u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported 136 u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
161 }; 137 };
162 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); 138 static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
163 139
164 struct IoctlSubmitGpfifo { 140 struct IoctlSubmitGpfifo {
165 u64_le address; // pointer to gpfifo entry structs 141 u64_le address{}; // pointer to gpfifo entry structs
166 u32_le num_entries; // number of fence objects being submitted 142 u32_le num_entries{}; // number of fence objects being submitted
167 union { 143 union {
168 u32_le raw; 144 u32_le raw;
169 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 145 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
@@ -172,7 +148,7 @@ private:
172 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 148 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
173 BitField<8, 1, u32_le> increment; // increment the returned fence 149 BitField<8, 1, u32_le> increment; // increment the returned fence
174 } flags; 150 } flags;
175 Fence fence_out; // returned new fence object for others to wait on 151 Fence fence_out{}; // returned new fence object for others to wait on
176 152
177 u32 AddIncrementValue() const { 153 u32 AddIncrementValue() const {
178 return flags.add_increment.Value() << 1; 154 return flags.add_increment.Value() << 1;
@@ -182,33 +158,34 @@ private:
182 "IoctlSubmitGpfifo is incorrect size"); 158 "IoctlSubmitGpfifo is incorrect size");
183 159
184 struct IoctlGetWaitbase { 160 struct IoctlGetWaitbase {
185 u32 unknown; // seems to be ignored? Nintendo added this 161 u32 unknown{}; // seems to be ignored? Nintendo added this
186 u32 value; 162 u32 value{};
187 }; 163 };
188 static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size"); 164 static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
189 165
190 u32_le nvmap_fd{}; 166 s32_le nvmap_fd{};
191 u64_le user_data{}; 167 u64_le user_data{};
192 IoctlZCullBind zcull_params{}; 168 IoctlZCullBind zcull_params{};
193 u32_le channel_priority{}; 169 u32_le channel_priority{};
194 u32_le channel_timeslice{}; 170 u32_le channel_timeslice{};
195 171
196 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 172 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
197 u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); 173 NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
198 u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output); 174 NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
199 u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); 175 NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
200 u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); 176 NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
201 u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); 177 NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
202 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 178 NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
203 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 179 NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
204 u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, 180 NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
205 Tegra::CommandList&& entries); 181 Tegra::CommandList&& entries);
206 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 182 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
207 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, 183 bool kickoff = false);
208 const std::vector<u8>& input2, IoctlVersion version); 184 NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
209 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 185 std::vector<u8>& output);
210 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 186 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
211 u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 187 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
188 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
212 189
213 std::shared_ptr<nvmap> nvmap_dev; 190 std::shared_ptr<nvmap> nvmap_dev;
214 SyncpointManager& syncpoint_manager; 191 SyncpointManager& syncpoint_manager;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b6df48360..d8735491c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -15,46 +15,58 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de
15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} 15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
19 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 19 std::vector<u8>& output) {
20 IoctlVersion version) { 20 switch (command.group) {
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 case 0x0:
22 command.raw, input.size(), output.size()); 22 switch (command.cmd) {
23 23 case 0x1:
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 return Submit(input, output);
25 case IoctlCommand::IocSetNVMAPfdCommand: 25 case 0x2:
26 return SetNVMAPfd(input); 26 return GetSyncpoint(input, output);
27 case IoctlCommand::IocSubmit: 27 case 0x3:
28 return Submit(input, output); 28 return GetWaitbase(input, output);
29 case IoctlCommand::IocGetSyncpoint: 29 case 0x7:
30 return GetSyncpoint(input, output); 30 return SetSubmitTimeout(input, output);
31 case IoctlCommand::IocGetWaitbase: 31 case 0x9:
32 return GetWaitbase(input, output); 32 return MapBuffer(input, output);
33 case IoctlCommand::IocMapBuffer: 33 case 0xa: {
34 case IoctlCommand::IocMapBuffer2: 34 if (command.length == 0x1c) {
35 case IoctlCommand::IocMapBuffer3: 35 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
36 case IoctlCommand::IocMapBufferEx: 36 Tegra::ChCommandHeaderList cmdlist(1);
37 return MapBuffer(input, output); 37 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
38 case IoctlCommand::IocUnmapBufferEx: { 38 system.GPU().PushCommandBuffer(cmdlist);
39 // This command is sent when the video stream has ended, flush all video contexts 39 }
40 // This is usually sent in the folowing order: vic, nvdec, vic. 40 return UnmapBuffer(input, output);
41 // Inform the GPU to clear any remaining nvdec buffers when this is detected. 41 }
42 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 42 default:
43 Tegra::ChCommandHeaderList cmdlist(1); 43 break;
44 cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F}; 44 }
45 system.GPU().PushCommandBuffer(cmdlist); 45 break;
46 [[fallthrough]]; // fallthrough to unmap buffers 46 case 'H':
47 }; 47 switch (command.cmd) {
48 case IoctlCommand::IocUnmapBuffer: 48 case 0x1:
49 case IoctlCommand::IocUnmapBuffer2: 49 return SetNVMAPfd(input);
50 case IoctlCommand::IocUnmapBuffer3: 50 default:
51 return UnmapBuffer(input, output); 51 break;
52 case IoctlCommand::IocSetSubmitTimeout: 52 }
53 return SetSubmitTimeout(input, output); 53 break;
54 } 54 }
55 55
56 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); 56 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
57 return 0; 57 return NvResult::NotImplemented;
58}
59
60NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
61 const std::vector<u8>& inline_input, std::vector<u8>& output) {
62 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
63 return NvResult::NotImplemented;
64}
65
66NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
67 std::vector<u8>& inline_output) {
68 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
69 return NvResult::NotImplemented;
58} 70}
59 71
60} // namespace Service::Nvidia::Devices 72} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 102777ddd..79b8b6de1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -14,26 +14,11 @@ public:
14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_nvdec() override; 15 ~nvhost_nvdec() override;
16 16
17 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
18 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
19 IoctlVersion version) override; 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
20 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
21private: 21 std::vector<u8>& inline_output) override;
22 enum class IoctlCommand : u32_le {
23 IocSetNVMAPfdCommand = 0x40044801,
24 IocSubmit = 0xC0400001,
25 IocGetSyncpoint = 0xC0080002,
26 IocGetWaitbase = 0xC0080003,
27 IocMapBuffer = 0xC01C0009,
28 IocMapBuffer2 = 0xC16C0009,
29 IocMapBuffer3 = 0xC15C0009,
30 IocMapBufferEx = 0xC0A40009,
31 IocUnmapBuffer = 0xC0A4000A,
32 IocUnmapBuffer2 = 0xC16C000A,
33 IocUnmapBufferEx = 0xC01C000A,
34 IocUnmapBuffer3 = 0xC15C000A,
35 IocSetSubmitTimeout = 0x40040007,
36 };
37}; 22};
38 23
39} // namespace Service::Nvidia::Devices 24} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 30f03f845..b49cecb42 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -36,26 +36,20 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
36} 36}
37} // Anonymous namespace 37} // Anonymous namespace
38 38
39namespace NvErrCodes {
40constexpr u32 Success{};
41[[maybe_unused]] constexpr u32 OutOfMemory{static_cast<u32>(-12)};
42constexpr u32 InvalidInput{static_cast<u32>(-22)};
43} // namespace NvErrCodes
44
45nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 39nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
46 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 40 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
47nvhost_nvdec_common::~nvhost_nvdec_common() = default; 41nvhost_nvdec_common::~nvhost_nvdec_common() = default;
48 42
49u32 nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 43NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
50 IoctlSetNvmapFD params{}; 44 IoctlSetNvmapFD params{};
51 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD)); 45 std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
52 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 46 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
53 47
54 nvmap_fd = params.nvmap_fd; 48 nvmap_fd = params.nvmap_fd;
55 return 0; 49 return NvResult::Success;
56} 50}
57 51
58u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) { 52NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
59 IoctlSubmit params{}; 53 IoctlSubmit params{};
60 std::memcpy(&params, input.data(), sizeof(IoctlSubmit)); 54 std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
61 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); 55 LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -83,12 +77,12 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
83 77
84 for (const auto& cmd_buffer : command_buffers) { 78 for (const auto& cmd_buffer : command_buffers) {
85 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); 79 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
86 ASSERT_OR_EXECUTE(object, return NvErrCodes::InvalidInput;); 80 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
87 const auto map = FindBufferMap(object->dma_map_addr); 81 const auto map = FindBufferMap(object->dma_map_addr);
88 if (!map) { 82 if (!map) {
89 LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}", 83 LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
90 object->addr, object->dma_map_addr); 84 object->addr, object->dma_map_addr);
91 return 0; 85 return NvResult::Success;
92 } 86 }
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 87 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(), 88 gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
@@ -105,10 +99,10 @@ u32 nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& o
105 offset = WriteVectors(output, syncpt_increments, offset); 99 offset = WriteVectors(output, syncpt_increments, offset);
106 offset = WriteVectors(output, wait_checks, offset); 100 offset = WriteVectors(output, wait_checks, offset);
107 101
108 return NvErrCodes::Success; 102 return NvResult::Success;
109} 103}
110 104
111u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { 105NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
112 IoctlGetSyncpoint params{}; 106 IoctlGetSyncpoint params{};
113 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 107 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
114 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 108 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
@@ -118,18 +112,18 @@ u32 nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<
118 params.value = 0; 112 params.value = 0;
119 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint)); 113 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
120 114
121 return NvErrCodes::Success; 115 return NvResult::Success;
122} 116}
123 117
124u32 nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 118NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
125 IoctlGetWaitbase params{}; 119 IoctlGetWaitbase params{};
126 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 120 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
127 params.value = 0; // Seems to be hard coded at 0 121 params.value = 0; // Seems to be hard coded at 0
128 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase)); 122 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
129 return 0; 123 return NvResult::Success;
130} 124}
131 125
132u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 126NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
133 IoctlMapBuffer params{}; 127 IoctlMapBuffer params{};
134 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 128 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
135 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 129 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -143,7 +137,7 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
143 if (!object) { 137 if (!object) {
144 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); 138 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
145 std::memcpy(output.data(), &params, output.size()); 139 std::memcpy(output.data(), &params, output.size());
146 return NvErrCodes::InvalidInput; 140 return NvResult::InvalidState;
147 } 141 }
148 if (object->dma_map_addr == 0) { 142 if (object->dma_map_addr == 0) {
149 // NVDEC and VIC memory is in the 32-bit address space 143 // NVDEC and VIC memory is in the 32-bit address space
@@ -165,10 +159,10 @@ u32 nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>
165 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), 159 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
166 cmd_buffer_handles.size() * sizeof(MapBufferEntry)); 160 cmd_buffer_handles.size() * sizeof(MapBufferEntry));
167 161
168 return NvErrCodes::Success; 162 return NvResult::Success;
169} 163}
170 164
171u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 165NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
172 IoctlMapBuffer params{}; 166 IoctlMapBuffer params{};
173 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 167 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
174 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 168 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
@@ -181,7 +175,7 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
181 if (!object) { 175 if (!object) {
182 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); 176 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
183 std::memcpy(output.data(), &params, output.size()); 177 std::memcpy(output.data(), &params, output.size());
184 return NvErrCodes::InvalidInput; 178 return NvResult::InvalidState;
185 } 179 }
186 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) { 180 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
187 gpu.MemoryManager().Unmap(object->dma_map_addr, *size); 181 gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
@@ -193,13 +187,14 @@ u32 nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u
193 object->dma_map_addr = 0; 187 object->dma_map_addr = 0;
194 } 188 }
195 std::memset(output.data(), 0, output.size()); 189 std::memset(output.data(), 0, output.size());
196 return NvErrCodes::Success; 190 return NvResult::Success;
197} 191}
198 192
199u32 nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output) { 193NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
194 std::vector<u8>& output) {
200 std::memcpy(&submit_timeout, input.data(), input.size()); 195 std::memcpy(&submit_timeout, input.data(), input.size());
201 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 196 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
202 return NvErrCodes::Success; 197 return NvResult::Success;
203} 198}
204 199
205std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap( 200std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index c249c5349..86ba3a4d1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -18,9 +18,37 @@ public:
18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
19 ~nvhost_nvdec_common() override; 19 ~nvhost_nvdec_common() override;
20 20
21 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 21 /**
22 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 22 * Handles an ioctl1 request.
23 IoctlVersion version) = 0; 23 * @param command The ioctl command id.
24 * @param input A buffer containing the input data for the ioctl.
25 * @param output A buffer where the output data will be written to.
26 * @returns The result code of the ioctl.
27 */
28 virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
29 std::vector<u8>& output) = 0;
30
31 /**
32 * Handles an ioctl2 request.
33 * @param command The ioctl command id.
34 * @param input A buffer containing the input data for the ioctl.
35 * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
36 * @param output A buffer where the output data will be written to.
37 * @returns The result code of the ioctl.
38 */
39 virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
40 const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
41
42 /**
43 * Handles an ioctl3 request.
44 * @param command The ioctl command id.
45 * @param input A buffer containing the input data for the ioctl.
46 * @param output A buffer where the output data will be written to.
47 * @param inline_output A buffer where the inlined output data will be written to.
48 * @returns The result code of the ioctl.
49 */
50 virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
51 std::vector<u8>& inline_output) = 0;
24 52
25protected: 53protected:
26 class BufferMap final { 54 class BufferMap final {
@@ -63,102 +91,102 @@ protected:
63 }; 91 };
64 92
65 struct IoctlSetNvmapFD { 93 struct IoctlSetNvmapFD {
66 u32_le nvmap_fd; 94 s32_le nvmap_fd{};
67 }; 95 };
68 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 96 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
69 97
70 struct IoctlSubmitCommandBuffer { 98 struct IoctlSubmitCommandBuffer {
71 u32_le id; 99 u32_le id{};
72 u32_le offset; 100 u32_le offset{};
73 u32_le count; 101 u32_le count{};
74 }; 102 };
75 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC, 103 static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
76 "IoctlSubmitCommandBuffer is incorrect size"); 104 "IoctlSubmitCommandBuffer is incorrect size");
77 struct IoctlSubmit { 105 struct IoctlSubmit {
78 u32_le cmd_buffer_count; 106 u32_le cmd_buffer_count{};
79 u32_le relocation_count; 107 u32_le relocation_count{};
80 u32_le syncpoint_count; 108 u32_le syncpoint_count{};
81 u32_le fence_count; 109 u32_le fence_count{};
82 }; 110 };
83 static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size"); 111 static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
84 112
85 struct CommandBuffer { 113 struct CommandBuffer {
86 s32 memory_id; 114 s32 memory_id{};
87 u32 offset; 115 u32 offset{};
88 s32 word_count; 116 s32 word_count{};
89 }; 117 };
90 static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size"); 118 static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
91 119
92 struct Reloc { 120 struct Reloc {
93 s32 cmdbuffer_memory; 121 s32 cmdbuffer_memory{};
94 s32 cmdbuffer_offset; 122 s32 cmdbuffer_offset{};
95 s32 target; 123 s32 target{};
96 s32 target_offset; 124 s32 target_offset{};
97 }; 125 };
98 static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size"); 126 static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
99 127
100 struct SyncptIncr { 128 struct SyncptIncr {
101 u32 id; 129 u32 id{};
102 u32 increments; 130 u32 increments{};
103 }; 131 };
104 static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size"); 132 static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
105 133
106 struct Fence { 134 struct Fence {
107 u32 id; 135 u32 id{};
108 u32 value; 136 u32 value{};
109 }; 137 };
110 static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size"); 138 static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
111 139
112 struct IoctlGetSyncpoint { 140 struct IoctlGetSyncpoint {
113 // Input 141 // Input
114 u32_le param; 142 u32_le param{};
115 // Output 143 // Output
116 u32_le value; 144 u32_le value{};
117 }; 145 };
118 static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size"); 146 static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
119 147
120 struct IoctlGetWaitbase { 148 struct IoctlGetWaitbase {
121 u32_le unknown; // seems to be ignored? Nintendo added this 149 u32_le unknown{}; // seems to be ignored? Nintendo added this
122 u32_le value; 150 u32_le value{};
123 }; 151 };
124 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size"); 152 static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
125 153
126 struct IoctlMapBuffer { 154 struct IoctlMapBuffer {
127 u32_le num_entries; 155 u32_le num_entries{};
128 u32_le data_address; // Ignored by the driver. 156 u32_le data_address{}; // Ignored by the driver.
129 u32_le attach_host_ch_das; 157 u32_le attach_host_ch_das{};
130 }; 158 };
131 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 159 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
132 160
133 struct IocGetIdParams { 161 struct IocGetIdParams {
134 // Input 162 // Input
135 u32_le param; 163 u32_le param{};
136 // Output 164 // Output
137 u32_le value; 165 u32_le value{};
138 }; 166 };
139 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 167 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
140 168
141 // Used for mapping and unmapping command buffers 169 // Used for mapping and unmapping command buffers
142 struct MapBufferEntry { 170 struct MapBufferEntry {
143 u32_le map_handle; 171 u32_le map_handle{};
144 u32_le map_address; 172 u32_le map_address{};
145 }; 173 };
146 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size"); 174 static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
147 175
148 /// Ioctl command implementations 176 /// Ioctl command implementations
149 u32 SetNVMAPfd(const std::vector<u8>& input); 177 NvResult SetNVMAPfd(const std::vector<u8>& input);
150 u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); 178 NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
151 u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); 179 NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
152 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 180 NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
153 u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 181 NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
154 u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 182 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
155 u32 SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 183 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
156 184
157 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 185 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
158 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 186 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
159 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); 187 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
160 188
161 u32_le nvmap_fd{}; 189 s32_le nvmap_fd{};
162 u32_le submit_timeout{}; 190 u32_le submit_timeout{};
163 std::shared_ptr<nvmap> nvmap_dev; 191 std::shared_ptr<nvmap> nvmap_dev;
164 192
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 96e7b7dab..2d06955c0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,28 +13,44 @@ namespace Service::Nvidia::Devices {
13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 16NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 17 std::vector<u8>& output) {
18 IoctlVersion version) { 18 switch (command.group) {
19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 case 'H':
20 command.raw, input.size(), output.size()); 20 switch (command.cmd) {
21 21 case 0x1:
22 switch (static_cast<IoctlCommand>(command.raw)) { 22 return SetNVMAPfd(input, output);
23 case IoctlCommand::IocSetNVMAPfdCommand: 23 default:
24 return SetNVMAPfd(input, output); 24 break;
25 }
26 break;
27 default:
28 break;
25 } 29 }
26 30
27 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 31 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
28 return 0; 32 return NvResult::NotImplemented;
29} 33}
30 34
31u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { 35NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
36 const std::vector<u8>& inline_input, std::vector<u8>& output) {
37 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
38 return NvResult::NotImplemented;
39}
40
41NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
42 std::vector<u8>& inline_output) {
43 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
44 return NvResult::NotImplemented;
45}
46
47NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
32 IoctlSetNvmapFD params{}; 48 IoctlSetNvmapFD params{};
33 std::memcpy(&params, input.data(), input.size()); 49 std::memcpy(&params, input.data(), input.size());
34 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); 50 LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
35 51
36 nvmap_fd = params.nvmap_fd; 52 nvmap_fd = params.nvmap_fd;
37 return 0; 53 return NvResult::Success;
38} 54}
39 55
40} // namespace Service::Nvidia::Devices 56} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 98dcac52f..43948d18d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,23 +16,21 @@ public:
16 explicit nvhost_nvjpg(Core::System& system); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 19 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 20 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
21 IoctlVersion version) override; 21 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
22 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
23 std::vector<u8>& inline_output) override;
22 24
23private: 25private:
24 enum class IoctlCommand : u32_le {
25 IocSetNVMAPfdCommand = 0x40044801,
26 };
27
28 struct IoctlSetNvmapFD { 26 struct IoctlSetNvmapFD {
29 u32_le nvmap_fd; 27 s32_le nvmap_fd{};
30 }; 28 };
31 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); 29 static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
32 30
33 u32_le nvmap_fd{}; 31 s32_le nvmap_fd{};
34 32
35 u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); 33 NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
36}; 34};
37 35
38} // namespace Service::Nvidia::Devices 36} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 60db54d00..805fe86ae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -15,36 +15,50 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
15 15
16nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
17 17
18u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, 18NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
19 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 19 switch (command.group) {
20 IoctlVersion version) { 20 case 0x0:
21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 switch (command.cmd) {
22 command.raw, input.size(), output.size()); 22 case 0x1:
23 23 return Submit(input, output);
24 switch (static_cast<IoctlCommand>(command.raw)) { 24 case 0x2:
25 case IoctlCommand::IocSetNVMAPfdCommand: 25 return GetSyncpoint(input, output);
26 return SetNVMAPfd(input); 26 case 0x3:
27 case IoctlCommand::IocSubmit: 27 return GetWaitbase(input, output);
28 return Submit(input, output); 28 case 0x9:
29 case IoctlCommand::IocGetSyncpoint: 29 return MapBuffer(input, output);
30 return GetSyncpoint(input, output); 30 case 0xa:
31 case IoctlCommand::IocGetWaitbase: 31 return UnmapBuffer(input, output);
32 return GetWaitbase(input, output); 32 default:
33 case IoctlCommand::IocMapBuffer: 33 break;
34 case IoctlCommand::IocMapBuffer2: 34 }
35 case IoctlCommand::IocMapBuffer3: 35 break;
36 case IoctlCommand::IocMapBuffer4: 36 case 'H':
37 case IoctlCommand::IocMapBufferEx: 37 switch (command.cmd) {
38 return MapBuffer(input, output); 38 case 0x1:
39 case IoctlCommand::IocUnmapBuffer: 39 return SetNVMAPfd(input);
40 case IoctlCommand::IocUnmapBuffer2: 40 default:
41 case IoctlCommand::IocUnmapBuffer3: 41 break;
42 case IoctlCommand::IocUnmapBufferEx: 42 }
43 return UnmapBuffer(input, output); 43 break;
44 default:
45 break;
44 } 46 }
45 47
46 UNIMPLEMENTED_MSG("Unimplemented ioctl 0x{:X}", command.raw); 48 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return 0; 49 return NvResult::NotImplemented;
50}
51
52NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
53 const std::vector<u8>& inline_input, std::vector<u8>& output) {
54 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
55 return NvResult::NotImplemented;
56}
57
58NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
59 std::vector<u8>& inline_output) {
60 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
61 return NvResult::NotImplemented;
48} 62}
49 63
50} // namespace Service::Nvidia::Devices 64} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index f975b190c..b2e11f4d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -13,25 +13,11 @@ class nvhost_vic final : public nvhost_nvdec_common {
13public: 13public:
14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
15 ~nvhost_vic(); 15 ~nvhost_vic();
16 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) override;
19 16
20private: 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
21 enum class IoctlCommand : u32_le { 18 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
22 IocSetNVMAPfdCommand = 0x40044801, 19 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
23 IocSubmit = 0xC0400001, 20 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
24 IocGetSyncpoint = 0xC0080002, 21 std::vector<u8>& inline_output) override;
25 IocGetWaitbase = 0xC0080003,
26 IocMapBuffer = 0xC01C0009,
27 IocMapBuffer2 = 0xC0340009,
28 IocMapBuffer3 = 0xC0140009,
29 IocMapBuffer4 = 0xC00C0009,
30 IocMapBufferEx = 0xC03C0009,
31 IocUnmapBuffer = 0xC03C000A,
32 IocUnmapBuffer2 = 0xC034000A,
33 IocUnmapBuffer3 = 0xC00C000A,
34 IocUnmapBufferEx = 0xC01C000A,
35 };
36}; 22};
37} // namespace Service::Nvidia::Devices 23} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 9436e16ad..4015a2740 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,13 +11,6 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
21nvmap::nvmap(Core::System& system) : nvdevice(system) { 14nvmap::nvmap(Core::System& system) : nvdevice(system) {
22 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to 15 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
23 // represent this. 16 // represent this.
@@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
26 19
27nvmap::~nvmap() = default; 20nvmap::~nvmap() = default;
28 21
22NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
23 switch (command.group) {
24 case 0x1:
25 switch (command.cmd) {
26 case 0x1:
27 return IocCreate(input, output);
28 case 0x3:
29 return IocFromId(input, output);
30 case 0x4:
31 return IocAlloc(input, output);
32 case 0x5:
33 return IocFree(input, output);
34 case 0x9:
35 return IocParam(input, output);
36 case 0xe:
37 return IocGetId(input, output);
38 default:
39 break;
40 }
41 break;
42 default:
43 break;
44 }
45
46 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
47 return NvResult::NotImplemented;
48}
49
50NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
51 const std::vector<u8>& inline_input, std::vector<u8>& output) {
52 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
53 return NvResult::NotImplemented;
54}
55
56NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
57 std::vector<u8>& inline_output) {
58 UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
59 return NvResult::NotImplemented;
60}
61
29VAddr nvmap::GetObjectAddress(u32 handle) const { 62VAddr nvmap::GetObjectAddress(u32 handle) const {
30 auto object = GetObject(handle); 63 auto object = GetObject(handle);
31 ASSERT(object); 64 ASSERT(object);
@@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
33 return object->addr; 66 return object->addr;
34} 67}
35 68
36u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
37 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
38 IoctlVersion version) {
39 switch (static_cast<IoctlCommand>(command.raw)) {
40 case IoctlCommand::Create:
41 return IocCreate(input, output);
42 case IoctlCommand::Alloc:
43 return IocAlloc(input, output);
44 case IoctlCommand::GetId:
45 return IocGetId(input, output);
46 case IoctlCommand::FromId:
47 return IocFromId(input, output);
48 case IoctlCommand::Param:
49 return IocParam(input, output);
50 case IoctlCommand::Free:
51 return IocFree(input, output);
52 }
53
54 UNIMPLEMENTED_MSG("Unimplemented ioctl");
55 return 0;
56}
57
58u32 nvmap::CreateObject(u32 size) { 69u32 nvmap::CreateObject(u32 size) {
59 // Create a new nvmap object and obtain a handle to it. 70 // Create a new nvmap object and obtain a handle to it.
60 auto object = std::make_shared<Object>(); 71 auto object = std::make_shared<Object>();
@@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) {
70 return handle; 81 return handle;
71} 82}
72 83
73u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 84NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
74 IocCreateParams params; 85 IocCreateParams params;
75 std::memcpy(&params, input.data(), sizeof(params)); 86 std::memcpy(&params, input.data(), sizeof(params));
76 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); 87 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
77 88
78 if (!params.size) { 89 if (!params.size) {
79 LOG_ERROR(Service_NVDRV, "Size is 0"); 90 LOG_ERROR(Service_NVDRV, "Size is 0");
80 return static_cast<u32>(NvErrCodes::InvalidValue); 91 return NvResult::BadValue;
81 } 92 }
82 93
83 params.handle = CreateObject(params.size); 94 params.handle = CreateObject(params.size);
84 95
85 std::memcpy(output.data(), &params, sizeof(params)); 96 std::memcpy(output.data(), &params, sizeof(params));
86 return 0; 97 return NvResult::Success;
87} 98}
88 99
89u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 100NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
90 IocAllocParams params; 101 IocAllocParams params;
91 std::memcpy(&params, input.data(), sizeof(params)); 102 std::memcpy(&params, input.data(), sizeof(params));
92 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); 103 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
93 104
94 if (!params.handle) { 105 if (!params.handle) {
95 LOG_ERROR(Service_NVDRV, "Handle is 0"); 106 LOG_ERROR(Service_NVDRV, "Handle is 0");
96 return static_cast<u32>(NvErrCodes::InvalidValue); 107 return NvResult::BadValue;
97 } 108 }
98 109
99 if ((params.align - 1) & params.align) { 110 if ((params.align - 1) & params.align) {
100 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); 111 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
101 return static_cast<u32>(NvErrCodes::InvalidValue); 112 return NvResult::BadValue;
102 } 113 }
103 114
104 const u32 min_alignment = 0x1000; 115 const u32 min_alignment = 0x1000;
@@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
109 auto object = GetObject(params.handle); 120 auto object = GetObject(params.handle);
110 if (!object) { 121 if (!object) {
111 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 122 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
112 return static_cast<u32>(NvErrCodes::InvalidValue); 123 return NvResult::BadValue;
113 } 124 }
114 125
115 if (object->status == Object::Status::Allocated) { 126 if (object->status == Object::Status::Allocated) {
116 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); 127 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
117 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 128 return NvResult::InsufficientMemory;
118 } 129 }
119 130
120 object->flags = params.flags; 131 object->flags = params.flags;
@@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
124 object->status = Object::Status::Allocated; 135 object->status = Object::Status::Allocated;
125 136
126 std::memcpy(output.data(), &params, sizeof(params)); 137 std::memcpy(output.data(), &params, sizeof(params));
127 return 0; 138 return NvResult::Success;
128} 139}
129 140
130u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 141NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
131 IocGetIdParams params; 142 IocGetIdParams params;
132 std::memcpy(&params, input.data(), sizeof(params)); 143 std::memcpy(&params, input.data(), sizeof(params));
133 144
@@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
135 146
136 if (!params.handle) { 147 if (!params.handle) {
137 LOG_ERROR(Service_NVDRV, "Handle is zero"); 148 LOG_ERROR(Service_NVDRV, "Handle is zero");
138 return static_cast<u32>(NvErrCodes::InvalidValue); 149 return NvResult::BadValue;
139 } 150 }
140 151
141 auto object = GetObject(params.handle); 152 auto object = GetObject(params.handle);
142 if (!object) { 153 if (!object) {
143 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 154 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
144 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 155 return NvResult::BadValue;
145 } 156 }
146 157
147 params.id = object->id; 158 params.id = object->id;
148 159
149 std::memcpy(output.data(), &params, sizeof(params)); 160 std::memcpy(output.data(), &params, sizeof(params));
150 return 0; 161 return NvResult::Success;
151} 162}
152 163
153u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { 164NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
154 IocFromIdParams params; 165 IocFromIdParams params;
155 std::memcpy(&params, input.data(), sizeof(params)); 166 std::memcpy(&params, input.data(), sizeof(params));
156 167
@@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
160 [&](const auto& entry) { return entry.second->id == params.id; }); 171 [&](const auto& entry) { return entry.second->id == params.id; });
161 if (itr == handles.end()) { 172 if (itr == handles.end()) {
162 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 173 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
163 return static_cast<u32>(NvErrCodes::InvalidValue); 174 return NvResult::BadValue;
164 } 175 }
165 176
166 auto& object = itr->second; 177 auto& object = itr->second;
167 if (object->status != Object::Status::Allocated) { 178 if (object->status != Object::Status::Allocated) {
168 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 179 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
169 return static_cast<u32>(NvErrCodes::InvalidValue); 180 return NvResult::BadValue;
170 } 181 }
171 182
172 itr->second->refcount++; 183 itr->second->refcount++;
@@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
175 params.handle = itr->first; 186 params.handle = itr->first;
176 187
177 std::memcpy(output.data(), &params, sizeof(params)); 188 std::memcpy(output.data(), &params, sizeof(params));
178 return 0; 189 return NvResult::Success;
179} 190}
180 191
181u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { 192NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
182 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; 193 enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
183 194
184 IocParamParams params; 195 IocParamParams params;
@@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
189 auto object = GetObject(params.handle); 200 auto object = GetObject(params.handle);
190 if (!object) { 201 if (!object) {
191 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 202 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
192 return static_cast<u32>(NvErrCodes::InvalidValue); 203 return NvResult::BadValue;
193 } 204 }
194 205
195 if (object->status != Object::Status::Allocated) { 206 if (object->status != Object::Status::Allocated) {
196 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 207 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
197 return static_cast<u32>(NvErrCodes::OperationNotPermitted); 208 return NvResult::BadValue;
198 } 209 }
199 210
200 switch (static_cast<ParamTypes>(params.param)) { 211 switch (static_cast<ParamTypes>(params.param)) {
@@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
216 } 227 }
217 228
218 std::memcpy(output.data(), &params, sizeof(params)); 229 std::memcpy(output.data(), &params, sizeof(params));
219 return 0; 230 return NvResult::Success;
220} 231}
221 232
222u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 233NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
223 // TODO(Subv): These flags are unconfirmed. 234 // TODO(Subv): These flags are unconfirmed.
224 enum FreeFlags { 235 enum FreeFlags {
225 Freed = 0, 236 Freed = 0,
@@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
234 auto itr = handles.find(params.handle); 245 auto itr = handles.find(params.handle);
235 if (itr == handles.end()) { 246 if (itr == handles.end()) {
236 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 247 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
237 return static_cast<u32>(NvErrCodes::InvalidValue); 248 return NvResult::BadValue;
238 } 249 }
239 if (!itr->second->refcount) { 250 if (!itr->second->refcount) {
240 LOG_ERROR( 251 LOG_ERROR(
241 Service_NVDRV, 252 Service_NVDRV,
242 "There is no references to this object. The object is already freed. handle={:08X}", 253 "There is no references to this object. The object is already freed. handle={:08X}",
243 params.handle); 254 params.handle);
244 return static_cast<u32>(NvErrCodes::InvalidValue); 255 return NvResult::BadValue;
245 } 256 }
246 257
247 itr->second->refcount--; 258 itr->second->refcount--;
@@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
261 handles.erase(params.handle); 272 handles.erase(params.handle);
262 273
263 std::memcpy(output.data(), &params, sizeof(params)); 274 std::memcpy(output.data(), &params, sizeof(params));
264 return 0; 275 return NvResult::Success;
265} 276}
266 277
267} // namespace Service::Nvidia::Devices 278} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 04b9ef540..4484bd79f 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -19,13 +19,15 @@ public:
19 explicit nvmap(Core::System& system); 19 explicit nvmap(Core::System& system);
20 ~nvmap() override; 20 ~nvmap() override;
21 21
22 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
23 NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
24 const std::vector<u8>& inline_input, std::vector<u8>& output) override;
25 NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
26 std::vector<u8>& inline_output) override;
27
22 /// Returns the allocated address of an nvmap object given its handle. 28 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 29 VAddr GetObjectAddress(u32 handle) const;
24 30
25 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
26 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
27 IoctlVersion version) override;
28
29 /// Represents an nvmap object. 31 /// Represents an nvmap object.
30 struct Object { 32 struct Object {
31 enum class Status { Created, Allocated }; 33 enum class Status { Created, Allocated };
@@ -58,76 +60,68 @@ private:
58 /// Mapping of currently allocated handles to the objects they represent. 60 /// Mapping of currently allocated handles to the objects they represent.
59 std::unordered_map<u32, std::shared_ptr<Object>> handles; 61 std::unordered_map<u32, std::shared_ptr<Object>> handles;
60 62
61 enum class IoctlCommand : u32 {
62 Create = 0xC0080101,
63 FromId = 0xC0080103,
64 Alloc = 0xC0200104,
65 Free = 0xC0180105,
66 Param = 0xC00C0109,
67 GetId = 0xC008010E,
68 };
69 struct IocCreateParams { 63 struct IocCreateParams {
70 // Input 64 // Input
71 u32_le size; 65 u32_le size{};
72 // Output 66 // Output
73 u32_le handle; 67 u32_le handle{};
74 }; 68 };
75 static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size"); 69 static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
76 70
77 struct IocFromIdParams { 71 struct IocFromIdParams {
78 // Input 72 // Input
79 u32_le id; 73 u32_le id{};
80 // Output 74 // Output
81 u32_le handle; 75 u32_le handle{};
82 }; 76 };
83 static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size"); 77 static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
84 78
85 struct IocAllocParams { 79 struct IocAllocParams {
86 // Input 80 // Input
87 u32_le handle; 81 u32_le handle{};
88 u32_le heap_mask; 82 u32_le heap_mask{};
89 u32_le flags; 83 u32_le flags{};
90 u32_le align; 84 u32_le align{};
91 u8 kind; 85 u8 kind{};
92 INSERT_PADDING_BYTES(7); 86 INSERT_PADDING_BYTES(7);
93 u64_le addr; 87 u64_le addr{};
94 }; 88 };
95 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); 89 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
96 90
97 struct IocFreeParams { 91 struct IocFreeParams {
98 u32_le handle; 92 u32_le handle{};
99 INSERT_PADDING_BYTES(4); 93 INSERT_PADDING_BYTES(4);
100 u64_le address; 94 u64_le address{};
101 u32_le size; 95 u32_le size{};
102 u32_le flags; 96 u32_le flags{};
103 }; 97 };
104 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); 98 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
105 99
106 struct IocParamParams { 100 struct IocParamParams {
107 // Input 101 // Input
108 u32_le handle; 102 u32_le handle{};
109 u32_le param; 103 u32_le param{};
110 // Output 104 // Output
111 u32_le result; 105 u32_le result{};
112 }; 106 };
113 static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size"); 107 static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
114 108
115 struct IocGetIdParams { 109 struct IocGetIdParams {
116 // Output 110 // Output
117 u32_le id; 111 u32_le id{};
118 // Input 112 // Input
119 u32_le handle; 113 u32_le handle{};
120 }; 114 };
121 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 115 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
122 116
123 u32 CreateObject(u32 size); 117 u32 CreateObject(u32 size);
124 118
125 u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 119 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
126 u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 120 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
127 u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 121 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
128 u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 122 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
129 u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output); 123 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
130 u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output); 124 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
131}; 125};
132 126
133} // namespace Service::Nvidia::Devices 127} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 88fbfa9b0..f6c38e853 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -23,124 +23,170 @@ void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
23void NVDRV::Open(Kernel::HLERequestContext& ctx) { 23void NVDRV::Open(Kernel::HLERequestContext& ctx) {
24 LOG_DEBUG(Service_NVDRV, "called"); 24 LOG_DEBUG(Service_NVDRV, "called");
25 25
26 if (!is_initialized) {
27 ServiceError(ctx, NvResult::NotInitialized);
28 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
29 return;
30 }
31
26 const auto& buffer = ctx.ReadBuffer(); 32 const auto& buffer = ctx.ReadBuffer();
27 std::string device_name(buffer.begin(), buffer.end()); 33 const std::string device_name(buffer.begin(), buffer.end());
34 DeviceFD fd = nvdrv->Open(device_name);
28 35
29 u32 fd = nvdrv->Open(device_name);
30 IPC::ResponseBuilder rb{ctx, 4}; 36 IPC::ResponseBuilder rb{ctx, 4};
31 rb.Push(RESULT_SUCCESS); 37 rb.Push(RESULT_SUCCESS);
32 rb.Push<u32>(fd); 38 rb.Push<DeviceFD>(fd);
33 rb.Push<u32>(0); 39 rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
40}
41
42void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
43 IPC::ResponseBuilder rb{ctx, 3};
44 rb.Push(RESULT_SUCCESS);
45 rb.PushEnum(result);
34} 46}
35 47
36void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { 48void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
37 IPC::RequestParser rp{ctx}; 49 IPC::RequestParser rp{ctx};
38 u32 fd = rp.Pop<u32>(); 50 const auto fd = rp.Pop<DeviceFD>();
39 u32 command = rp.Pop<u32>(); 51 const auto command = rp.PopRaw<Ioctl>();
40 52 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
41 /// Ioctl 3 has 2 outputs, first in the input params, second is the result 53
42 std::vector<u8> output(ctx.GetWriteBufferSize(0)); 54 if (!is_initialized) {
43 std::vector<u8> output2; 55 ServiceError(ctx, NvResult::NotInitialized);
44 if (version == IoctlVersion::Version3) { 56 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
45 output2.resize((ctx.GetWriteBufferSize(1))); 57 return;
46 } 58 }
47 59
48 /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer. 60 // Check device
49 /// KickOfPB uses this 61 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
50 auto input = ctx.ReadBuffer(0); 62 const auto input_buffer = ctx.ReadBuffer(0);
51 63
52 std::vector<u8> input2; 64 const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
53 if (version == IoctlVersion::Version2) {
54 input2 = ctx.ReadBuffer(1);
55 }
56 65
57 IoctlCtrl ctrl{}; 66 if (command.is_out != 0) {
58 67 ctx.WriteBuffer(output_buffer);
59 u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
60
61 if (ctrl.must_delay) {
62 ctrl.fresh_call = false;
63 ctx.SleepClientThread(
64 "NVServices::DelayedResponse", ctrl.timeout,
65 [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
66 Kernel::ThreadWakeupReason reason) {
67 IoctlCtrl ctrl2{ctrl};
68 std::vector<u8> tmp_output = output;
69 std::vector<u8> tmp_output2 = output2;
70 const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
71 tmp_output2, ctrl2, version);
72 ctx_.WriteBuffer(tmp_output, 0);
73 if (version == IoctlVersion::Version3) {
74 ctx_.WriteBuffer(tmp_output2, 1);
75 }
76 IPC::ResponseBuilder rb{ctx_, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(ioctl_result);
79 },
80 nvdrv->GetEventWriteable(ctrl.event_id));
81 } else {
82 ctx.WriteBuffer(output);
83 if (version == IoctlVersion::Version3) {
84 ctx.WriteBuffer(output2, 1);
85 }
86 } 68 }
69
87 IPC::ResponseBuilder rb{ctx, 3}; 70 IPC::ResponseBuilder rb{ctx, 3};
88 rb.Push(RESULT_SUCCESS); 71 rb.Push(RESULT_SUCCESS);
89 rb.Push(result); 72 rb.PushEnum(nv_result);
90}
91
92void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
93 LOG_DEBUG(Service_NVDRV, "called");
94 IoctlBase(ctx, IoctlVersion::Version1);
95} 73}
96 74
97void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { 75void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
98 LOG_DEBUG(Service_NVDRV, "called"); 76 IPC::RequestParser rp{ctx};
99 IoctlBase(ctx, IoctlVersion::Version2); 77 const auto fd = rp.Pop<DeviceFD>();
78 const auto command = rp.PopRaw<Ioctl>();
79 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
80
81 if (!is_initialized) {
82 ServiceError(ctx, NvResult::NotInitialized);
83 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
84 return;
85 }
86
87 const auto input_buffer = ctx.ReadBuffer(0);
88 const auto input_inlined_buffer = ctx.ReadBuffer(1);
89 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
90
91 const auto nv_result =
92 nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
93
94 if (command.is_out != 0) {
95 ctx.WriteBuffer(output_buffer);
96 }
97
98 IPC::ResponseBuilder rb{ctx, 3};
99 rb.Push(RESULT_SUCCESS);
100 rb.PushEnum(nv_result);
100} 101}
101 102
102void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { 103void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
103 LOG_DEBUG(Service_NVDRV, "called"); 104 IPC::RequestParser rp{ctx};
104 IoctlBase(ctx, IoctlVersion::Version3); 105 const auto fd = rp.Pop<DeviceFD>();
106 const auto command = rp.PopRaw<Ioctl>();
107 LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
108
109 if (!is_initialized) {
110 ServiceError(ctx, NvResult::NotInitialized);
111 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
112 return;
113 }
114
115 const auto input_buffer = ctx.ReadBuffer(0);
116 std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
117 std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
118
119 const auto nv_result =
120 nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
121
122 if (command.is_out != 0) {
123 ctx.WriteBuffer(output_buffer, 0);
124 ctx.WriteBuffer(output_buffer_inline, 1);
125 }
126
127 IPC::ResponseBuilder rb{ctx, 3};
128 rb.Push(RESULT_SUCCESS);
129 rb.PushEnum(nv_result);
105} 130}
106 131
107void NVDRV::Close(Kernel::HLERequestContext& ctx) { 132void NVDRV::Close(Kernel::HLERequestContext& ctx) {
108 LOG_DEBUG(Service_NVDRV, "called"); 133 LOG_DEBUG(Service_NVDRV, "called");
109 134
110 IPC::RequestParser rp{ctx}; 135 if (!is_initialized) {
111 u32 fd = rp.Pop<u32>(); 136 ServiceError(ctx, NvResult::NotInitialized);
137 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
138 return;
139 }
112 140
113 auto result = nvdrv->Close(fd); 141 IPC::RequestParser rp{ctx};
142 const auto fd = rp.Pop<DeviceFD>();
143 const auto result = nvdrv->Close(fd);
114 144
115 IPC::ResponseBuilder rb{ctx, 2}; 145 IPC::ResponseBuilder rb{ctx, 3};
116 rb.Push(result); 146 rb.Push(RESULT_SUCCESS);
147 rb.PushEnum(result);
117} 148}
118 149
119void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { 150void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
120 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 151 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
121 152
153 is_initialized = true;
154
122 IPC::ResponseBuilder rb{ctx, 3}; 155 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(RESULT_SUCCESS); 156 rb.Push(RESULT_SUCCESS);
124 rb.Push<u32>(0); 157 rb.PushEnum(NvResult::Success);
125} 158}
126 159
127void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 160void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
128 IPC::RequestParser rp{ctx}; 161 IPC::RequestParser rp{ctx};
129 u32 fd = rp.Pop<u32>(); 162 const auto fd = rp.Pop<DeviceFD>();
130 // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 163 const auto event_id = rp.Pop<u32>() & 0x00FF;
131 u32 event_id = rp.Pop<u32>() & 0x000000FF;
132 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); 164 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
133 165
134 IPC::ResponseBuilder rb{ctx, 3, 1}; 166 if (!is_initialized) {
135 rb.Push(RESULT_SUCCESS); 167 ServiceError(ctx, NvResult::NotInitialized);
168 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
169 return;
170 }
171
172 const auto nv_result = nvdrv->VerifyFD(fd);
173 if (nv_result != NvResult::Success) {
174 LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
175 ServiceError(ctx, nv_result);
176 return;
177 }
178
136 if (event_id < MaxNvEvents) { 179 if (event_id < MaxNvEvents) {
180 IPC::ResponseBuilder rb{ctx, 3, 1};
181 rb.Push(RESULT_SUCCESS);
137 auto event = nvdrv->GetEvent(event_id); 182 auto event = nvdrv->GetEvent(event_id);
138 event->Clear(); 183 event->Clear();
139 rb.PushCopyObjects(event); 184 rb.PushCopyObjects(event);
140 rb.Push<u32>(NvResult::Success); 185 rb.PushEnum(NvResult::Success);
141 } else { 186 } else {
142 rb.Push<u32>(0); 187 IPC::ResponseBuilder rb{ctx, 3};
143 rb.Push<u32>(NvResult::BadParameter); 188 rb.Push(RESULT_SUCCESS);
189 rb.PushEnum(NvResult::BadParameter);
144 } 190 }
145} 191}
146 192
@@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
151 197
152 IPC::ResponseBuilder rb{ctx, 3}; 198 IPC::ResponseBuilder rb{ctx, 3};
153 rb.Push(RESULT_SUCCESS); 199 rb.Push(RESULT_SUCCESS);
154 rb.Push<u32>(0); 200 rb.PushEnum(NvResult::Success);
155} 201}
156 202
157void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) { 203void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
@@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct
164void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { 210void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
165 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 211 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
166 212
167 IPC::ResponseBuilder rb{ctx, 2}; 213 IPC::ResponseBuilder rb{ctx, 3};
168 rb.Push(RESULT_SUCCESS); 214 rb.Push(RESULT_SUCCESS);
215 rb.PushEnum(NvResult::Success);
169} 216}
170 217
171void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { 218void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
181 : ServiceFramework(name), nvdrv(std::move(nvdrv)) { 228 : ServiceFramework(name), nvdrv(std::move(nvdrv)) {
182 static const FunctionInfo functions[] = { 229 static const FunctionInfo functions[] = {
183 {0, &NVDRV::Open, "Open"}, 230 {0, &NVDRV::Open, "Open"},
184 {1, &NVDRV::Ioctl, "Ioctl"}, 231 {1, &NVDRV::Ioctl1, "Ioctl"},
185 {2, &NVDRV::Close, "Close"}, 232 {2, &NVDRV::Close, "Close"},
186 {3, &NVDRV::Initialize, "Initialize"}, 233 {3, &NVDRV::Initialize, "Initialize"},
187 {4, &NVDRV::QueryEvent, "QueryEvent"}, 234 {4, &NVDRV::QueryEvent, "QueryEvent"},
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 72e17a728..e05f905ae 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -23,7 +23,7 @@ public:
23 23
24private: 24private:
25 void Open(Kernel::HLERequestContext& ctx); 25 void Open(Kernel::HLERequestContext& ctx);
26 void Ioctl(Kernel::HLERequestContext& ctx); 26 void Ioctl1(Kernel::HLERequestContext& ctx);
27 void Ioctl2(Kernel::HLERequestContext& ctx); 27 void Ioctl2(Kernel::HLERequestContext& ctx);
28 void Ioctl3(Kernel::HLERequestContext& ctx); 28 void Ioctl3(Kernel::HLERequestContext& ctx);
29 void Close(Kernel::HLERequestContext& ctx); 29 void Close(Kernel::HLERequestContext& ctx);
@@ -33,11 +33,13 @@ private:
33 void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx); 33 void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
34 void GetStatus(Kernel::HLERequestContext& ctx); 34 void GetStatus(Kernel::HLERequestContext& ctx);
35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); 35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
36 void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version); 36
37 void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
37 38
38 std::shared_ptr<Module> nvdrv; 39 std::shared_ptr<Module> nvdrv;
39 40
40 u64 pid{}; 41 u64 pid{};
42 bool is_initialized{};
41}; 43};
42 44
43} // namespace Service::Nvidia 45} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 529b03471..3294bc0e7 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,12 +1,16 @@
1#pragma once 1#pragma once
2 2
3#include <array> 3#include <array>
4#include "common/bit_field.h"
4#include "common/common_types.h" 5#include "common/common_types.h"
5 6
6namespace Service::Nvidia { 7namespace Service::Nvidia {
7 8
8constexpr u32 MaxSyncPoints = 192; 9constexpr u32 MaxSyncPoints = 192;
9constexpr u32 MaxNvEvents = 64; 10constexpr u32 MaxNvEvents = 64;
11using DeviceFD = s32;
12
13constexpr DeviceFD INVALID_NVDRV_FD = -1;
10 14
11struct Fence { 15struct Fence {
12 s32 id; 16 s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
20 std::array<Fence, 4> fences; 24 std::array<Fence, 4> fences;
21}; 25};
22 26
23enum NvResult : u32 { 27enum class NvResult : u32 {
24 Success = 0, 28 Success = 0x0,
25 BadParameter = 4, 29 NotImplemented = 0x1,
26 Timeout = 5, 30 NotSupported = 0x2,
27 ResourceError = 15, 31 NotInitialized = 0x3,
32 BadParameter = 0x4,
33 Timeout = 0x5,
34 InsufficientMemory = 0x6,
35 ReadOnlyAttribute = 0x7,
36 InvalidState = 0x8,
37 InvalidAddress = 0x9,
38 InvalidSize = 0xA,
39 BadValue = 0xB,
40 AlreadyAllocated = 0xD,
41 Busy = 0xE,
42 ResourceError = 0xF,
43 CountMismatch = 0x10,
44 OverFlow = 0x11,
45 InsufficientTransferMemory = 0x1000,
46 InsufficientVideoMemory = 0x10000,
47 BadSurfaceColorScheme = 0x10001,
48 InvalidSurface = 0x10002,
49 SurfaceNotSupported = 0x10003,
50 DispInitFailed = 0x20000,
51 DispAlreadyAttached = 0x20001,
52 DispTooManyDisplays = 0x20002,
53 DispNoDisplaysAttached = 0x20003,
54 DispModeNotSupported = 0x20004,
55 DispNotFound = 0x20005,
56 DispAttachDissallowed = 0x20006,
57 DispTypeNotSupported = 0x20007,
58 DispAuthenticationFailed = 0x20008,
59 DispNotAttached = 0x20009,
60 DispSamePwrState = 0x2000A,
61 DispEdidFailure = 0x2000B,
62 DispDsiReadAckError = 0x2000C,
63 DispDsiReadInvalidResp = 0x2000D,
64 FileWriteFailed = 0x30000,
65 FileReadFailed = 0x30001,
66 EndOfFile = 0x30002,
67 FileOperationFailed = 0x30003,
68 DirOperationFailed = 0x30004,
69 EndOfDirList = 0x30005,
70 ConfigVarNotFound = 0x30006,
71 InvalidConfigVar = 0x30007,
72 LibraryNotFound = 0x30008,
73 SymbolNotFound = 0x30009,
74 MemoryMapFailed = 0x3000A,
75 IoctlFailed = 0x3000F,
76 AccessDenied = 0x30010,
77 DeviceNotFound = 0x30011,
78 KernelDriverNotFound = 0x30012,
79 FileNotFound = 0x30013,
80 PathAlreadyExists = 0x30014,
81 ModuleNotPresent = 0xA000E,
28}; 82};
29 83
30enum class EventState { 84enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
34 Busy = 3, 88 Busy = 3,
35}; 89};
36 90
37enum class IoctlVersion : u32 { 91union Ioctl {
38 Version1, 92 u32_le raw;
39 Version2, 93 BitField<0, 8, u32> cmd;
40 Version3, 94 BitField<8, 8, u32> group;
41}; 95 BitField<16, 14, u32> length;
42 96 BitField<30, 1, u32> is_in;
43struct IoctlCtrl { 97 BitField<31, 1, u32> is_out;
44 // First call done to the servioce for services that call itself again after a call.
45 bool fresh_call{true};
46 // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
47 bool must_delay{};
48 // Timeout for the delay
49 s64 timeout{};
50 // NV Event Id
51 s32 event_id{-1};
52}; 98};
53 99
54} // namespace Service::Nvidia 100} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 046a1f28c..bdbbedd0d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -62,36 +62,101 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
62 62
63Module::~Module() = default; 63Module::~Module() = default;
64 64
65u32 Module::Open(const std::string& device_name) { 65NvResult Module::VerifyFD(DeviceFD fd) const {
66 ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}", 66 if (fd < 0) {
67 device_name); 67 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
68 return NvResult::InvalidState;
69 }
70
71 if (open_files.find(fd) == open_files.end()) {
72 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
73 return NvResult::NotImplemented;
74 }
75
76 return NvResult::Success;
77}
78
79DeviceFD Module::Open(const std::string& device_name) {
80 if (devices.find(device_name) == devices.end()) {
81 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
82 return INVALID_NVDRV_FD;
83 }
68 84
69 auto device = devices[device_name]; 85 auto device = devices[device_name];
70 const u32 fd = next_fd++; 86 const DeviceFD fd = next_fd++;
71 87
72 open_files[fd] = std::move(device); 88 open_files[fd] = std::move(device);
73 89
74 return fd; 90 return fd;
75} 91}
76 92
77u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, 93NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
78 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 94 std::vector<u8>& output) {
79 IoctlVersion version) { 95 if (fd < 0) {
80 auto itr = open_files.find(fd); 96 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
81 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 97 return NvResult::InvalidState;
98 }
82 99
83 auto& device = itr->second; 100 const auto itr = open_files.find(fd);
84 return device->ioctl({command}, input, input2, output, output2, ctrl, version); 101
102 if (itr == open_files.end()) {
103 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
104 return NvResult::NotImplemented;
105 }
106
107 return itr->second->Ioctl1(command, input, output);
85} 108}
86 109
87ResultCode Module::Close(u32 fd) { 110NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
88 auto itr = open_files.find(fd); 111 const std::vector<u8>& inline_input, std::vector<u8>& output) {
89 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 112 if (fd < 0) {
113 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
114 return NvResult::InvalidState;
115 }
116
117 const auto itr = open_files.find(fd);
118
119 if (itr == open_files.end()) {
120 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
121 return NvResult::NotImplemented;
122 }
123
124 return itr->second->Ioctl2(command, input, inline_input, output);
125}
126
127NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
128 std::vector<u8>& output, std::vector<u8>& inline_output) {
129 if (fd < 0) {
130 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
131 return NvResult::InvalidState;
132 }
133
134 const auto itr = open_files.find(fd);
135
136 if (itr == open_files.end()) {
137 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
138 return NvResult::NotImplemented;
139 }
140
141 return itr->second->Ioctl3(command, input, output, inline_output);
142}
143
144NvResult Module::Close(DeviceFD fd) {
145 if (fd < 0) {
146 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
147 return NvResult::InvalidState;
148 }
149
150 const auto itr = open_files.find(fd);
151
152 if (itr == open_files.end()) {
153 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
154 return NvResult::NotImplemented;
155 }
90 156
91 open_files.erase(itr); 157 open_files.erase(itr);
92 158
93 // TODO(flerovium): return correct result code if operation failed. 159 return NvResult::Success;
94 return RESULT_SUCCESS;
95} 160}
96 161
97void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { 162void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index f3d863dac..7654bb026 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -112,14 +112,23 @@ public:
112 return std::static_pointer_cast<T>(itr->second); 112 return std::static_pointer_cast<T>(itr->second);
113 } 113 }
114 114
115 NvResult VerifyFD(DeviceFD fd) const;
116
115 /// Opens a device node and returns a file descriptor to it. 117 /// Opens a device node and returns a file descriptor to it.
116 u32 Open(const std::string& device_name); 118 DeviceFD Open(const std::string& device_name);
119
117 /// Sends an ioctl command to the specified file descriptor. 120 /// Sends an ioctl command to the specified file descriptor.
118 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, 121 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
119 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, 122 std::vector<u8>& output);
120 IoctlVersion version); 123
124 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
125 const std::vector<u8>& inline_input, std::vector<u8>& output);
126
127 NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
128 std::vector<u8>& output, std::vector<u8>& inline_output);
129
121 /// Closes a device file descriptor and returns operation success. 130 /// Closes a device file descriptor and returns operation success.
122 ResultCode Close(u32 fd); 131 NvResult Close(DeviceFD fd);
123 132
124 void SignalSyncpt(const u32 syncpoint_id, const u32 value); 133 void SignalSyncpt(const u32 syncpoint_id, const u32 value);
125 134
@@ -132,10 +141,10 @@ private:
132 SyncpointManager syncpoint_manager; 141 SyncpointManager syncpoint_manager;
133 142
134 /// Id to use for the next open file descriptor. 143 /// Id to use for the next open file descriptor.
135 u32 next_fd = 1; 144 DeviceFD next_fd = 1;
136 145
137 /// Mapping of file descriptors to the devices they reference. 146 /// Mapping of file descriptors to the devices they reference.
138 std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files; 147 std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
139 148
140 /// Mapping of device node names to their implementation. 149 /// Mapping of device node names to their implementation.
141 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; 150 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index fbfda2d5b..fb4979af2 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -188,17 +188,19 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
188 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
189} 189}
190 190
191/// Initialize ServiceManager 191/// Initialize Services
192void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 192Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
193 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
194
193 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 195 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
194 // here and pass it into the respective InstallInterfaces functions. 196 // here and pass it into the respective InstallInterfaces functions.
195 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); 197
196 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 198 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
197 199
198 SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); 200 SM::ServiceManager::InstallInterfaces(sm, system.Kernel());
199 201
200 Account::InstallInterfaces(system); 202 Account::InstallInterfaces(system);
201 AM::InstallInterfaces(*sm, nv_flinger, system); 203 AM::InstallInterfaces(*sm, *nv_flinger, system);
202 AOC::InstallInterfaces(*sm, system); 204 AOC::InstallInterfaces(*sm, system);
203 APM::InstallInterfaces(system); 205 APM::InstallInterfaces(system);
204 Audio::InstallInterfaces(*sm, system); 206 Audio::InstallInterfaces(*sm, system);
@@ -246,14 +248,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
246 SSL::InstallInterfaces(*sm); 248 SSL::InstallInterfaces(*sm);
247 Time::InstallInterfaces(system); 249 Time::InstallInterfaces(system);
248 USB::InstallInterfaces(*sm); 250 USB::InstallInterfaces(*sm);
249 VI::InstallInterfaces(*sm, nv_flinger); 251 VI::InstallInterfaces(*sm, *nv_flinger);
250 WLAN::InstallInterfaces(*sm); 252 WLAN::InstallInterfaces(*sm);
251
252 LOG_DEBUG(Service, "initialized OK");
253} 253}
254 254
255/// Shutdown ServiceManager 255Services::~Services() = default;
256void Shutdown() { 256
257 LOG_DEBUG(Service, "shutdown OK");
258}
259} // namespace Service 257} // namespace Service
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index a01ef3353..ed4792289 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -29,7 +29,11 @@ namespace Service {
29 29
30namespace FileSystem { 30namespace FileSystem {
31class FileSystemController; 31class FileSystemController;
32} // namespace FileSystem 32}
33
34namespace NVFlinger {
35class NVFlinger;
36}
33 37
34namespace SM { 38namespace SM {
35class ServiceManager; 39class ServiceManager;
@@ -181,10 +185,17 @@ private:
181 } 185 }
182}; 186};
183 187
184/// Initialize ServiceManager 188/**
185void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); 189 * The purpose of this class is to own any objects that need to be shared across the other service
190 * implementations. Will be torn down when the global system instance is shutdown.
191 */
192class Services final {
193public:
194 explicit Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system);
195 ~Services();
186 196
187/// Shutdown ServiceManager 197private:
188void Shutdown(); 198 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
199};
189 200
190} // namespace Service 201} // namespace Service
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 55e00dd93..86bd604f4 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -492,8 +492,8 @@ private:
492 492
493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 493class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
494public: 494public:
495 explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 495 explicit IHOSBinderDriver(NVFlinger::NVFlinger& nv_flinger)
496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { 496 : ServiceFramework("IHOSBinderDriver"), nv_flinger(nv_flinger) {
497 static const FunctionInfo functions[] = { 497 static const FunctionInfo functions[] = {
498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 498 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 499 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -530,8 +530,8 @@ private:
530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 530 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
531 static_cast<u32>(transaction), flags); 531 static_cast<u32>(transaction), flags);
532 532
533 const auto guard = nv_flinger->Lock(); 533 const auto guard = nv_flinger.Lock();
534 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 534 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
535 535
536 switch (transaction) { 536 switch (transaction) {
537 case TransactionId::Connect: { 537 case TransactionId::Connect: {
@@ -570,8 +570,8 @@ private:
570 [=, this](std::shared_ptr<Kernel::Thread> thread, 570 [=, this](std::shared_ptr<Kernel::Thread> thread,
571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { 571 Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
572 // Repeat TransactParcel DequeueBuffer when a buffer is available 572 // Repeat TransactParcel DequeueBuffer when a buffer is available
573 const auto guard = nv_flinger->Lock(); 573 const auto guard = nv_flinger.Lock();
574 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 574 auto& buffer_queue = nv_flinger.FindBufferQueue(id);
575 auto result = buffer_queue.DequeueBuffer(width, height); 575 auto result = buffer_queue.DequeueBuffer(width, height);
576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 576 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
577 577
@@ -676,7 +676,7 @@ private:
676 676
677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 677 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
678 678
679 const auto& buffer_queue = nv_flinger->FindBufferQueue(id); 679 const auto& buffer_queue = nv_flinger.FindBufferQueue(id);
680 680
681 // TODO(Subv): Find out what this actually is. 681 // TODO(Subv): Find out what this actually is.
682 IPC::ResponseBuilder rb{ctx, 2, 1}; 682 IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -684,8 +684,8 @@ private:
684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent()); 684 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
685 } 685 }
686 686
687 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 687 NVFlinger::NVFlinger& nv_flinger;
688}; // namespace VI 688};
689 689
690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 690class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
691public: 691public:
@@ -790,8 +790,8 @@ private:
790 790
791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { 791class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
792public: 792public:
793 explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 793 explicit IManagerDisplayService(NVFlinger::NVFlinger& nv_flinger)
794 : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { 794 : ServiceFramework("IManagerDisplayService"), nv_flinger(nv_flinger) {
795 // clang-format off 795 // clang-format off
796 static const FunctionInfo functions[] = { 796 static const FunctionInfo functions[] = {
797 {200, nullptr, "AllocateProcessHeapBlock"}, 797 {200, nullptr, "AllocateProcessHeapBlock"},
@@ -893,7 +893,7 @@ private:
893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", 893 "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
894 unknown, display, aruid); 894 unknown, display, aruid);
895 895
896 const auto layer_id = nv_flinger->CreateLayer(display); 896 const auto layer_id = nv_flinger.CreateLayer(display);
897 if (!layer_id) { 897 if (!layer_id) {
898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display); 898 LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
899 IPC::ResponseBuilder rb{ctx, 2}; 899 IPC::ResponseBuilder rb{ctx, 2};
@@ -930,12 +930,12 @@ private:
930 rb.Push(RESULT_SUCCESS); 930 rb.Push(RESULT_SUCCESS);
931 } 931 }
932 932
933 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 933 NVFlinger::NVFlinger& nv_flinger;
934}; 934};
935 935
936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 936class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
937public: 937public:
938 explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 938 explicit IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger);
939 939
940private: 940private:
941 enum class ConvertedScaleMode : u64 { 941 enum class ConvertedScaleMode : u64 {
@@ -1010,7 +1010,7 @@ private:
1010 1010
1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); 1011 ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
1012 1012
1013 const auto display_id = nv_flinger->OpenDisplay(name); 1013 const auto display_id = nv_flinger.OpenDisplay(name);
1014 if (!display_id) { 1014 if (!display_id) {
1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name); 1015 LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
1016 IPC::ResponseBuilder rb{ctx, 2}; 1016 IPC::ResponseBuilder rb{ctx, 2};
@@ -1110,7 +1110,7 @@ private:
1110 1110
1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); 1111 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
1112 1112
1113 const auto display_id = nv_flinger->OpenDisplay(display_name); 1113 const auto display_id = nv_flinger.OpenDisplay(display_name);
1114 if (!display_id) { 1114 if (!display_id) {
1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id); 1115 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
1116 IPC::ResponseBuilder rb{ctx, 2}; 1116 IPC::ResponseBuilder rb{ctx, 2};
@@ -1118,7 +1118,7 @@ private:
1118 return; 1118 return;
1119 } 1119 }
1120 1120
1121 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id); 1121 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(*display_id, layer_id);
1122 if (!buffer_queue_id) { 1122 if (!buffer_queue_id) {
1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id); 1123 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
1124 IPC::ResponseBuilder rb{ctx, 2}; 1124 IPC::ResponseBuilder rb{ctx, 2};
@@ -1138,7 +1138,7 @@ private:
1138 1138
1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id); 1139 LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}", layer_id);
1140 1140
1141 nv_flinger->CloseLayer(layer_id); 1141 nv_flinger.CloseLayer(layer_id);
1142 1142
1143 IPC::ResponseBuilder rb{ctx, 2}; 1143 IPC::ResponseBuilder rb{ctx, 2};
1144 rb.Push(RESULT_SUCCESS); 1144 rb.Push(RESULT_SUCCESS);
@@ -1154,7 +1154,7 @@ private:
1154 1154
1155 // TODO(Subv): What's the difference between a Stray and a Managed layer? 1155 // TODO(Subv): What's the difference between a Stray and a Managed layer?
1156 1156
1157 const auto layer_id = nv_flinger->CreateLayer(display_id); 1157 const auto layer_id = nv_flinger.CreateLayer(display_id);
1158 if (!layer_id) { 1158 if (!layer_id) {
1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1159 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
1160 IPC::ResponseBuilder rb{ctx, 2}; 1160 IPC::ResponseBuilder rb{ctx, 2};
@@ -1162,7 +1162,7 @@ private:
1162 return; 1162 return;
1163 } 1163 }
1164 1164
1165 const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id); 1165 const auto buffer_queue_id = nv_flinger.FindBufferQueueId(display_id, *layer_id);
1166 if (!buffer_queue_id) { 1166 if (!buffer_queue_id) {
1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 1167 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
1168 IPC::ResponseBuilder rb{ctx, 2}; 1168 IPC::ResponseBuilder rb{ctx, 2};
@@ -1193,7 +1193,7 @@ private:
1193 1193
1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); 1194 LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
1195 1195
1196 const auto vsync_event = nv_flinger->FindVsyncEvent(display_id); 1196 const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
1197 if (!vsync_event) { 1197 if (!vsync_event) {
1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id); 1198 LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
1199 IPC::ResponseBuilder rb{ctx, 2}; 1199 IPC::ResponseBuilder rb{ctx, 2};
@@ -1258,12 +1258,11 @@ private:
1258 } 1258 }
1259 } 1259 }
1260 1260
1261 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1261 NVFlinger::NVFlinger& nv_flinger;
1262}; 1262};
1263 1263
1264IApplicationDisplayService::IApplicationDisplayService( 1264IApplicationDisplayService::IApplicationDisplayService(NVFlinger::NVFlinger& nv_flinger)
1265 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 1265 : ServiceFramework("IApplicationDisplayService"), nv_flinger(nv_flinger) {
1266 : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
1267 static const FunctionInfo functions[] = { 1266 static const FunctionInfo functions[] = {
1268 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, 1267 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1269 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, 1268 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
@@ -1304,8 +1303,7 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1304 return false; 1303 return false;
1305} 1304}
1306 1305
1307void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 1306void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
1308 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
1309 Permission permission) { 1307 Permission permission) {
1310 IPC::RequestParser rp{ctx}; 1308 IPC::RequestParser rp{ctx};
1311 const auto policy = rp.PopEnum<Policy>(); 1309 const auto policy = rp.PopEnum<Policy>();
@@ -1319,11 +1317,10 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
1319 1317
1320 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1318 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1321 rb.Push(RESULT_SUCCESS); 1319 rb.Push(RESULT_SUCCESS);
1322 rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger)); 1320 rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
1323} 1321}
1324 1322
1325void InstallInterfaces(SM::ServiceManager& service_manager, 1323void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger) {
1326 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
1327 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager); 1324 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
1328 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager); 1325 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
1329 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager); 1326 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 6b66f8b81..5229fa753 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -43,12 +43,11 @@ enum class Policy {
43}; 43};
44 44
45namespace detail { 45namespace detail {
46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, 46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, NVFlinger::NVFlinger& nv_flinger,
47 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission); 47 Permission permission);
48} // namespace detail 48} // namespace detail
49 49
50/// Registers all VI services with the specified service manager. 50/// Registers all VI services with the specified service manager.
51void InstallInterfaces(SM::ServiceManager& service_manager, 51void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nv_flinger);
52 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
53 52
54} // namespace Service::VI 53} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 06070087f..41da3ee93 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_M::VI_M(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:m"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 13 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 290e06689..ee2489874 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_M final : public ServiceFramework<VI_M> { 19class VI_M final : public ServiceFramework<VI_M> {
20public: 20public:
21 explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_M(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_M() override; 22 ~VI_M() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 57c596cc4..6acb51e2a 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_S::VI_S(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:s"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 13 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 47804dc0b..6790673ab 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_S final : public ServiceFramework<VI_S> { 19class VI_S final : public ServiceFramework<VI_S> {
20public: 20public:
21 explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_S(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_S() override; 22 ~VI_S() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 6b7329345..44e00a4f6 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,7 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_U::VI_U(NVFlinger::NVFlinger& nv_flinger) : ServiceFramework{"vi:u"}, nv_flinger{nv_flinger} {
12 : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 13 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 14 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 19bdb73b0..b59f986f0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -18,13 +18,13 @@ namespace Service::VI {
18 18
19class VI_U final : public ServiceFramework<VI_U> { 19class VI_U final : public ServiceFramework<VI_U> {
20public: 20public:
21 explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_U(NVFlinger::NVFlinger& nv_flinger);
22 ~VI_U() override; 22 ~VI_U() override;
23 23
24private: 24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx); 25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26 26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 27 NVFlinger::NVFlinger& nv_flinger;
28}; 28};
29 29
30} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 394a1bf26..2002dc4f2 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -114,7 +114,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
114 } 114 }
115 115
116 if (override_update) { 116 if (override_update) {
117 const FileSys::PatchManager patch_manager(metadata.GetTitleID()); 117 const FileSys::PatchManager patch_manager(
118 metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
118 dir = patch_manager.PatchExeFS(dir); 119 dir = patch_manager.PatchExeFS(dir);
119 } 120 }
120 121
@@ -160,7 +161,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
160 modules.clear(); 161 modules.clear();
161 const VAddr base_address{process.PageTable().GetCodeRegionStart()}; 162 const VAddr base_address{process.PageTable().GetCodeRegionStart()};
162 VAddr next_load_addr{base_address}; 163 VAddr next_load_addr{base_address};
163 const FileSys::PatchManager pm{metadata.GetTitleID()}; 164 const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
165 system.GetContentProvider()};
164 for (const auto& module : static_modules) { 166 for (const auto& module : static_modules) {
165 const FileSys::VirtualFile module_file{dir->GetFile(module)}; 167 const FileSys::VirtualFile module_file{dir->GetFile(module)};
166 if (!module_file) { 168 if (!module_file) {
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 9bc3a8840..d91c15561 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/string_util.h" 12#include "common/string_util.h"
13#include "core/core.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/loader/deconstructed_rom_directory.h" 15#include "core/loader/deconstructed_rom_directory.h"
15#include "core/loader/elf.h" 16#include "core/loader/elf.h"
@@ -194,15 +195,15 @@ AppLoader::~AppLoader() = default;
194 195
195/** 196/**
196 * Get a loader for a file with a specific type 197 * Get a loader for a file with a specific type
197 * @param file The file to load 198 * @param system The system context to use.
198 * @param type The type of the file 199 * @param file The file to retrieve the loader for
199 * @param file the file to retrieve the loader for 200 * @param type The file type
200 * @param type the file type 201 * @param program_index Specifies the index within the container of the program to launch.
201 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 202 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
202 */ 203 */
203static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) { 204static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
205 FileType type, std::size_t program_index) {
204 switch (type) { 206 switch (type) {
205
206 // Standard ELF file format. 207 // Standard ELF file format.
207 case FileType::ELF: 208 case FileType::ELF:
208 return std::make_unique<AppLoader_ELF>(std::move(file)); 209 return std::make_unique<AppLoader_ELF>(std::move(file));
@@ -221,7 +222,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
221 222
222 // NX XCI (nX Card Image) file format. 223 // NX XCI (nX Card Image) file format.
223 case FileType::XCI: 224 case FileType::XCI:
224 return std::make_unique<AppLoader_XCI>(std::move(file)); 225 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
226 system.GetContentProvider(), program_index);
225 227
226 // NX NAX (NintendoAesXts) file format. 228 // NX NAX (NintendoAesXts) file format.
227 case FileType::NAX: 229 case FileType::NAX:
@@ -229,7 +231,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
229 231
230 // NX NSP (Nintendo Submission Package) file format 232 // NX NSP (Nintendo Submission Package) file format
231 case FileType::NSP: 233 case FileType::NSP:
232 return std::make_unique<AppLoader_NSP>(std::move(file)); 234 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
235 system.GetContentProvider(), program_index);
233 236
234 // NX KIP (Kernel Internal Process) file format 237 // NX KIP (Kernel Internal Process) file format
235 case FileType::KIP: 238 case FileType::KIP:
@@ -244,20 +247,22 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
244 } 247 }
245} 248}
246 249
247std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) { 250std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
251 std::size_t program_index) {
248 FileType type = IdentifyFile(file); 252 FileType type = IdentifyFile(file);
249 FileType filename_type = GuessFromFilename(file->GetName()); 253 const FileType filename_type = GuessFromFilename(file->GetName());
250 254
251 // Special case: 00 is either a NCA or NAX. 255 // Special case: 00 is either a NCA or NAX.
252 if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) { 256 if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
253 LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); 257 LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
254 if (FileType::Unknown == type) 258 if (FileType::Unknown == type) {
255 type = filename_type; 259 type = filename_type;
260 }
256 } 261 }
257 262
258 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 263 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
259 264
260 return GetFileLoader(std::move(file), type); 265 return GetFileLoader(system, std::move(file), type, program_index);
261} 266}
262 267
263} // namespace Loader 268} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index ac60b097a..36e79e71d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -290,9 +290,14 @@ protected:
290 290
291/** 291/**
292 * Identifies a bootable file and return a suitable loader 292 * Identifies a bootable file and return a suitable loader
293 * @param file The bootable file 293 *
294 * @return the best loader for this file 294 * @param system The system context.
295 * @param file The bootable file.
296 * @param program_index Specifies the index within the container of the program to launch.
297 *
298 * @return the best loader for this file.
295 */ 299 */
296std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file); 300std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
301 std::size_t program_index = 0);
297 302
298} // namespace Loader 303} // namespace Loader
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 497f438a1..aa85c1a29 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -149,7 +149,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
149 // Apply cheats if they exist and the program has a valid title ID 149 // Apply cheats if they exist and the program has a valid title ID
150 if (pm) { 150 if (pm) {
151 system.SetCurrentProcessBuildID(nso_header.build_id); 151 system.SetCurrentProcessBuildID(nso_header.build_id);
152 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 152 const auto cheats = pm->CreateCheatList(nso_header.build_id);
153 if (!cheats.empty()) { 153 if (!cheats.empty()) {
154 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); 154 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
155 } 155 }
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 15e528fa8..928f64c8c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -21,26 +21,34 @@
21 21
22namespace Loader { 22namespace Loader {
23 23
24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) 24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
25 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), 25 const Service::FileSystem::FileSystemController& fsc,
26 const FileSys::ContentProvider& content_provider,
27 std::size_t program_index)
28 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
26 title_id(nsp->GetProgramTitleID()) { 29 title_id(nsp->GetProgramTitleID()) {
27 30
28 if (nsp->GetStatus() != ResultStatus::Success) 31 if (nsp->GetStatus() != ResultStatus::Success) {
29 return; 32 return;
33 }
30 34
31 if (nsp->IsExtractedType()) { 35 if (nsp->IsExtractedType()) {
32 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 36 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
33 } else { 37 } else {
34 const auto control_nca = 38 const auto control_nca =
35 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); 39 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
36 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) 40 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
37 return; 41 return;
42 }
38 43
39 std::tie(nacp_file, icon_file) = 44 std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
40 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); 45 const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
46 return pm.ParseControlNCA(*control_nca);
47 }();
41 48
42 if (title_id == 0) 49 if (title_id == 0) {
43 return; 50 return;
51 }
44 52
45 secondary_loader = std::make_unique<AppLoader_NCA>( 53 secondary_loader = std::make_unique<AppLoader_NCA>(
46 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); 54 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index b27deb686..f0518ac47 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -9,15 +9,16 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
16namespace FileSys { 12namespace FileSys {
13class ContentProvider;
17class NACP; 14class NACP;
18class NSP; 15class NSP;
19} // namespace FileSys 16} // namespace FileSys
20 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
21namespace Loader { 22namespace Loader {
22 23
23class AppLoader_NCA; 24class AppLoader_NCA;
@@ -25,7 +26,10 @@ class AppLoader_NCA;
25/// Loads an XCI file 26/// Loads an XCI file
26class AppLoader_NSP final : public AppLoader { 27class AppLoader_NSP final : public AppLoader {
27public: 28public:
28 explicit AppLoader_NSP(FileSys::VirtualFile file); 29 explicit AppLoader_NSP(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
29 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
30 34
31 /** 35 /**
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 25e83af0f..aaa250cea 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -20,18 +20,25 @@
20 20
21namespace Loader { 21namespace Loader {
22 22
23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) 23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
24 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 24 const Service::FileSystem::FileSystemController& fsc,
25 const FileSys::ContentProvider& content_provider,
26 std::size_t program_index)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
25 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
26 if (xci->GetStatus() != ResultStatus::Success) 29 if (xci->GetStatus() != ResultStatus::Success) {
27 return; 30 return;
31 }
28 32
29 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); 33 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
30 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) 34 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
31 return; 35 return;
36 }
32 37
33 std::tie(nacp_file, icon_file) = 38 std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
34 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca); 39 const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
40 return pm.ParseControlNCA(*control_nca);
41 }();
35} 42}
36 43
37AppLoader_XCI::~AppLoader_XCI() = default; 44AppLoader_XCI::~AppLoader_XCI() = default;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 04aea286f..764dc8328 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -9,15 +9,16 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
16namespace FileSys { 12namespace FileSys {
13class ContentProvider;
17class NACP; 14class NACP;
18class XCI; 15class XCI;
19} // namespace FileSys 16} // namespace FileSys
20 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
21namespace Loader { 22namespace Loader {
22 23
23class AppLoader_NCA; 24class AppLoader_NCA;
@@ -25,7 +26,10 @@ class AppLoader_NCA;
25/// Loads an XCI file 26/// Loads an XCI file
26class AppLoader_XCI final : public AppLoader { 27class AppLoader_XCI final : public AppLoader {
27public: 28public:
28 explicit AppLoader_XCI(FileSys::VirtualFile file); 29 explicit AppLoader_XCI(FileSys::VirtualFile file,
30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider,
32 std::size_t program_index);
29 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
30 34
31 /** 35 /**
diff --git a/src/core/settings.h b/src/core/settings.h
index 476c3fdf3..1143aba5d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -215,6 +215,7 @@ struct Values {
215 bool reporting_services; 215 bool reporting_services;
216 bool quest_flag; 216 bool quest_flag;
217 bool disable_macro_jit; 217 bool disable_macro_jit;
218 bool extended_logging;
218 219
219 // Misceallaneous 220 // Misceallaneous
220 std::string log_filter; 221 std::string log_filter;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index e0908186b..d11b15f38 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -147,7 +147,9 @@ TelemetrySession::~TelemetrySession() {
147 } 147 }
148} 148}
149 149
150void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { 150void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
151 const Service::FileSystem::FileSystemController& fsc,
152 const FileSys::ContentProvider& content_provider) {
151 // Log one-time top-level information 153 // Log one-time top-level information
152 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); 154 AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
153 155
@@ -167,7 +169,10 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
167 app_loader.ReadTitle(name); 169 app_loader.ReadTitle(name);
168 170
169 if (name.empty()) { 171 if (name.empty()) {
170 const auto metadata = FileSys::PatchManager(program_id).GetControlMetadata(); 172 const auto metadata = [&content_provider, &fsc, program_id] {
173 const FileSys::PatchManager pm{program_id, fsc, content_provider};
174 return pm.GetControlMetadata();
175 }();
171 if (metadata.first != nullptr) { 176 if (metadata.first != nullptr) {
172 name = metadata.first->GetApplicationName(); 177 name = metadata.first->GetApplicationName();
173 } 178 }
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 66789d4bd..6f3d45bea 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -7,10 +7,18 @@
7#include <string> 7#include <string>
8#include "common/telemetry.h" 8#include "common/telemetry.h"
9 9
10namespace FileSys {
11class ContentProvider;
12}
13
10namespace Loader { 14namespace Loader {
11class AppLoader; 15class AppLoader;
12} 16}
13 17
18namespace Service::FileSystem {
19class FileSystemController;
20}
21
14namespace Core { 22namespace Core {
15 23
16/** 24/**
@@ -40,10 +48,14 @@ public:
40 * - Title file format 48 * - Title file format
41 * - Miscellaneous settings values. 49 * - Miscellaneous settings values.
42 * 50 *
43 * @param app_loader The application loader to use to retrieve 51 * @param app_loader The application loader to use to retrieve
44 * title-specific information. 52 * title-specific information.
53 * @param fsc Filesystem controller to use to retrieve info.
54 * @param content_provider Content provider to use to retrieve info.
45 */ 55 */
46 void AddInitialInfo(Loader::AppLoader& app_loader); 56 void AddInitialInfo(Loader::AppLoader& app_loader,
57 const Service::FileSystem::FileSystemController& fsc,
58 const FileSys::ContentProvider& content_provider);
47 59
48 /** 60 /**
49 * Wrapper around the Telemetry::FieldCollection::AddField method. 61 * Wrapper around the Telemetry::FieldCollection::AddField method.
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 7b39a38c1..1d1b2e08a 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -31,6 +31,9 @@ add_library(input_common STATIC
31 31
32if (MSVC) 32if (MSVC)
33 target_compile_options(input_common PRIVATE 33 target_compile_options(input_common PRIVATE
34 /W4
35 /WX
36
34 # 'expression' : signed/unsigned mismatch 37 # 'expression' : signed/unsigned mismatch
35 /we4018 38 /we4018
36 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point) 39 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
@@ -46,6 +49,7 @@ if (MSVC)
46 ) 49 )
47else() 50else()
48 target_compile_options(input_common PRIVATE 51 target_compile_options(input_common PRIVATE
52 -Werror
49 -Werror=conversion 53 -Werror=conversion
50 -Werror=ignored-qualifiers 54 -Werror=ignored-qualifiers
51 -Werror=implicit-fallthrough 55 -Werror=implicit-fallthrough
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 74744d7f3..d748c1c04 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -2,6 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <cmath>
7#include <thread>
8#include "common/math_util.h"
5#include "input_common/analog_from_button.h" 9#include "input_common/analog_from_button.h"
6 10
7namespace InputCommon { 11namespace InputCommon {
@@ -11,31 +15,104 @@ public:
11 using Button = std::unique_ptr<Input::ButtonDevice>; 15 using Button = std::unique_ptr<Input::ButtonDevice>;
12 16
13 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, 17 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
14 float modifier_scale_) 18 float modifier_scale_, float modifier_angle_)
15 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), 19 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
16 right(std::move(right_)), modifier(std::move(modifier_)), 20 right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
17 modifier_scale(modifier_scale_) {} 21 modifier_angle(modifier_angle_) {
18 22 update_thread = std::thread(&Analog::UpdateStatus, this);
19 std::tuple<float, float> GetStatus() const override { 23 }
20 constexpr float SQRT_HALF = 0.707106781f;
21 int x = 0, y = 0;
22 24
23 if (right->GetStatus()) { 25 ~Analog() override {
24 ++x; 26 update_thread_running = false;
27 if (update_thread.joinable()) {
28 update_thread.join();
25 } 29 }
26 if (left->GetStatus()) { 30 }
27 --x; 31
32 void MoveToDirection(bool enable, float to_angle) {
33 if (!enable) {
34 return;
28 } 35 }
29 if (up->GetStatus()) { 36 constexpr float TAU = Common::PI * 2.0f;
30 ++y; 37 // Use wider angle to ease the transition.
38 constexpr float aperture = TAU * 0.15f;
39 const float top_limit = to_angle + aperture;
40 const float bottom_limit = to_angle - aperture;
41
42 if ((angle > to_angle && angle <= top_limit) ||
43 (angle + TAU > to_angle && angle + TAU <= top_limit)) {
44 angle -= modifier_angle;
45 if (angle < 0) {
46 angle += TAU;
47 }
48 } else if ((angle >= bottom_limit && angle < to_angle) ||
49 (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
50 angle += modifier_angle;
51 if (angle >= TAU) {
52 angle -= TAU;
53 }
54 } else {
55 angle = to_angle;
31 } 56 }
32 if (down->GetStatus()) { 57 }
33 --y; 58
59 void UpdateStatus() {
60 while (update_thread_running) {
61 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
62
63 bool r = right->GetStatus();
64 bool l = left->GetStatus();
65 bool u = up->GetStatus();
66 bool d = down->GetStatus();
67
68 // Eliminate contradictory movements
69 if (r && l) {
70 r = false;
71 l = false;
72 }
73 if (u && d) {
74 u = false;
75 d = false;
76 }
77
78 // Move to the right
79 MoveToDirection(r && !u && !d, 0.0f);
80
81 // Move to the upper right
82 MoveToDirection(r && u && !d, Common::PI * 0.25f);
83
84 // Move up
85 MoveToDirection(u && !l && !r, Common::PI * 0.5f);
86
87 // Move to the upper left
88 MoveToDirection(l && u && !d, Common::PI * 0.75f);
89
90 // Move to the left
91 MoveToDirection(l && !u && !d, Common::PI);
92
93 // Move to the bottom left
94 MoveToDirection(l && !u && d, Common::PI * 1.25f);
95
96 // Move down
97 MoveToDirection(d && !l && !r, Common::PI * 1.5f);
98
99 // Move to the bottom right
100 MoveToDirection(r && !u && d, Common::PI * 1.75f);
101
102 // Move if a key is pressed
103 if (r || l || u || d) {
104 amplitude = coef;
105 } else {
106 amplitude = 0;
107 }
108
109 // Delay the update rate to 100hz
110 std::this_thread::sleep_for(std::chrono::milliseconds(10));
34 } 111 }
112 }
35 113
36 const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; 114 std::tuple<float, float> GetStatus() const override {
37 return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF), 115 return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
38 static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
39 } 116 }
40 117
41 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 118 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -59,6 +136,11 @@ private:
59 Button right; 136 Button right;
60 Button modifier; 137 Button modifier;
61 float modifier_scale; 138 float modifier_scale;
139 float modifier_angle;
140 float angle{};
141 float amplitude{};
142 std::thread update_thread;
143 bool update_thread_running{true};
62}; 144};
63 145
64std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { 146std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
@@ -69,8 +151,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
69 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); 151 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
70 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); 152 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
71 auto modifier_scale = params.Get("modifier_scale", 0.5f); 153 auto modifier_scale = params.Get("modifier_scale", 0.5f);
154 auto modifier_angle = params.Get("modifier_angle", 0.035f);
72 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), 155 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
73 std::move(right), std::move(modifier), modifier_scale); 156 std::move(right), std::move(modifier), modifier_scale,
157 modifier_angle);
74} 158}
75 159
76} // namespace InputCommon 160} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index d95574bb5..4d1052414 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -96,7 +96,6 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
96 adapter.get()); 96 adapter.get());
97 } 97 }
98 98
99 UNREACHABLE();
100 return nullptr; 99 return nullptr;
101} 100}
102 101
@@ -300,7 +299,8 @@ public:
300 return gcadapter->RumblePlay(port, 0); 299 return gcadapter->RumblePlay(port, 0);
301 } 300 }
302 301
303 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { 302 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
303 [[maybe_unused]] f32 freq_high) const override {
304 const auto mean_amplitude = (amp_low + amp_high) * 0.5f; 304 const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
305 const auto processed_amplitude = 305 const auto processed_amplitude =
306 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8); 306 static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index f3554be9a..42bbf14d4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -23,7 +23,7 @@ public:
23 /// Unregisters SDL device factories and shut them down. 23 /// Unregisters SDL device factories and shut them down.
24 virtual ~State() = default; 24 virtual ~State() = default;
25 25
26 virtual Pollers GetPollers(Polling::DeviceType type) { 26 virtual Pollers GetPollers(Polling::DeviceType) {
27 return {}; 27 return {};
28 } 28 }
29 29
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index c395d96cf..7827e324c 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -400,7 +400,8 @@ public:
400 return joystick->RumblePlay(0, 0); 400 return joystick->RumblePlay(0, 0);
401 } 401 }
402 402
403 bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override { 403 bool SetRumblePlay(f32 amp_low, [[maybe_unused]] f32 freq_low, f32 amp_high,
404 [[maybe_unused]] f32 freq_high) const override {
404 const auto process_amplitude = [](f32 amplitude) { 405 const auto process_amplitude = [](f32 amplitude) {
405 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF); 406 return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
406 }; 407 };
@@ -864,6 +865,8 @@ Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Eve
864Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid, 865Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
865 const SDL_GameControllerButtonBind& binding) { 866 const SDL_GameControllerButtonBind& binding) {
866 switch (binding.bindType) { 867 switch (binding.bindType) {
868 case SDL_CONTROLLER_BINDTYPE_NONE:
869 break;
867 case SDL_CONTROLLER_BINDTYPE_AXIS: 870 case SDL_CONTROLLER_BINDTYPE_AXIS:
868 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis); 871 return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
869 case SDL_CONTROLLER_BINDTYPE_BUTTON: 872 case SDL_CONTROLLER_BINDTYPE_BUTTON:
@@ -984,7 +987,7 @@ class SDLPoller : public InputCommon::Polling::DevicePoller {
984public: 987public:
985 explicit SDLPoller(SDLState& state_) : state(state_) {} 988 explicit SDLPoller(SDLState& state_) : state(state_) {}
986 989
987 void Start(const std::string& device_id) override { 990 void Start([[maybe_unused]] const std::string& device_id) override {
988 state.event_queue.Clear(); 991 state.event_queue.Clear();
989 state.polling = true; 992 state.polling = true;
990 } 993 }
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
index c37716aae..a07124a86 100644
--- a/src/input_common/touch_from_button.cpp
+++ b/src/input_common/touch_from_button.cpp
@@ -44,8 +44,7 @@ private:
44 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map; 44 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
45}; 45};
46 46
47std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create( 47std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(const Common::ParamPackage&) {
48 const Common::ParamPackage& params) {
49 return std::make_unique<TouchFromButtonDevice>(); 48 return std::make_unique<TouchFromButtonDevice>();
50} 49}
51 50
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 3677e79ca..c0bb90048 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -63,7 +63,7 @@ public:
63 } 63 }
64 64
65private: 65private:
66 void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { 66 void HandleReceive(const boost::system::error_code&, std::size_t bytes_transferred) {
67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { 67 if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) {
68 switch (*type) { 68 switch (*type) {
69 case Type::Version: { 69 case Type::Version: {
@@ -90,7 +90,7 @@ private:
90 StartReceive(); 90 StartReceive();
91 } 91 }
92 92
93 void HandleSend(const boost::system::error_code& error) { 93 void HandleSend(const boost::system::error_code&) {
94 boost::system::error_code _ignored{}; 94 boost::system::error_code _ignored{};
95 // Send a request for getting port info for the pad 95 // Send a request for getting port info for the pad
96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}}; 96 const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}};
@@ -189,11 +189,11 @@ void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_ind
189 StartCommunication(client, host, port, pad_index, client_id); 189 StartCommunication(client, host, port, pad_index, client_id);
190} 190}
191 191
192void Client::OnVersion(Response::Version data) { 192void Client::OnVersion([[maybe_unused]] Response::Version data) {
193 LOG_TRACE(Input, "Version packet received: {}", data.version); 193 LOG_TRACE(Input, "Version packet received: {}", data.version);
194} 194}
195 195
196void Client::OnPortInfo(Response::PortInfo data) { 196void Client::OnPortInfo([[maybe_unused]] Response::PortInfo data) {
197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model); 197 LOG_TRACE(Input, "PortInfo packet received: {}", data.model);
198} 198}
199 199
@@ -369,7 +369,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
369 u16 max_y{}; 369 u16 max_y{};
370 370
371 Status current_status{Status::Initialized}; 371 Status current_status{Status::Initialized};
372 SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, 372 SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
373 [&](Response::PadData data) { 373 [&](Response::PadData data) {
374 if (current_status == Status::Initialized) { 374 if (current_status == Status::Initialized) {
375 // Receiving data means the communication is ready now 375 // Receiving data means the communication is ready now
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h
index 3ba4d1fc8..fc1aea4b9 100644
--- a/src/input_common/udp/protocol.h
+++ b/src/input_common/udp/protocol.h
@@ -7,7 +7,16 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9#include <type_traits> 9#include <type_traits>
10
11#ifdef _MSC_VER
12#pragma warning(push)
13#pragma warning(disable : 4701)
14#endif
10#include <boost/crc.hpp> 15#include <boost/crc.hpp>
16#ifdef _MSC_VER
17#pragma warning(pop)
18#endif
19
11#include "common/bit_field.h" 20#include "common/bit_field.h"
12#include "common/swap.h" 21#include "common/swap.h"
13 22
@@ -93,7 +102,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
93 102
94/** 103/**
95 * Creates a message with the proper header data that can be sent to the server. 104 * Creates a message with the proper header data that can be sent to the server.
96 * @param T data Request body to send 105 * @param data Request body to send
97 * @param client_id ID of the udp client (usually not checked on the server) 106 * @param client_id ID of the udp client (usually not checked on the server)
98 */ 107 */
99template <typename T> 108template <typename T>
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cdcde7c59..cfddbde5d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1156,7 +1156,7 @@ void RasterizerOpenGL::SyncViewport() {
1156 flags[Dirty::ClipControl] = false; 1156 flags[Dirty::ClipControl] = false;
1157 1157
1158 bool flip_y = false; 1158 bool flip_y = false;
1159 if (regs.viewport_transform[0].scale_y < 0.0) { 1159 if (regs.viewport_transform[0].scale_y < 0.0f) {
1160 flip_y = !flip_y; 1160 flip_y = !flip_y;
1161 } 1161 }
1162 if (regs.screen_y_control.y_negate != 0) { 1162 if (regs.screen_y_control.y_negate != 0) {
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index 39cc3b869..6920afdf2 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -43,8 +43,8 @@ void AsyncShaders::AllocateWorkers() {
43 // Create workers 43 // Create workers
44 for (std::size_t i = 0; i < num_workers; i++) { 44 for (std::size_t i = 0; i < num_workers; i++) {
45 context_list.push_back(emu_window.CreateSharedContext()); 45 context_list.push_back(emu_window.CreateSharedContext());
46 worker_threads.push_back( 46 worker_threads.emplace_back(&AsyncShaders::ShaderCompilerThread, this,
47 std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())); 47 context_list[i].get());
48 } 48 }
49} 49}
50 50
@@ -106,8 +106,7 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
106 std::vector<Result> results; 106 std::vector<Result> results;
107 { 107 {
108 std::unique_lock lock{completed_mutex}; 108 std::unique_lock lock{completed_mutex};
109 results.assign(std::make_move_iterator(finished_work.begin()), 109 results = std::move(finished_work);
110 std::make_move_iterator(finished_work.end()));
111 finished_work.clear(); 110 finished_work.clear();
112 } 111 }
113 return results; 112 return results;
@@ -116,11 +115,10 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
116void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device, 115void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
117 Tegra::Engines::ShaderType shader_type, u64 uid, 116 Tegra::Engines::ShaderType shader_type, u64 uid,
118 std::vector<u64> code, std::vector<u64> code_b, 117 std::vector<u64> code, std::vector<u64> code_b,
119 u32 main_offset, 118 u32 main_offset, CompilerSettings compiler_settings,
120 VideoCommon::Shader::CompilerSettings compiler_settings, 119 const Registry& registry, VAddr cpu_addr) {
121 const VideoCommon::Shader::Registry& registry, 120 std::unique_lock lock(queue_mutex);
122 VAddr cpu_addr) { 121 pending_queue.push({
123 WorkerParams params{
124 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL, 122 .backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
125 .device = &device, 123 .device = &device,
126 .shader_type = shader_type, 124 .shader_type = shader_type,
@@ -131,9 +129,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
131 .compiler_settings = compiler_settings, 129 .compiler_settings = compiler_settings,
132 .registry = registry, 130 .registry = registry,
133 .cpu_address = cpu_addr, 131 .cpu_address = cpu_addr,
134 }; 132 });
135 std::unique_lock lock(queue_mutex);
136 pending_queue.push(std::move(params));
137 cv.notify_one(); 133 cv.notify_one();
138} 134}
139 135
@@ -145,7 +141,8 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
145 std::vector<VkDescriptorSetLayoutBinding> bindings, 141 std::vector<VkDescriptorSetLayoutBinding> bindings,
146 Vulkan::SPIRVProgram program, 142 Vulkan::SPIRVProgram program,
147 Vulkan::GraphicsPipelineCacheKey key) { 143 Vulkan::GraphicsPipelineCacheKey key) {
148 WorkerParams params{ 144 std::unique_lock lock(queue_mutex);
145 pending_queue.push({
149 .backend = Backend::Vulkan, 146 .backend = Backend::Vulkan,
150 .pp_cache = pp_cache, 147 .pp_cache = pp_cache,
151 .vk_device = &device, 148 .vk_device = &device,
@@ -153,13 +150,10 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
153 .descriptor_pool = &descriptor_pool, 150 .descriptor_pool = &descriptor_pool,
154 .update_descriptor_queue = &update_descriptor_queue, 151 .update_descriptor_queue = &update_descriptor_queue,
155 .renderpass_cache = &renderpass_cache, 152 .renderpass_cache = &renderpass_cache,
156 .bindings = bindings, 153 .bindings = std::move(bindings),
157 .program = program, 154 .program = std::move(program),
158 .key = key, 155 .key = key,
159 }; 156 });
160
161 std::unique_lock lock(queue_mutex);
162 pending_queue.push(std::move(params));
163 cv.notify_one(); 157 cv.notify_one();
164} 158}
165 159
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index 618d309d2..1ed4212ee 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -212,10 +212,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
212 return 0; 212 return 0;
213 case TextureFormat::R8G24: 213 case TextureFormat::R8G24:
214 if (component == 0) { 214 if (component == 0) {
215 return 8; 215 return 24;
216 } 216 }
217 if (component == 1) { 217 if (component == 1) {
218 return 24; 218 return 8;
219 } 219 }
220 return 0; 220 return 0;
221 case TextureFormat::R8G8: 221 case TextureFormat::R8G8:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d62b0efc2..f0338cf7a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,6 +302,12 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
302 this->setMouseTracking(true); 302 this->setMouseTracking(true);
303 303
304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
305 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
306 Qt::QueuedConnection);
307}
308
309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
310 emit ExecuteProgramSignal(program_index);
305} 311}
306 312
307GRenderWindow::~GRenderWindow() { 313GRenderWindow::~GRenderWindow() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ca35cf831..503b4f89e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -166,6 +166,12 @@ public:
166 166
167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; 167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
168 168
169 /**
170 * Instructs the window to re-launch the application using the specified program_index.
171 * @param program_index Specifies the index within the application of the program to launch.
172 */
173 void ExecuteProgram(std::size_t program_index);
174
169public slots: 175public slots:
170 void OnEmulationStarting(EmuThread* emu_thread); 176 void OnEmulationStarting(EmuThread* emu_thread);
171 void OnEmulationStopping(); 177 void OnEmulationStopping();
@@ -175,6 +181,7 @@ signals:
175 /// Emitted when the window is closed 181 /// Emitted when the window is closed
176 void Closed(); 182 void Closed();
177 void FirstFrameDisplayed(); 183 void FirstFrameDisplayed();
184 void ExecuteProgramSignal(std::size_t program_index);
178 185
179private: 186private:
180 void TouchBeginEvent(const QTouchEvent* event); 187 void TouchBeginEvent(const QTouchEvent* event);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6fa842cd5..3c423a271 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -649,6 +649,8 @@ void Config::ReadDebuggingValues() {
649 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); 649 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
650 Settings::values.disable_macro_jit = 650 Settings::values.disable_macro_jit =
651 ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool(); 651 ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
652 Settings::values.extended_logging =
653 ReadSetting(QStringLiteral("extended_logging"), false).toBool();
652 654
653 qt_config->endGroup(); 655 qt_config->endGroup();
654} 656}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 2bfe2c306..027099ab7 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -41,6 +41,7 @@ void ConfigureDebug::SetConfiguration() {
41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); 41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
42 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 42 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
43 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit); 43 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
44 ui->extended_logging->setChecked(Settings::values.extended_logging);
44} 45}
45 46
46void ConfigureDebug::ApplyConfiguration() { 47void ConfigureDebug::ApplyConfiguration() {
@@ -53,6 +54,7 @@ void ConfigureDebug::ApplyConfiguration() {
53 Settings::values.quest_flag = ui->quest_flag->isChecked(); 54 Settings::values.quest_flag = ui->quest_flag->isChecked();
54 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 55 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
55 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 56 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
57 Settings::values.extended_logging = ui->extended_logging->isChecked();
56 Debugger::ToggleConsole(); 58 Debugger::ToggleConsole();
57 Log::Filter filter; 59 Log::Filter filter;
58 filter.ParseFilterString(Settings::values.log_filter); 60 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 9d6feb9f7..6f94fe304 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -90,7 +90,7 @@
90 <item> 90 <item>
91 <widget class="QCheckBox" name="toggle_console"> 91 <widget class="QCheckBox" name="toggle_console">
92 <property name="text"> 92 <property name="text">
93 <string>Show Log Console (Windows Only)</string> 93 <string>Show Log in Console</string>
94 </property> 94 </property>
95 </widget> 95 </widget>
96 </item> 96 </item>
@@ -103,6 +103,34 @@
103 </item> 103 </item>
104 </layout> 104 </layout>
105 </item> 105 </item>
106 <item>
107 <widget class="QCheckBox" name="extended_logging">
108 <property name="enabled">
109 <bool>true</bool>
110 </property>
111 <property name="toolTip">
112 <string>When checked, the max size of the log increases from 100 MB to 1 GB</string>
113 </property>
114 <property name="text">
115 <string>Enable Extended Logging</string>
116 </property>
117 </widget>
118 </item>
119 <item>
120 <widget class="QLabel" name="label_3">
121 <property name="font">
122 <font>
123 <italic>true</italic>
124 </font>
125 </property>
126 <property name="text">
127 <string>This will be reset automatically when yuzu closes.</string>
128 </property>
129 <property name="indent">
130 <number>20</number>
131 </property>
132 </widget>
133 </item>
106 </layout> 134 </layout>
107 </widget> 135 </widget>
108 </item> 136 </item>
@@ -115,7 +143,7 @@
115 <item> 143 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout_4"> 144 <layout class="QHBoxLayout" name="horizontalLayout_4">
117 <item> 145 <item>
118 <widget class="QLabel" name="label_3"> 146 <widget class="QLabel" name="label_4">
119 <property name="text"> 147 <property name="text">
120 <string>Arguments String</string> 148 <string>Arguments String</string>
121 </property> 149 </property>
@@ -140,8 +168,8 @@
140 <property name="enabled"> 168 <property name="enabled">
141 <bool>true</bool> 169 <bool>true</bool>
142 </property> 170 </property>
143 <property name="whatsThis"> 171 <property name="toolTip">
144 <string>When checked, the graphics API enters in a slower debugging mode</string> 172 <string>When checked, the graphics API enters a slower debugging mode</string>
145 </property> 173 </property>
146 <property name="text"> 174 <property name="text">
147 <string>Enable Graphics Debugging</string> 175 <string>Enable Graphics Debugging</string>
@@ -153,8 +181,8 @@
153 <property name="enabled"> 181 <property name="enabled">
154 <bool>true</bool> 182 <bool>true</bool>
155 </property> 183 </property>
156 <property name="whatsThis"> 184 <property name="toolTip">
157 <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string> 185 <string>When checked, it disables the macro Just In Time compiler. Enabling this makes games run slower</string>
158 </property> 186 </property>
159 <property name="text"> 187 <property name="text">
160 <string>Disable Macro JIT</string> 188 <string>Disable Macro JIT</string>
@@ -169,7 +197,7 @@
169 <property name="title"> 197 <property name="title">
170 <string>Dump</string> 198 <string>Dump</string>
171 </property> 199 </property>
172 <layout class="QVBoxLayout" name="verticalLayout_6"> 200 <layout class="QVBoxLayout" name="verticalLayout_7">
173 <item> 201 <item>
174 <widget class="QCheckBox" name="reporting_services"> 202 <widget class="QCheckBox" name="reporting_services">
175 <property name="text"> 203 <property name="text">
@@ -178,7 +206,7 @@
178 </widget> 206 </widget>
179 </item> 207 </item>
180 <item> 208 <item>
181 <widget class="QLabel" name="label"> 209 <widget class="QLabel" name="label_5">
182 <property name="font"> 210 <property name="font">
183 <font> 211 <font>
184 <italic>true</italic> 212 <italic>true</italic>
@@ -200,7 +228,7 @@
200 <property name="title"> 228 <property name="title">
201 <string>Advanced</string> 229 <string>Advanced</string>
202 </property> 230 </property>
203 <layout class="QVBoxLayout" name="verticalLayout_7"> 231 <layout class="QVBoxLayout" name="verticalLayout_8">
204 <item> 232 <item>
205 <widget class="QCheckBox" name="quest_flag"> 233 <widget class="QCheckBox" name="quest_flag">
206 <property name="text"> 234 <property name="text">
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 81464dd37..8eac3bd9d 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -16,6 +16,7 @@
16 16
17#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/file_util.h" 18#include "common/file_util.h"
19#include "core/core.h"
19#include "core/file_sys/control_metadata.h" 20#include "core/file_sys/control_metadata.h"
20#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/xts_archive.h" 22#include "core/file_sys/xts_archive.h"
@@ -89,9 +90,11 @@ void ConfigurePerGame::LoadConfiguration() {
89 ui->display_title_id->setText( 90 ui->display_title_id->setText(
90 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); 91 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
91 92
92 FileSys::PatchManager pm{title_id}; 93 auto& system = Core::System::GetInstance();
94 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
95 system.GetContentProvider()};
93 const auto control = pm.GetControlMetadata(); 96 const auto control = pm.GetControlMetadata();
94 const auto loader = Loader::GetLoader(file); 97 const auto loader = Loader::GetLoader(system, file);
95 98
96 if (control.first != nullptr) { 99 if (control.first != nullptr) {
97 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); 100 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 793fd8975..cdeeec01c 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -112,8 +112,10 @@ void ConfigurePerGameAddons::LoadConfiguration() {
112 return; 112 return;
113 } 113 }
114 114
115 FileSys::PatchManager pm{title_id}; 115 auto& system = Core::System::GetInstance();
116 const auto loader = Loader::GetLoader(file); 116 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
117 system.GetContentProvider()};
118 const auto loader = Loader::GetLoader(system, file);
117 119
118 FileSys::VirtualFile update_raw; 120 FileSys::VirtualFile update_raw;
119 loader->ReadUpdateRaw(update_raw); 121 loader->ReadUpdateRaw(update_raw);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index e0ce45fd9..23643aea2 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -235,12 +235,11 @@ GameListWorker::~GameListWorker() = default;
235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { 235void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
236 using namespace FileSys; 236 using namespace FileSys;
237 237
238 const auto& cache = 238 auto& system = Core::System::GetInstance();
239 dynamic_cast<ContentProviderUnion&>(Core::System::GetInstance().GetContentProvider()); 239 const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider());
240 240
241 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> installed_games; 241 auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application,
242 installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, 242 ContentRecordType::Program);
243 ContentRecordType::Program);
244 243
245 if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) { 244 if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) {
246 installed_games = cache.ListEntriesFilterOrigin( 245 installed_games = cache.ListEntriesFilterOrigin(
@@ -254,23 +253,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
254 } 253 }
255 254
256 for (const auto& [slot, game] : installed_games) { 255 for (const auto& [slot, game] : installed_games) {
257 if (slot == ContentProviderUnionSlot::FrontendManual) 256 if (slot == ContentProviderUnionSlot::FrontendManual) {
258 continue; 257 continue;
258 }
259 259
260 const auto file = cache.GetEntryUnparsed(game.title_id, game.type); 260 const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
261 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 261 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(system, file);
262 if (!loader) 262 if (!loader) {
263 continue; 263 continue;
264 }
264 265
265 std::vector<u8> icon; 266 std::vector<u8> icon;
266 std::string name; 267 std::string name;
267 u64 program_id = 0; 268 u64 program_id = 0;
268 loader->ReadProgramId(program_id); 269 loader->ReadProgramId(program_id);
269 270
270 const PatchManager patch{program_id}; 271 const PatchManager patch{program_id, system.GetFileSystemController(),
272 system.GetContentProvider()};
271 const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control); 273 const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control);
272 if (control != nullptr) 274 if (control != nullptr) {
273 GetMetadataFromControlNCA(patch, *control, icon, name); 275 GetMetadataFromControlNCA(patch, *control, icon, name);
276 }
274 277
275 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, 278 emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
276 compatibility_list, patch), 279 compatibility_list, patch),
@@ -280,9 +283,11 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
280 283
281void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, 284void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
282 unsigned int recursion, GameListDir* parent_dir) { 285 unsigned int recursion, GameListDir* parent_dir) {
283 const auto callback = [this, target, recursion, 286 auto& system = Core::System::GetInstance();
284 parent_dir](u64* num_entries_out, const std::string& directory, 287
285 const std::string& virtual_name) -> bool { 288 const auto callback = [this, target, recursion, parent_dir,
289 &system](u64* num_entries_out, const std::string& directory,
290 const std::string& virtual_name) -> bool {
286 if (stop_processing) { 291 if (stop_processing) {
287 // Breaks the callback loop. 292 // Breaks the callback loop.
288 return false; 293 return false;
@@ -293,7 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
293 if (!is_dir && 298 if (!is_dir &&
294 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 299 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
295 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); 300 const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
296 auto loader = Loader::GetLoader(file); 301 auto loader = Loader::GetLoader(system, file);
297 if (!loader) { 302 if (!loader) {
298 return true; 303 return true;
299 } 304 }
@@ -331,7 +336,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
331 std::string name = " "; 336 std::string name = " ";
332 [[maybe_unused]] const auto res3 = loader->ReadTitle(name); 337 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
333 338
334 const FileSys::PatchManager patch{program_id}; 339 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
340 system.GetContentProvider()};
335 341
336 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 342 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
337 compatibility_list, patch), 343 compatibility_list, patch),
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9dabd8889..805619ccf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -978,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
978#endif 978#endif
979} 979}
980 980
981bool GMainWindow::LoadROM(const QString& filename) { 981bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
982 // Shutdown previous session if the emu thread is still active... 982 // Shutdown previous session if the emu thread is still active...
983 if (emu_thread != nullptr) 983 if (emu_thread != nullptr)
984 ShutdownGame(); 984 ShutdownGame();
@@ -1003,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
1003 1003
1004 system.RegisterHostThread(); 1004 system.RegisterHostThread();
1005 1005
1006 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 1006 const Core::System::ResultStatus result{
1007 system.Load(*render_window, filename.toStdString(), program_index)};
1007 1008
1008 const auto drd_callout = 1009 const auto drd_callout =
1009 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1010 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1085,14 +1086,18 @@ void GMainWindow::SelectAndSetCurrentUser() {
1085 Settings::values.current_user = dialog.GetIndex(); 1086 Settings::values.current_user = dialog.GetIndex();
1086} 1087}
1087 1088
1088void GMainWindow::BootGame(const QString& filename) { 1089void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1089 LOG_INFO(Frontend, "yuzu starting..."); 1090 LOG_INFO(Frontend, "yuzu starting...");
1090 StoreRecentFile(filename); // Put the filename on top of the list 1091 StoreRecentFile(filename); // Put the filename on top of the list
1091 1092
1092 u64 title_id{0}; 1093 u64 title_id{0};
1093 1094
1095 last_filename_booted = filename;
1096
1097 auto& system = Core::System::GetInstance();
1094 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1098 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1095 const auto loader = Loader::GetLoader(v_file); 1099 const auto loader = Loader::GetLoader(system, v_file, program_index);
1100
1096 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1101 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1097 // Load per game settings 1102 // Load per game settings
1098 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); 1103 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
@@ -1106,7 +1111,7 @@ void GMainWindow::BootGame(const QString& filename) {
1106 SelectAndSetCurrentUser(); 1111 SelectAndSetCurrentUser();
1107 } 1112 }
1108 1113
1109 if (!LoadROM(filename)) 1114 if (!LoadROM(filename, program_index))
1110 return; 1115 return;
1111 1116
1112 // Create and start the emulation thread 1117 // Create and start the emulation thread
@@ -1114,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
1114 emit EmulationStarting(emu_thread.get()); 1119 emit EmulationStarting(emu_thread.get());
1115 emu_thread->start(); 1120 emu_thread->start();
1116 1121
1122 // Register an ExecuteProgram callback such that Core can execute a sub-program
1123 system.RegisterExecuteProgramCallback(
1124 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
1125
1117 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1126 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1118 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 1127 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
1119 // before the CPU continues 1128 // before the CPU continues
@@ -1144,9 +1153,13 @@ void GMainWindow::BootGame(const QString& filename) {
1144 1153
1145 std::string title_name; 1154 std::string title_name;
1146 std::string title_version; 1155 std::string title_version;
1147 const auto res = Core::System::GetInstance().GetGameName(title_name); 1156 const auto res = system.GetGameName(title_name);
1148 1157
1149 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata(); 1158 const auto metadata = [&system, title_id] {
1159 const FileSys::PatchManager pm(title_id, system.GetFileSystemController(),
1160 system.GetContentProvider());
1161 return pm.GetControlMetadata();
1162 }();
1150 if (metadata.first != nullptr) { 1163 if (metadata.first != nullptr) {
1151 title_version = metadata.first->GetVersionString(); 1164 title_version = metadata.first->GetVersionString();
1152 title_name = metadata.first->GetApplicationName(); 1165 title_name = metadata.first->GetApplicationName();
@@ -1157,7 +1170,7 @@ void GMainWindow::BootGame(const QString& filename) {
1157 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); 1170 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1158 UpdateWindowTitle(title_name, title_version); 1171 UpdateWindowTitle(title_name, title_version);
1159 1172
1160 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); 1173 loading_screen->Prepare(system.GetAppLoader());
1161 loading_screen->show(); 1174 loading_screen->show();
1162 1175
1163 emulation_running = true; 1176 emulation_running = true;
@@ -1276,16 +1289,18 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1276 const std::string& game_path) { 1289 const std::string& game_path) {
1277 std::string path; 1290 std::string path;
1278 QString open_target; 1291 QString open_target;
1292 auto& system = Core::System::GetInstance();
1279 1293
1280 const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] { 1294 const auto [user_save_size, device_save_size] = [this, &game_path, &program_id, &system] {
1281 FileSys::PatchManager pm{program_id}; 1295 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
1296 system.GetContentProvider()};
1282 const auto control = pm.GetControlMetadata().first; 1297 const auto control = pm.GetControlMetadata().first;
1283 if (control != nullptr) { 1298 if (control != nullptr) {
1284 return std::make_pair(control->GetDefaultNormalSaveSize(), 1299 return std::make_pair(control->GetDefaultNormalSaveSize(),
1285 control->GetDeviceSaveDataSize()); 1300 control->GetDeviceSaveDataSize());
1286 } else { 1301 } else {
1287 const auto file = Core::GetGameFileFromPath(vfs, game_path); 1302 const auto file = Core::GetGameFileFromPath(vfs, game_path);
1288 const auto loader = Loader::GetLoader(file); 1303 const auto loader = Loader::GetLoader(system, file);
1289 1304
1290 FileSys::NACP nacp{}; 1305 FileSys::NACP nacp{};
1291 loader->ReadControlData(nacp); 1306 loader->ReadControlData(nacp);
@@ -1612,7 +1627,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1612 "cancelled the operation.")); 1627 "cancelled the operation."));
1613 }; 1628 };
1614 1629
1615 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 1630 auto& system = Core::System::GetInstance();
1631 const auto loader = Loader::GetLoader(system, vfs->OpenFile(game_path, FileSys::Mode::Read));
1616 if (loader == nullptr) { 1632 if (loader == nullptr) {
1617 failed(); 1633 failed();
1618 return; 1634 return;
@@ -1624,7 +1640,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1624 return; 1640 return;
1625 } 1641 }
1626 1642
1627 const auto& installed = Core::System::GetInstance().GetContentProvider(); 1643 const auto& installed = system.GetContentProvider();
1628 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); 1644 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
1629 1645
1630 if (!romfs_title_id) { 1646 if (!romfs_title_id) {
@@ -1639,7 +1655,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
1639 1655
1640 if (*romfs_title_id == program_id) { 1656 if (*romfs_title_id == program_id) {
1641 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset(); 1657 const u64 ivfc_offset = loader->ReadRomFSIVFCOffset();
1642 FileSys::PatchManager pm{program_id}; 1658 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed};
1643 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program); 1659 romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program);
1644 } else { 1660 } else {
1645 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS(); 1661 romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
@@ -1756,7 +1772,8 @@ void GMainWindow::OnGameListShowList(bool show) {
1756void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { 1772void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1757 u64 title_id{}; 1773 u64 title_id{};
1758 const auto v_file = Core::GetGameFileFromPath(vfs, file); 1774 const auto v_file = Core::GetGameFileFromPath(vfs, file);
1759 const auto loader = Loader::GetLoader(v_file); 1775 const auto loader = Loader::GetLoader(Core::System::GetInstance(), v_file);
1776
1760 if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { 1777 if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
1761 QMessageBox::information(this, tr("Properties"), 1778 QMessageBox::information(this, tr("Properties"),
1762 tr("The game properties could not be loaded.")); 1779 tr("The game properties could not be loaded."));
@@ -2128,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
2128 loading_screen->OnLoadComplete(); 2145 loading_screen->OnLoadComplete();
2129} 2146}
2130 2147
2148void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2149 ShutdownGame();
2150 BootGame(last_filename_booted, program_index);
2151}
2152
2131void GMainWindow::ErrorDisplayDisplayError(QString body) { 2153void GMainWindow::ErrorDisplayDisplayError(QString body) {
2132 QMessageBox::critical(this, tr("Error Display"), body); 2154 QMessageBox::critical(this, tr("Error Display"), body);
2133 emit ErrorDisplayFinished(); 2155 emit ErrorDisplayFinished();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b380a66f3..6242341d1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -131,6 +131,7 @@ signals:
131 131
132public slots: 132public slots:
133 void OnLoadComplete(); 133 void OnLoadComplete();
134 void OnExecuteProgram(std::size_t program_index);
134 void ControllerSelectorReconfigureControllers( 135 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters); 136 const Core::Frontend::ControllerParameters& parameters);
136 void ErrorDisplayDisplayError(QString body); 137 void ErrorDisplayDisplayError(QString body);
@@ -154,8 +155,8 @@ private:
154 void PreventOSSleep(); 155 void PreventOSSleep();
155 void AllowOSSleep(); 156 void AllowOSSleep();
156 157
157 bool LoadROM(const QString& filename); 158 bool LoadROM(const QString& filename, std::size_t program_index);
158 void BootGame(const QString& filename); 159 void BootGame(const QString& filename, std::size_t program_index = 0);
159 void ShutdownGame(); 160 void ShutdownGame();
160 161
161 void ShowTelemetryCallout(); 162 void ShowTelemetryCallout();
@@ -317,6 +318,9 @@ private:
317 // Install progress dialog 318 // Install progress dialog
318 QProgressDialog* install_progress; 319 QProgressDialog* install_progress;
319 320
321 // Last game booted, used for multi-process apps
322 QString last_filename_booted;
323
320protected: 324protected:
321 void dropEvent(QDropEvent* event) override; 325 void dropEvent(QDropEvent* event) override;
322 void dragEnterEvent(QDragEnterEvent* event) override; 326 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 3a76c785f..14a23c71b 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -240,11 +240,11 @@ int main(int argc, char** argv) {
240 system.CurrentProcess()->GetTitleID(), false, 240 system.CurrentProcess()->GetTitleID(), false,
241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 241 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
242 242
243 system.Run(); 243 void(system.Run());
244 while (emu_window->IsOpen()) { 244 while (emu_window->IsOpen()) {
245 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 245 std::this_thread::sleep_for(std::chrono::milliseconds(1));
246 } 246 }
247 system.Pause(); 247 void(system.Pause());
248 system.Shutdown(); 248 system.Shutdown();
249 249
250 detached_tasks.WaitForAllTasks(); 250 detached_tasks.WaitForAllTasks();
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 5798ce43a..88e4bd1f7 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -256,11 +256,11 @@ int main(int argc, char** argv) {
256 256
257 system.GPU().Start(); 257 system.GPU().Start();
258 258
259 system.Run(); 259 void(system.Run());
260 while (!finished) { 260 while (!finished) {
261 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 261 std::this_thread::sleep_for(std::chrono::milliseconds(1));
262 } 262 }
263 system.Pause(); 263 void(system.Pause());
264 264
265 detached_tasks.WaitForAllTasks(); 265 detached_tasks.WaitForAllTasks();
266 return return_value; 266 return return_value;