summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt8
-rw-r--r--src/audio_core/audio_out.cpp10
-rw-r--r--src/audio_core/audio_out.h5
-rw-r--r--src/audio_core/audio_renderer.cpp234
-rw-r--r--src/audio_core/audio_renderer.h206
-rw-r--r--src/audio_core/buffer.h13
-rw-r--r--src/audio_core/codec.cpp77
-rw-r--r--src/audio_core/codec.h44
-rw-r--r--src/audio_core/cubeb_sink.cpp54
-rw-r--r--src/audio_core/cubeb_sink.h3
-rw-r--r--src/audio_core/null_sink.h6
-rw-r--r--src/audio_core/sink.h4
-rw-r--r--src/audio_core/sink_stream.h4
-rw-r--r--src/audio_core/stream.cpp26
-rw-r--r--src/audio_core/stream.h7
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp2
-rw-r--r--src/core/core.cpp14
-rw-r--r--src/core/core.h33
-rw-r--r--src/core/core_timing.cpp14
-rw-r--r--src/core/core_timing.h11
-rw-r--r--src/core/crypto/aes_util.cpp35
-rw-r--r--src/core/crypto/aes_util.h12
-rw-r--r--src/core/crypto/encryption_layer.h1
-rw-r--r--src/core/crypto/key_manager.cpp15
-rw-r--r--src/core/crypto/key_manager.h7
-rw-r--r--src/core/gdbstub/gdbstub.cpp174
-rw-r--r--src/core/gdbstub/gdbstub.h8
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp30
-rw-r--r--src/core/hle/kernel/client_port.cpp12
-rw-r--r--src/core/hle/kernel/client_port.h14
-rw-r--r--src/core/hle/kernel/event.h12
-rw-r--r--src/core/hle/kernel/server_session.cpp2
-rw-r--r--src/core/hle/service/apm/apm.cpp1
-rw-r--r--src/core/hle/service/apm/interface.cpp25
-rw-r--r--src/core/hle/service/apm/interface.h8
-rw-r--r--src/core/hle/service/arp/arp.cpp75
-rw-r--r--src/core/hle/service/arp/arp.h16
-rw-r--r--src/core/hle/service/audio/audin_a.cpp2
-rw-r--r--src/core/hle/service/audio/audout_a.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp12
-rw-r--r--src/core/hle/service/audio/audout_u.h10
-rw-r--r--src/core/hle/service/audio/audrec_a.cpp2
-rw-r--r--src/core/hle/service/audio/audren_a.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp208
-rw-r--r--src/core/hle/service/audio/audren_u.h19
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.h1
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h8
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/usb/usb.cpp238
-rw-r--r--src/core/hle/service/usb/usb.h15
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
-rw-r--r--src/core/loader/nca.cpp44
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/memory.cpp24
-rw-r--r--src/core/memory.h23
-rw-r--r--src/core/perf_stats.cpp17
-rw-r--r--src/core/perf_stats.h8
-rw-r--r--src/core/settings.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/gpu.h6
-rw-r--r--src/video_core/renderer_base.cpp4
-rw-r--r--src/video_core/renderer_base.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp7
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h2
-rw-r--r--src/video_core/video_core.cpp25
-rw-r--r--src/video_core/video_core.h20
-rw-r--r--src/yuzu/about_dialog.cpp2
-rw-r--r--src/yuzu/about_dialog.h2
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h4
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.h4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp3
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h6
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.h4
-rw-r--r--src/yuzu/game_list.cpp44
-rw-r--r--src/yuzu/game_list_p.h17
-rw-r--r--src/yuzu/hotkeys.cpp67
-rw-r--r--src/yuzu/hotkeys.h107
-rw-r--r--src/yuzu/main.cpp130
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
115 files changed, 1706 insertions, 831 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 81121167d..ec71524a3 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -1,9 +1,11 @@
1add_library(audio_core STATIC 1add_library(audio_core STATIC
2 audio_out.cpp 2 audio_out.cpp
3 audio_out.h 3 audio_out.h
4 audio_renderer.cpp
5 audio_renderer.h
4 buffer.h 6 buffer.h
5 cubeb_sink.cpp 7 codec.cpp
6 cubeb_sink.h 8 codec.h
7 null_sink.h 9 null_sink.h
8 stream.cpp 10 stream.cpp
9 stream.h 11 stream.h
@@ -11,6 +13,8 @@ add_library(audio_core STATIC
11 sink_details.cpp 13 sink_details.cpp
12 sink_details.h 14 sink_details.h
13 sink_stream.h 15 sink_stream.h
16
17 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
14) 18)
15 19
16create_target_directory_groups(audio_core) 20create_target_directory_groups(audio_core)
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 3dfdf61f9..12632a95c 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
27 return {}; 27 return {};
28} 28}
29 29
30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, 30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
31 Stream::ReleaseCallback&& release_callback) { 31 Stream::ReleaseCallback&& release_callback) {
32 if (!sink) { 32 if (!sink) {
33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); 33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
34 sink = sink_details.factory(Settings::values.audio_device_id); 34 sink = sink_details.factory(Settings::values.audio_device_id);
35 } 35 }
36 36
37 return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), 37 return std::make_shared<Stream>(
38 std::move(release_callback), 38 sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
39 sink->AcquireSinkStream(sample_rate, num_channels)); 39 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
40} 40}
41 41
42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { 42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
51 stream->Stop(); 51 stream->Stop();
52} 52}
53 53
54bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) { 54bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
55 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); 55 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
56} 56}
57 57
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index 95e9b53fe..39b7e656b 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8#include <vector> 9#include <vector>
9 10
10#include "audio_core/buffer.h" 11#include "audio_core/buffer.h"
@@ -20,7 +21,7 @@ namespace AudioCore {
20class AudioOut { 21class AudioOut {
21public: 22public:
22 /// Opens a new audio stream 23 /// Opens a new audio stream
23 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, 24 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
24 Stream::ReleaseCallback&& release_callback); 25 Stream::ReleaseCallback&& release_callback);
25 26
26 /// Returns a vector of recently released buffers specified by tag for the specified stream 27 /// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -33,7 +34,7 @@ public:
33 void StopStream(StreamPtr stream); 34 void StopStream(StreamPtr stream);
34 35
35 /// Queues a buffer into the specified audio stream, returns true on success 36 /// Queues a buffer into the specified audio stream, returns true on success
36 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); 37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
37 38
38private: 39private:
39 SinkPtr sink; 40 SinkPtr sink;
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
new file mode 100644
index 000000000..282f345c5
--- /dev/null
+++ b/src/audio_core/audio_renderer.cpp
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_renderer.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/memory.h"
9
10namespace AudioCore {
11
12constexpr u32 STREAM_SAMPLE_RATE{48000};
13constexpr u32 STREAM_NUM_CHANNELS{2};
14
15AudioRenderer::AudioRenderer(AudioRendererParameter params,
16 Kernel::SharedPtr<Kernel::Event> buffer_event)
17 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
18
19 audio_core = std::make_unique<AudioCore::AudioOut>();
20 stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
21 [=]() { buffer_event->Signal(); });
22 audio_core->StartStream(stream);
23
24 QueueMixedBuffer(0);
25 QueueMixedBuffer(1);
26 QueueMixedBuffer(2);
27}
28
29std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
30 // Copy UpdateDataHeader struct
31 UpdateDataHeader config{};
32 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
33 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
34
35 // Copy MemoryPoolInfo structs
36 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
37 std::memcpy(mem_pool_info.data(),
38 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
39 memory_pool_count * sizeof(MemoryPoolInfo));
40
41 // Copy VoiceInfo structs
42 size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
43 config.voice_resource_size};
44 for (auto& voice : voices) {
45 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
46 offset += sizeof(VoiceInfo);
47 }
48
49 // Update voices
50 for (auto& voice : voices) {
51 voice.UpdateState();
52 if (!voice.GetInfo().is_in_use) {
53 continue;
54 }
55 if (voice.GetInfo().is_new) {
56 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
57 }
58 }
59
60 // Update memory pool state
61 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
62 for (size_t index = 0; index < memory_pool.size(); ++index) {
63 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
64 memory_pool[index].state = MemoryPoolStates::Attached;
65 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
66 memory_pool[index].state = MemoryPoolStates::Detached;
67 }
68 }
69
70 // Release previous buffers and queue next ones for playback
71 ReleaseAndQueueBuffers();
72
73 // Copy output header
74 UpdateDataHeader response_data{worker_params};
75 std::vector<u8> output_params(response_data.total_size);
76 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
77
78 // Copy output memory pool entries
79 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
80 response_data.memory_pools_size);
81
82 // Copy output voice status
83 size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
84 for (const auto& voice : voices) {
85 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
86 sizeof(VoiceOutStatus));
87 voice_out_status_offset += sizeof(VoiceOutStatus);
88 }
89
90 return output_params;
91}
92
93void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
94 wave_index = index & 3;
95 is_refresh_pending = true;
96}
97
98std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
99 if (!IsPlaying()) {
100 return {};
101 }
102
103 if (is_refresh_pending) {
104 RefreshBuffer();
105 }
106
107 const size_t max_size{samples.size() - offset};
108 const size_t dequeue_offset{offset};
109 size_t size{sample_count * STREAM_NUM_CHANNELS};
110 if (size > max_size) {
111 size = max_size;
112 }
113
114 out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
115 offset += size;
116
117 const auto& wave_buffer{info.wave_buffer[wave_index]};
118 if (offset == samples.size()) {
119 offset = 0;
120
121 if (!wave_buffer.is_looping) {
122 SetWaveIndex(wave_index + 1);
123 }
124
125 out_status.wave_buffer_consumed++;
126
127 if (wave_buffer.end_of_stream) {
128 info.play_state = PlayState::Paused;
129 }
130 }
131
132 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
133}
134
135void AudioRenderer::VoiceState::UpdateState() {
136 if (is_in_use && !info.is_in_use) {
137 // No longer in use, reset state
138 is_refresh_pending = true;
139 wave_index = 0;
140 offset = 0;
141 out_status = {};
142 }
143 is_in_use = info.is_in_use;
144}
145
146void AudioRenderer::VoiceState::RefreshBuffer() {
147 std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
148 Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
149 info.wave_buffer[wave_index].buffer_sz);
150
151 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
152 case Codec::PcmFormat::Int16: {
153 // PCM16 is played as-is
154 break;
155 }
156 case Codec::PcmFormat::Adpcm: {
157 // Decode ADPCM to PCM16
158 Codec::ADPCM_Coeff coeffs;
159 Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
160 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
161 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
162 break;
163 }
164 default:
165 LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
166 UNREACHABLE();
167 break;
168 }
169
170 switch (info.channel_count) {
171 case 1:
172 // 1 channel is upsampled to 2 channel
173 samples.resize(new_samples.size() * 2);
174 for (size_t index = 0; index < new_samples.size(); ++index) {
175 samples[index * 2] = new_samples[index];
176 samples[index * 2 + 1] = new_samples[index];
177 }
178 break;
179 case 2: {
180 // 2 channel is played as is
181 samples = std::move(new_samples);
182 break;
183 }
184 default:
185 LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
186 UNREACHABLE();
187 break;
188 }
189
190 is_refresh_pending = false;
191}
192
193static constexpr s16 ClampToS16(s32 value) {
194 return static_cast<s16>(std::clamp(value, -32768, 32767));
195}
196
197void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
198 constexpr size_t BUFFER_SIZE{512};
199 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
200
201 for (auto& voice : voices) {
202 if (!voice.IsPlaying()) {
203 continue;
204 }
205
206 size_t offset{};
207 s64 samples_remaining{BUFFER_SIZE};
208 while (samples_remaining > 0) {
209 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
210
211 if (samples.empty()) {
212 break;
213 }
214
215 samples_remaining -= samples.size();
216
217 for (const auto& sample : samples) {
218 const s32 buffer_sample{buffer[offset]};
219 buffer[offset++] =
220 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
221 }
222 }
223 }
224 audio_core->QueueBuffer(stream, tag, std::move(buffer));
225}
226
227void AudioRenderer::ReleaseAndQueueBuffers() {
228 const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
229 for (const auto& tag : released_buffers) {
230 QueueMixedBuffer(tag);
231 }
232}
233
234} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
new file mode 100644
index 000000000..6950a4681
--- /dev/null
+++ b/src/audio_core/audio_renderer.h
@@ -0,0 +1,206 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <vector>
10
11#include "audio_core/audio_out.h"
12#include "audio_core/codec.h"
13#include "audio_core/stream.h"
14#include "common/common_types.h"
15#include "common/swap.h"
16#include "core/hle/kernel/event.h"
17
18namespace AudioCore {
19
20enum class PlayState : u8 {
21 Started = 0,
22 Stopped = 1,
23 Paused = 2,
24};
25
26struct AudioRendererParameter {
27 u32_le sample_rate;
28 u32_le sample_count;
29 u32_le unknown_8;
30 u32_le unknown_c;
31 u32_le voice_count;
32 u32_le sink_count;
33 u32_le effect_count;
34 u32_le unknown_1c;
35 u8 unknown_20;
36 INSERT_PADDING_BYTES(3);
37 u32_le splitter_count;
38 u32_le unknown_2c;
39 INSERT_PADDING_WORDS(1);
40 u32_le revision;
41};
42static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
43
44enum class MemoryPoolStates : u32 { // Should be LE
45 Invalid = 0x0,
46 Unknown = 0x1,
47 RequestDetach = 0x2,
48 Detached = 0x3,
49 RequestAttach = 0x4,
50 Attached = 0x5,
51 Released = 0x6,
52};
53
54struct MemoryPoolEntry {
55 MemoryPoolStates state;
56 u32_le unknown_4;
57 u32_le unknown_8;
58 u32_le unknown_c;
59};
60static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
61
62struct MemoryPoolInfo {
63 u64_le pool_address;
64 u64_le pool_size;
65 MemoryPoolStates pool_state;
66 INSERT_PADDING_WORDS(3); // Unknown
67};
68static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
69struct BiquadFilter {
70 u8 enable;
71 INSERT_PADDING_BYTES(1);
72 std::array<s16_le, 3> numerator;
73 std::array<s16_le, 2> denominator;
74};
75static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
76
77struct WaveBuffer {
78 u64_le buffer_addr;
79 u64_le buffer_sz;
80 s32_le start_sample_offset;
81 s32_le end_sample_offset;
82 u8 is_looping;
83 u8 end_of_stream;
84 u8 sent_to_server;
85 INSERT_PADDING_BYTES(5);
86 u64 context_addr;
87 u64 context_sz;
88 INSERT_PADDING_BYTES(8);
89};
90static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
91
92struct VoiceInfo {
93 u32_le id;
94 u32_le node_id;
95 u8 is_new;
96 u8 is_in_use;
97 PlayState play_state;
98 u8 sample_format;
99 u32_le sample_rate;
100 u32_le priority;
101 u32_le sorting_order;
102 u32_le channel_count;
103 float_le pitch;
104 float_le volume;
105 std::array<BiquadFilter, 2> biquad_filter;
106 u32_le wave_buffer_count;
107 u32_le wave_buffer_head;
108 INSERT_PADDING_WORDS(1);
109 u64_le additional_params_addr;
110 u64_le additional_params_sz;
111 u32_le mix_id;
112 u32_le splitter_info_id;
113 std::array<WaveBuffer, 4> wave_buffer;
114 std::array<u32_le, 6> voice_channel_resource_ids;
115 INSERT_PADDING_BYTES(24);
116};
117static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
118
119struct VoiceOutStatus {
120 u64_le played_sample_count;
121 u32_le wave_buffer_consumed;
122 u32_le voice_drops_count;
123};
124static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
125
126struct UpdateDataHeader {
127 UpdateDataHeader() {}
128
129 explicit UpdateDataHeader(const AudioRendererParameter& config) {
130 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
131 behavior_size = 0xb0;
132 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
133 voices_size = config.voice_count * 0x10;
134 voice_resource_size = 0x0;
135 effects_size = config.effect_count * 0x10;
136 mixes_size = 0x0;
137 sinks_size = config.sink_count * 0x20;
138 performance_manager_size = 0x10;
139 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
140 effects_size + sinks_size + performance_manager_size;
141 }
142
143 u32_le revision;
144 u32_le behavior_size;
145 u32_le memory_pools_size;
146 u32_le voices_size;
147 u32_le voice_resource_size;
148 u32_le effects_size;
149 u32_le mixes_size;
150 u32_le sinks_size;
151 u32_le performance_manager_size;
152 INSERT_PADDING_WORDS(6);
153 u32_le total_size;
154};
155static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
156
157class AudioRenderer {
158public:
159 AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
160 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
161 void QueueMixedBuffer(Buffer::Tag tag);
162 void ReleaseAndQueueBuffers();
163
164private:
165 class VoiceState {
166 public:
167 bool IsPlaying() const {
168 return is_in_use && info.play_state == PlayState::Started;
169 }
170
171 const VoiceOutStatus& GetOutStatus() const {
172 return out_status;
173 }
174
175 const VoiceInfo& GetInfo() const {
176 return info;
177 }
178
179 VoiceInfo& Info() {
180 return info;
181 }
182
183 void SetWaveIndex(size_t index);
184 std::vector<s16> DequeueSamples(size_t sample_count);
185 void UpdateState();
186 void RefreshBuffer();
187
188 private:
189 bool is_in_use{};
190 bool is_refresh_pending{};
191 size_t wave_index{};
192 size_t offset{};
193 Codec::ADPCMState adpcm_state{};
194 std::vector<s16> samples;
195 VoiceOutStatus out_status{};
196 VoiceInfo info{};
197 };
198
199 AudioRendererParameter worker_params;
200 Kernel::SharedPtr<Kernel::Event> buffer_event;
201 std::vector<VoiceState> voices;
202 std::unique_ptr<AudioCore::AudioOut> audio_core;
203 AudioCore::StreamPtr stream;
204};
205
206} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
index 4bf5fd58a..a323b23ec 100644
--- a/src/audio_core/buffer.h
+++ b/src/audio_core/buffer.h
@@ -18,11 +18,16 @@ class Buffer {
18public: 18public:
19 using Tag = u64; 19 using Tag = u64;
20 20
21 Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {} 21 Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
22 22
23 /// Returns the raw audio data for the buffer 23 /// Returns the raw audio data for the buffer
24 const std::vector<u8>& GetData() const { 24 std::vector<s16>& Samples() {
25 return data; 25 return samples;
26 }
27
28 /// Returns the raw audio data for the buffer
29 const std::vector<s16>& GetSamples() const {
30 return samples;
26 } 31 }
27 32
28 /// Returns the buffer tag, this is provided by the game to the audout service 33 /// Returns the buffer tag, this is provided by the game to the audout service
@@ -32,7 +37,7 @@ public:
32 37
33private: 38private:
34 Tag tag; 39 Tag tag;
35 std::vector<u8> data; 40 std::vector<s16> samples;
36}; 41};
37 42
38using BufferPtr = std::shared_ptr<Buffer>; 43using BufferPtr = std::shared_ptr<Buffer>;
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..c3021403f
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,77 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "audio_core/codec.h"
8
9namespace AudioCore::Codec {
10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
12 ADPCMState& state) {
13 // GC-ADPCM with scale factor and variable coefficients.
14 // Frames are 8 bytes long containing 14 samples each.
15 // Samples are 4 bits (one nibble) long.
16
17 constexpr size_t FRAME_LEN = 8;
18 constexpr size_t SAMPLES_PER_FRAME = 14;
19 constexpr std::array<int, 16> SIGNED_NIBBLES = {
20 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
21
22 const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
23 const size_t ret_size =
24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
25 std::vector<s16> ret(ret_size);
26
27 int yn1 = state.yn1, yn2 = state.yn2;
28
29 const size_t NUM_FRAMES =
30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
31 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
32 const int frame_header = data[framei * FRAME_LEN];
33 const int scale = 1 << (frame_header & 0xF);
34 const int idx = (frame_header >> 4) & 0x7;
35
36 // Coefficients are fixed point with 11 bits fractional part.
37 const int coef1 = coeff[idx * 2 + 0];
38 const int coef2 = coeff[idx * 2 + 1];
39
40 // Decodes an audio sample. One nibble produces one sample.
41 const auto decode_sample = [&](const int nibble) -> s16 {
42 const int xn = nibble * scale;
43 // We first transform everything into 11 bit fixed point, perform the second order
44 // digital filter, then transform back.
45 // 0x400 == 0.5 in 11 bit fixed point.
46 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
47 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
48 // Clamp to output range.
49 val = std::clamp<s32>(val, -32768, 32767);
50 // Advance output feedback.
51 yn2 = yn1;
52 yn1 = val;
53 return static_cast<s16>(val);
54 };
55
56 size_t outputi = framei * SAMPLES_PER_FRAME;
57 size_t datai = framei * FRAME_LEN + 1;
58 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
60 ret[outputi] = sample1;
61 outputi++;
62
63 const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
64 ret[outputi] = sample2;
65 outputi++;
66
67 datai++;
68 }
69 }
70
71 state.yn1 = yn1;
72 state.yn2 = yn2;
73
74 return ret;
75}
76
77} // namespace AudioCore::Codec
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..3f845c42c
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore::Codec {
13
14enum class PcmFormat : u32 {
15 Invalid = 0,
16 Int8 = 1,
17 Int16 = 2,
18 Int24 = 3,
19 Int32 = 4,
20 PcmFloat = 5,
21 Adpcm = 6,
22};
23
24/// See: Codec::DecodeADPCM
25struct ADPCMState {
26 // Two historical samples from previous processed buffer,
27 // required for ADPCM decoding
28 s16 yn1; ///< y[n-1]
29 s16 yn2; ///< y[n-2]
30};
31
32using ADPCM_Coeff = std::array<s16, 16>;
33
34/**
35 * @param data Pointer to buffer that contains ADPCM data to decode
36 * @param size Size of buffer in bytes
37 * @param coeff ADPCM coefficients
38 * @param state ADPCM state, this is updated with new state
39 * @return Decoded stereo signed PCM16 data, sample_count in length
40 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
42 ADPCMState& state);
43
44}; // namespace AudioCore::Codec
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 34ae5b062..1501ef1f4 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -13,20 +13,30 @@ namespace AudioCore {
13 13
14class SinkStreamImpl final : public SinkStream { 14class SinkStreamImpl final : public SinkStream {
15public: 15public:
16 SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} { 16 SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
17 cubeb_stream_params params; 17 const std::string& name)
18 params.rate = 48000; 18 : ctx{ctx}, num_channels{num_channels_} {
19 params.channels = GetNumChannels(); 19
20 if (num_channels == 6) {
21 // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
22 // channel for now
23 is_6_channel = true;
24 num_channels = 2;
25 }
26
27 cubeb_stream_params params{};
28 params.rate = sample_rate;
29 params.channels = num_channels;
20 params.format = CUBEB_SAMPLE_S16NE; 30 params.format = CUBEB_SAMPLE_S16NE;
21 params.layout = CUBEB_LAYOUT_STEREO; 31 params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
22 32
23 u32 minimum_latency = 0; 33 u32 minimum_latency{};
24 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) { 34 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
25 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); 35 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
26 } 36 }
27 37
28 if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr, 38 if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
29 output_device, &params, std::max(512u, minimum_latency), 39 &params, std::max(512u, minimum_latency),
30 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback, 40 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
31 this) != CUBEB_OK) { 41 this) != CUBEB_OK) {
32 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream"); 42 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -51,33 +61,29 @@ public:
51 cubeb_stream_destroy(stream_backend); 61 cubeb_stream_destroy(stream_backend);
52 } 62 }
53 63
54 void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override { 64 void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
55 if (!ctx) { 65 if (!ctx) {
56 return; 66 return;
57 } 67 }
58 68
59 queue.reserve(queue.size() + sample_count * GetNumChannels()); 69 queue.reserve(queue.size() + samples.size() * GetNumChannels());
60 70
61 if (num_channels == 2) { 71 if (is_6_channel) {
62 // Copy as-is
63 std::copy(samples, samples + sample_count * GetNumChannels(),
64 std::back_inserter(queue));
65 } else if (num_channels == 6) {
66 // Downsample 6 channels to 2 72 // Downsample 6 channels to 2
67 const size_t sample_count_copy_size = sample_count * num_channels * 2; 73 const size_t sample_count_copy_size = samples.size() * 2;
68 queue.reserve(sample_count_copy_size); 74 queue.reserve(sample_count_copy_size);
69 for (size_t i = 0; i < sample_count * num_channels; i += num_channels) { 75 for (size_t i = 0; i < samples.size(); i += num_channels) {
70 queue.push_back(samples[i]); 76 queue.push_back(samples[i]);
71 queue.push_back(samples[i + 1]); 77 queue.push_back(samples[i + 1]);
72 } 78 }
73 } else { 79 } else {
74 ASSERT_MSG(false, "Unimplemented"); 80 // Copy as-is
81 std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
75 } 82 }
76 } 83 }
77 84
78 u32 GetNumChannels() const { 85 u32 GetNumChannels() const {
79 // Only support 2-channel stereo output for now 86 return num_channels;
80 return 2;
81 } 87 }
82 88
83private: 89private:
@@ -85,6 +91,8 @@ private:
85 91
86 cubeb* ctx{}; 92 cubeb* ctx{};
87 cubeb_stream* stream_backend{}; 93 cubeb_stream* stream_backend{};
94 u32 num_channels{};
95 bool is_6_channel{};
88 96
89 std::vector<s16> queue; 97 std::vector<s16> queue;
90 98
@@ -129,8 +137,10 @@ CubebSink::~CubebSink() {
129 cubeb_destroy(ctx); 137 cubeb_destroy(ctx);
130} 138}
131 139
132SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) { 140SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
133 sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device)); 141 const std::string& name) {
142 sink_streams.push_back(
143 std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
134 return *sink_streams.back(); 144 return *sink_streams.back();
135} 145}
136 146
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
index d07113f1f..59cbf05e9 100644
--- a/src/audio_core/cubeb_sink.h
+++ b/src/audio_core/cubeb_sink.h
@@ -18,7 +18,8 @@ public:
18 explicit CubebSink(std::string device_id); 18 explicit CubebSink(std::string device_id);
19 ~CubebSink() override; 19 ~CubebSink() override;
20 20
21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override; 21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
22 const std::string& name) override;
22 23
23private: 24private:
24 cubeb* ctx{}; 25 cubeb* ctx{};
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index 2e04438f7..f235d93e5 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -13,14 +13,14 @@ public:
13 explicit NullSink(std::string){}; 13 explicit NullSink(std::string){};
14 ~NullSink() override = default; 14 ~NullSink() override = default;
15 15
16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override { 16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
17 const std::string& /*name*/) override {
17 return null_sink_stream; 18 return null_sink_stream;
18 } 19 }
19 20
20private: 21private:
21 struct NullSinkStreamImpl final : SinkStream { 22 struct NullSinkStreamImpl final : SinkStream {
22 void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/, 23 void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
23 size_t /*sample_count*/) override {}
24 } null_sink_stream; 24 } null_sink_stream;
25}; 25};
26 26
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index d1bb98c3d..95c7b2b6e 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8 9
9#include "audio_core/sink_stream.h" 10#include "audio_core/sink_stream.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
21class Sink { 22class Sink {
22public: 23public:
23 virtual ~Sink() = default; 24 virtual ~Sink() = default;
24 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0; 25 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
26 const std::string& name) = 0;
25}; 27};
26 28
27using SinkPtr = std::unique_ptr<Sink>; 29using SinkPtr = std::unique_ptr<Sink>;
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
index e7a3f01b0..41b6736d8 100644
--- a/src/audio_core/sink_stream.h
+++ b/src/audio_core/sink_stream.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -22,9 +23,8 @@ public:
22 * Feed stereo samples to sink. 23 * Feed stereo samples to sink.
23 * @param num_channels Number of channels used. 24 * @param num_channels Number of channels used.
24 * @param samples Samples in interleaved stereo PCM16 format. 25 * @param samples Samples in interleaved stereo PCM16 format.
25 * @param sample_count Number of samples.
26 */ 26 */
27 virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0; 27 virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
28}; 28};
29 29
30using SinkStreamPtr = std::unique_ptr<SinkStream>; 30using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index a0045b7a1..ad9e2915c 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -32,17 +32,13 @@ u32 Stream::GetNumChannels() const {
32 return {}; 32 return {};
33} 33}
34 34
35u32 Stream::GetSampleSize() const {
36 return GetNumChannels() * 2;
37}
38
39Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 35Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
40 SinkStream& sink_stream) 36 SinkStream& sink_stream, std::string&& name_)
41 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, 37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
42 sink_stream{sink_stream} { 38 sink_stream{sink_stream}, name{std::move(name_)} {
43 39
44 release_event = CoreTiming::RegisterEvent( 40 release_event = CoreTiming::RegisterEvent(
45 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
46} 42}
47 43
48void Stream::Play() { 44void Stream::Play() {
@@ -55,17 +51,15 @@ void Stream::Stop() {
55} 51}
56 52
57s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 53s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
58 const size_t num_samples{buffer.GetData().size() / GetSampleSize()}; 54 const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
59 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); 55 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
60} 56}
61 57
62static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) { 58static void VolumeAdjustSamples(std::vector<s16>& samples) {
63 std::vector<s16> samples(data.size() / sizeof(s16));
64 std::memcpy(samples.data(), data.data(), data.size());
65 const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)}; 59 const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
66 60
67 if (volume == 1.0f) { 61 if (volume == 1.0f) {
68 return samples; 62 return;
69 } 63 }
70 64
71 // Implementation of a volume slider with a dynamic range of 60 dB 65 // Implementation of a volume slider with a dynamic range of 60 dB
@@ -73,8 +67,6 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
73 for (auto& sample : samples) { 67 for (auto& sample : samples) {
74 sample = static_cast<s16>(sample * volume_scale_factor); 68 sample = static_cast<s16>(sample * volume_scale_factor);
75 } 69 }
76
77 return samples;
78} 70}
79 71
80void Stream::PlayNextBuffer() { 72void Stream::PlayNextBuffer() {
@@ -96,14 +88,14 @@ void Stream::PlayNextBuffer() {
96 active_buffer = queued_buffers.front(); 88 active_buffer = queued_buffers.front();
97 queued_buffers.pop(); 89 queued_buffers.pop();
98 90
99 const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()}; 91 VolumeAdjustSamples(active_buffer->Samples());
100 sink_stream.EnqueueSamples( 92 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
101 GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
102 93
103 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 94 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
104} 95}
105 96
106void Stream::ReleaseActiveBuffer() { 97void Stream::ReleaseActiveBuffer() {
98 ASSERT(active_buffer);
107 released_buffers.push(std::move(active_buffer)); 99 released_buffers.push(std::move(active_buffer));
108 release_callback(); 100 release_callback();
109 PlayNextBuffer(); 101 PlayNextBuffer();
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 35253920e..049b92ca9 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -6,6 +6,7 @@
6 6
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <string>
9#include <vector> 10#include <vector>
10#include <queue> 11#include <queue>
11 12
@@ -33,7 +34,7 @@ public:
33 using ReleaseCallback = std::function<void()>; 34 using ReleaseCallback = std::function<void()>;
34 35
35 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback, 36 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
36 SinkStream& sink_stream); 37 SinkStream& sink_stream, std::string&& name_);
37 38
38 /// Plays the audio stream 39 /// Plays the audio stream
39 void Play(); 40 void Play();
@@ -68,9 +69,6 @@ public:
68 /// Gets the number of channels 69 /// Gets the number of channels
69 u32 GetNumChannels() const; 70 u32 GetNumChannels() const;
70 71
71 /// Gets the sample size in bytes
72 u32 GetSampleSize() const;
73
74private: 72private:
75 /// Current state of the stream 73 /// Current state of the stream
76 enum class State { 74 enum class State {
@@ -96,6 +94,7 @@ private:
96 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream 94 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
97 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream 95 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
98 SinkStream& sink_stream; ///< Output sink for the stream 96 SinkStream& sink_stream; ///< Output sink for the stream
97 std::string name; ///< Name of the stream, must be unique
99}; 98};
100 99
101using StreamPtr = std::shared_ptr<Stream>; 100using StreamPtr = std::shared_ptr<Stream>;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 34dec06fe..355abd682 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -168,6 +168,7 @@ void FileBackend::Write(const Entry& entry) {
168 SUB(Service, AM) \ 168 SUB(Service, AM) \
169 SUB(Service, AOC) \ 169 SUB(Service, AOC) \
170 SUB(Service, APM) \ 170 SUB(Service, APM) \
171 SUB(Service, ARP) \
171 SUB(Service, BCAT) \ 172 SUB(Service, BCAT) \
172 SUB(Service, BPC) \ 173 SUB(Service, BPC) \
173 SUB(Service, BTM) \ 174 SUB(Service, BTM) \
@@ -199,6 +200,7 @@ void FileBackend::Write(const Entry& entry) {
199 SUB(Service, SPL) \ 200 SUB(Service, SPL) \
200 SUB(Service, SSL) \ 201 SUB(Service, SSL) \
201 SUB(Service, Time) \ 202 SUB(Service, Time) \
203 SUB(Service, USB) \
202 SUB(Service, VI) \ 204 SUB(Service, VI) \
203 SUB(Service, WLAN) \ 205 SUB(Service, WLAN) \
204 CLS(HW) \ 206 CLS(HW) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index dd5c9e6be..a889ebefa 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -54,6 +54,7 @@ enum class Class : ClassType {
54 Service_AM, ///< The AM (Applet manager) service 54 Service_AM, ///< The AM (Applet manager) service
55 Service_AOC, ///< The AOC (AddOn Content) service 55 Service_AOC, ///< The AOC (AddOn Content) service
56 Service_APM, ///< The APM (Performance) service 56 Service_APM, ///< The APM (Performance) service
57 Service_ARP, ///< The ARP service
57 Service_Audio, ///< The Audio (Audio control) service 58 Service_Audio, ///< The Audio (Audio control) service
58 Service_BCAT, ///< The BCAT service 59 Service_BCAT, ///< The BCAT service
59 Service_BPC, ///< The BPC service 60 Service_BPC, ///< The BPC service
@@ -86,6 +87,7 @@ enum class Class : ClassType {
86 Service_SPL, ///< The SPL service 87 Service_SPL, ///< The SPL service
87 Service_SSL, ///< The SSL service 88 Service_SSL, ///< The SSL service
88 Service_Time, ///< The time service 89 Service_Time, ///< The time service
90 Service_USB, ///< The USB (Universal Serial Bus) service
89 Service_VI, ///< The VI (Video interface) service 91 Service_VI, ///< The VI (Video interface) service
90 Service_WLAN, ///< The WLAN (Wireless local area network) service 92 Service_WLAN, ///< The WLAN (Wireless local area network) service
91 HW, ///< Low-level hardware emulation 93 HW, ///< Low-level hardware emulation
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 28de22398..0abf7edc1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -134,6 +134,8 @@ add_library(core STATIC
134 hle/service/apm/apm.h 134 hle/service/apm/apm.h
135 hle/service/apm/interface.cpp 135 hle/service/apm/interface.cpp
136 hle/service/apm/interface.h 136 hle/service/apm/interface.h
137 hle/service/arp/arp.cpp
138 hle/service/arp/arp.h
137 hle/service/audio/audctl.cpp 139 hle/service/audio/audctl.cpp
138 hle/service/audio/audctl.h 140 hle/service/audio/audctl.h
139 hle/service/audio/auddbg.cpp 141 hle/service/audio/auddbg.cpp
@@ -313,6 +315,8 @@ add_library(core STATIC
313 hle/service/time/interface.h 315 hle/service/time/interface.h
314 hle/service/time/time.cpp 316 hle/service/time/time.cpp
315 hle/service/time/time.h 317 hle/service/time/time.h
318 hle/service/usb/usb.cpp
319 hle/service/usb/usb.h
316 hle/service/vi/vi.cpp 320 hle/service/vi/vi.cpp
317 hle/service/vi/vi.h 321 hle/service/vi/vi.h
318 hle/service/vi/vi_m.cpp 322 hle/service/vi/vi_m.cpp
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 4c11f35a4..6bc349460 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
203 } 203 }
204 Kernel::Thread* thread = Kernel::GetCurrentThread(); 204 Kernel::Thread* thread = Kernel::GetCurrentThread();
205 SaveContext(thread->context); 205 SaveContext(thread->context);
206 if (last_bkpt_hit || (num_instructions == 1)) { 206 if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
207 last_bkpt_hit = false; 207 last_bkpt_hit = false;
208 GDBStub::Break(); 208 GDBStub::Break();
209 GDBStub::SendTrap(thread, 5); 209 GDBStub::SendTrap(thread, 5);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e51e66550..085ba68d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -18,6 +18,7 @@
18#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19#include "core/settings.h" 19#include "core/settings.h"
20#include "file_sys/vfs_real.h" 20#include "file_sys/vfs_real.h"
21#include "video_core/renderer_base.h"
21#include "video_core/video_core.h" 22#include "video_core/video_core.h"
22 23
23namespace Core { 24namespace Core {
@@ -61,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
61 // execute. Otherwise, get out of the loop function. 62 // execute. Otherwise, get out of the loop function.
62 if (GDBStub::GetCpuHaltFlag()) { 63 if (GDBStub::GetCpuHaltFlag()) {
63 if (GDBStub::GetCpuStepFlag()) { 64 if (GDBStub::GetCpuStepFlag()) {
64 GDBStub::SetCpuStepFlag(false);
65 tight_loop = false; 65 tight_loop = false;
66 } else { 66 } else {
67 return ResultStatus::Success; 67 return ResultStatus::Success;
@@ -77,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
77 } 77 }
78 } 78 }
79 79
80 if (GDBStub::IsServerEnabled()) {
81 GDBStub::SetCpuStepFlag(false);
82 }
83
80 return status; 84 return status;
81} 85}
82 86
@@ -178,7 +182,6 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
178 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 182 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
179 } 183 }
180 184
181 gpu_core = std::make_unique<Tegra::GPU>();
182 telemetry_session = std::make_unique<Core::TelemetrySession>(); 185 telemetry_session = std::make_unique<Core::TelemetrySession>();
183 service_manager = std::make_shared<Service::SM::ServiceManager>(); 186 service_manager = std::make_shared<Service::SM::ServiceManager>();
184 187
@@ -186,10 +189,13 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
186 Service::Init(service_manager); 189 Service::Init(service_manager);
187 GDBStub::Init(); 190 GDBStub::Init();
188 191
189 if (!VideoCore::Init(emu_window)) { 192 renderer = VideoCore::CreateRenderer(emu_window);
193 if (!renderer->Init()) {
190 return ResultStatus::ErrorVideoCore; 194 return ResultStatus::ErrorVideoCore;
191 } 195 }
192 196
197 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
198
193 // Create threads for CPU cores 1-3, and build thread_to_cpu map 199 // Create threads for CPU cores 1-3, and build thread_to_cpu map
194 // CPU core 0 is run on the main thread 200 // CPU core 0 is run on the main thread
195 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 201 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -221,7 +227,7 @@ void System::Shutdown() {
221 perf_results.frametime * 1000.0); 227 perf_results.frametime * 1000.0);
222 228
223 // Shutdown emulation session 229 // Shutdown emulation session
224 VideoCore::Shutdown(); 230 renderer.reset();
225 GDBStub::Shutdown(); 231 GDBStub::Shutdown();
226 Service::Shutdown(); 232 Service::Shutdown();
227 Kernel::Shutdown(); 233 Kernel::Shutdown();
diff --git a/src/core/core.h b/src/core/core.h
index 4c9967559..c8ca4b247 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -27,6 +27,10 @@ namespace Service::SM {
27class ServiceManager; 27class ServiceManager;
28} 28}
29 29
30namespace VideoCore {
31class RendererBase;
32}
33
30namespace Core { 34namespace Core {
31 35
32class System { 36class System {
@@ -78,6 +82,17 @@ public:
78 */ 82 */
79 ResultStatus SingleStep(); 83 ResultStatus SingleStep();
80 84
85 /**
86 * Invalidate the CPU instruction caches
87 * This function should only be used by GDB Stub to support breakpoints, memory updates and
88 * step/continue commands.
89 */
90 void InvalidateCpuInstructionCaches() {
91 for (auto& cpu : cpu_cores) {
92 cpu->ArmInterface().ClearInstructionCache();
93 }
94 }
95
81 /// Shutdown the emulated system. 96 /// Shutdown the emulated system.
82 void Shutdown(); 97 void Shutdown();
83 98
@@ -129,11 +144,26 @@ public:
129 /// Gets a CPU interface to the CPU core with the specified index 144 /// Gets a CPU interface to the CPU core with the specified index
130 Cpu& CpuCore(size_t core_index); 145 Cpu& CpuCore(size_t core_index);
131 146
132 /// Gets the GPU interface 147 /// Gets a mutable reference to the GPU interface
133 Tegra::GPU& GPU() { 148 Tegra::GPU& GPU() {
134 return *gpu_core; 149 return *gpu_core;
135 } 150 }
136 151
152 /// Gets an immutable reference to the GPU interface.
153 const Tegra::GPU& GPU() const {
154 return *gpu_core;
155 }
156
157 /// Gets a mutable reference to the renderer.
158 VideoCore::RendererBase& Renderer() {
159 return *renderer;
160 }
161
162 /// Gets an immutable reference to the renderer.
163 const VideoCore::RendererBase& Renderer() const {
164 return *renderer;
165 }
166
137 /// Gets the scheduler for the CPU core that is currently running 167 /// Gets the scheduler for the CPU core that is currently running
138 Kernel::Scheduler& CurrentScheduler() { 168 Kernel::Scheduler& CurrentScheduler() {
139 return *CurrentCpuCore().Scheduler(); 169 return *CurrentCpuCore().Scheduler();
@@ -197,6 +227,7 @@ private:
197 227
198 /// AppLoader used to load the current executing application 228 /// AppLoader used to load the current executing application
199 std::unique_ptr<Loader::AppLoader> app_loader; 229 std::unique_ptr<Loader::AppLoader> app_loader;
230 std::unique_ptr<VideoCore::RendererBase> renderer;
200 std::unique_ptr<Tegra::GPU> gpu_core; 231 std::unique_ptr<Tegra::GPU> gpu_core;
201 std::shared_ptr<Tegra::DebugContext> debug_context; 232 std::shared_ptr<Tegra::DebugContext> debug_context;
202 Kernel::SharedPtr<Kernel::Process> current_process; 233 Kernel::SharedPtr<Kernel::Process> current_process;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a1b6f96f1..d3bb6f818 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
141 ForceExceptionCheck(cycles_into_future); 141 ForceExceptionCheck(cycles_into_future);
142 142
143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
145} 145}
146 146
147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
156 // Removing random items breaks the invariant so we have to re-establish it. 156 // Removing random items breaks the invariant so we have to re-establish it.
157 if (itr != event_queue.end()) { 157 if (itr != event_queue.end()) {
158 event_queue.erase(itr, event_queue.end()); 158 event_queue.erase(itr, event_queue.end());
159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
160 } 160 }
161} 161}
162 162
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
167 // Removing random items breaks the invariant so we have to re-establish it. 167 // Removing random items breaks the invariant so we have to re-establish it.
168 if (itr != event_queue.end()) { 168 if (itr != event_queue.end()) {
169 event_queue.erase(itr, event_queue.end()); 169 event_queue.erase(itr, event_queue.end());
170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
171 } 171 }
172} 172}
173 173
@@ -190,7 +190,7 @@ void MoveEvents() {
190 for (Event ev; ts_queue.Pop(ev);) { 190 for (Event ev; ts_queue.Pop(ev);) {
191 ev.fifo_order = event_fifo_id++; 191 ev.fifo_order = event_fifo_id++;
192 event_queue.emplace_back(std::move(ev)); 192 event_queue.emplace_back(std::move(ev));
193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
194 } 194 }
195} 195}
196 196
@@ -205,7 +205,7 @@ void Advance() {
205 205
206 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 206 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
207 Event evt = std::move(event_queue.front()); 207 Event evt = std::move(event_queue.front());
208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
209 event_queue.pop_back(); 209 event_queue.pop_back();
210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); 210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
211 } 211 }
@@ -226,8 +226,8 @@ void Idle() {
226 downcount = 0; 226 downcount = 0;
227} 227}
228 228
229u64 GetGlobalTimeUs() { 229std::chrono::microseconds GetGlobalTimeUs() {
230 return GetTicks() * 1000000 / BASE_CLOCK_RATE; 230 return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
231} 231}
232 232
233int GetDowncount() { 233int GetDowncount() {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 7fe6380ad..dfa161c0d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -17,12 +17,17 @@
17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") 17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
18 */ 18 */
19 19
20#include <chrono>
20#include <functional> 21#include <functional>
21#include <string> 22#include <string>
22#include "common/common_types.h" 23#include "common/common_types.h"
23 24
24namespace CoreTiming { 25namespace CoreTiming {
25 26
27struct EventType;
28
29using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
30
26/** 31/**
27 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 32 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
28 * required to end slice -1 and start slice 0 before the first cycle of code is executed. 33 * required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -30,8 +35,6 @@ namespace CoreTiming {
30void Init(); 35void Init();
31void Shutdown(); 36void Shutdown();
32 37
33typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
34
35/** 38/**
36 * This should only be called from the emu thread, if you are calling it any other thread, you are 39 * This should only be called from the emu thread, if you are calling it any other thread, you are
37 * doing something evil 40 * doing something evil
@@ -40,8 +43,6 @@ u64 GetTicks();
40u64 GetIdleTicks(); 43u64 GetIdleTicks();
41void AddTicks(u64 ticks); 44void AddTicks(u64 ticks);
42 45
43struct EventType;
44
45/** 46/**
46 * Returns the event_type identifier. if name is not unique, it will assert. 47 * Returns the event_type identifier. if name is not unique, it will assert.
47 */ 48 */
@@ -86,7 +87,7 @@ void ClearPendingEvents();
86 87
87void ForceExceptionCheck(s64 cycles); 88void ForceExceptionCheck(s64 cycles);
88 89
89u64 GetGlobalTimeUs(); 90std::chrono::microseconds GetGlobalTimeUs();
90 91
91int GetDowncount(); 92int GetDowncount();
92 93
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 4690af5f8..a9876c83e 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -3,10 +3,22 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mbedtls/cipher.h> 5#include <mbedtls/cipher.h>
6#include "common/assert.h"
7#include "common/logging/log.h"
6#include "core/crypto/aes_util.h" 8#include "core/crypto/aes_util.h"
7#include "core/crypto/key_manager.h" 9#include "core/crypto/key_manager.h"
8 10
9namespace Core::Crypto { 11namespace Core::Crypto {
12namespace {
13std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
14 std::vector<u8> out(0x10);
15 for (size_t i = 0xF; i <= 0xF; --i) {
16 out[i] = sector_id & 0xFF;
17 sector_id >>= 8;
18 }
19 return out;
20}
21} // Anonymous namespace
10 22
11static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), 23static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
12 "CTR has incorrect value."); 24 "CTR has incorrect value.");
@@ -56,27 +68,28 @@ void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
56} 68}
57 69
58template <typename Key, size_t KeySize> 70template <typename Key, size_t KeySize>
59void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) { 71void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
60 size_t written = 0; 72 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
61
62 const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
63 73
64 mbedtls_cipher_reset(context); 74 mbedtls_cipher_reset(context);
65 75
76 size_t written = 0;
66 if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { 77 if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
67 mbedtls_cipher_update(context, src, size, dest, &written); 78 mbedtls_cipher_update(context, src, size, dest, &written);
68 if (written != size) 79 if (written != size) {
69 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", 80 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
70 size, written); 81 size, written);
82 }
71 } else { 83 } else {
72 const auto block_size = mbedtls_cipher_get_block_size(context); 84 const auto block_size = mbedtls_cipher_get_block_size(context);
73 85
74 for (size_t offset = 0; offset < size; offset += block_size) { 86 for (size_t offset = 0; offset < size; offset += block_size) {
75 auto length = std::min<size_t>(block_size, size - offset); 87 auto length = std::min<size_t>(block_size, size - offset);
76 mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); 88 mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
77 if (written != length) 89 if (written != length) {
78 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", 90 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
79 length, written); 91 length, written);
92 }
80 } 93 }
81 } 94 }
82 95
@@ -97,16 +110,6 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
97 } 110 }
98} 111}
99 112
100template <typename Key, size_t KeySize>
101std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) {
102 std::vector<u8> out(0x10);
103 for (size_t i = 0xF; i <= 0xF; --i) {
104 out[i] = sector_id & 0xFF;
105 sector_id >>= 8;
106 }
107 return out;
108}
109
110template class AESCipher<Key128>; 113template class AESCipher<Key128>;
111template class AESCipher<Key256>; 114template class AESCipher<Key256>;
112} // namespace Core::Crypto \ No newline at end of file 115} // namespace Core::Crypto \ No newline at end of file
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index 5b0b02738..8ce9d6612 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <type_traits> 8#include <type_traits>
9#include <vector> 9#include <vector>
10#include "common/assert.h" 10#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
12 12
13namespace Core::Crypto { 13namespace Core::Crypto {
@@ -38,15 +38,19 @@ public:
38 void SetIV(std::vector<u8> iv); 38 void SetIV(std::vector<u8> iv);
39 39
40 template <typename Source, typename Dest> 40 template <typename Source, typename Dest>
41 void Transcode(const Source* src, size_t size, Dest* dest, Op op) { 41 void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
42 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
43 "Transcode source and destination types must be trivially copyable.");
42 Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); 44 Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
43 } 45 }
44 46
45 void Transcode(const u8* src, size_t size, u8* dest, Op op); 47 void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
46 48
47 template <typename Source, typename Dest> 49 template <typename Source, typename Dest>
48 void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, 50 void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
49 size_t sector_size, Op op) { 51 size_t sector_size, Op op) {
52 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
53 "XTSTranscode source and destination types must be trivially copyable.");
50 XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, 54 XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
51 sector_size, op); 55 sector_size, op);
52 } 56 }
@@ -56,7 +60,5 @@ public:
56 60
57private: 61private:
58 std::unique_ptr<CipherContext> ctx; 62 std::unique_ptr<CipherContext> ctx;
59
60 static std::vector<u8> CalculateNintendoTweak(size_t sector_id);
61}; 63};
62} // namespace Core::Crypto 64} // namespace Core::Crypto
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
index 71bca1f23..7f05af9b4 100644
--- a/src/core/crypto/encryption_layer.h
+++ b/src/core/crypto/encryption_layer.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h"
7#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs.h"
8 9
9namespace Core::Crypto { 10namespace Core::Crypto {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 678ac5752..fc45e7ab5 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -2,19 +2,16 @@
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 <algorithm>
5#include <array> 6#include <array>
6#include <fstream> 7#include <fstream>
7#include <locale> 8#include <locale>
8#include <sstream> 9#include <sstream>
9#include <string_view> 10#include <string_view>
10#include <mbedtls/sha256.h>
11#include "common/assert.h"
12#include "common/common_paths.h" 11#include "common/common_paths.h"
13#include "common/file_util.h" 12#include "common/file_util.h"
14#include "common/logging/log.h"
15#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
16#include "core/settings.h" 14#include "core/settings.h"
17#include "key_manager.h"
18 15
19namespace Core::Crypto { 16namespace Core::Crypto {
20 17
@@ -66,8 +63,7 @@ KeyManager::KeyManager() {
66 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); 63 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
67} 64}
68 65
69void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) { 66void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
70 const auto filename = std::string(filename_);
71 std::ifstream file(filename); 67 std::ifstream file(filename);
72 if (!file.is_open()) 68 if (!file.is_open())
73 return; 69 return;
@@ -107,11 +103,8 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
107 } 103 }
108} 104}
109 105
110void KeyManager::AttemptLoadKeyFile(std::string_view dir1_, std::string_view dir2_, 106void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
111 std::string_view filename_, bool title) { 107 const std::string& filename, bool title) {
112 const std::string dir1(dir1_);
113 const std::string dir2(dir2_);
114 const std::string filename(filename_);
115 if (FileUtil::Exists(dir1 + DIR_SEP + filename)) 108 if (FileUtil::Exists(dir1 + DIR_SEP + filename))
116 LoadFromFile(dir1 + DIR_SEP + filename, title); 109 LoadFromFile(dir1 + DIR_SEP + filename, title);
117 else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) 110 else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 03152a12c..c4c53cefc 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <string>
8#include <type_traits> 9#include <type_traits>
9#include <unordered_map> 10#include <unordered_map>
10#include <vector> 11#include <vector>
@@ -109,9 +110,9 @@ private:
109 std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys; 110 std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
110 111
111 bool dev_mode; 112 bool dev_mode;
112 void LoadFromFile(std::string_view filename, bool is_title_keys); 113 void LoadFromFile(const std::string& filename, bool is_title_keys);
113 void AttemptLoadKeyFile(std::string_view dir1, std::string_view dir2, std::string_view filename, 114 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
114 bool title); 115 const std::string& filename, bool title);
115 116
116 static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id; 117 static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
117 static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id; 118 static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 75f6b8235..332e5c3d0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -41,40 +41,42 @@
41#include "core/loader/loader.h" 41#include "core/loader/loader.h"
42#include "core/memory.h" 42#include "core/memory.h"
43 43
44const int GDB_BUFFER_SIZE = 10000; 44namespace GDBStub {
45namespace {
46constexpr int GDB_BUFFER_SIZE = 10000;
45 47
46const char GDB_STUB_START = '$'; 48constexpr char GDB_STUB_START = '$';
47const char GDB_STUB_END = '#'; 49constexpr char GDB_STUB_END = '#';
48const char GDB_STUB_ACK = '+'; 50constexpr char GDB_STUB_ACK = '+';
49const char GDB_STUB_NACK = '-'; 51constexpr char GDB_STUB_NACK = '-';
50 52
51#ifndef SIGTRAP 53#ifndef SIGTRAP
52const u32 SIGTRAP = 5; 54constexpr u32 SIGTRAP = 5;
53#endif 55#endif
54 56
55#ifndef SIGTERM 57#ifndef SIGTERM
56const u32 SIGTERM = 15; 58constexpr u32 SIGTERM = 15;
57#endif 59#endif
58 60
59#ifndef MSG_WAITALL 61#ifndef MSG_WAITALL
60const u32 MSG_WAITALL = 8; 62constexpr u32 MSG_WAITALL = 8;
61#endif 63#endif
62 64
63const u32 LR_REGISTER = 30; 65constexpr u32 LR_REGISTER = 30;
64const u32 SP_REGISTER = 31; 66constexpr u32 SP_REGISTER = 31;
65const u32 PC_REGISTER = 32; 67constexpr u32 PC_REGISTER = 32;
66const u32 CPSR_REGISTER = 33; 68constexpr u32 CPSR_REGISTER = 33;
67const u32 UC_ARM64_REG_Q0 = 34; 69constexpr u32 UC_ARM64_REG_Q0 = 34;
68const u32 FPSCR_REGISTER = 66; 70constexpr u32 FPSCR_REGISTER = 66;
69 71
70// TODO/WiP - Used while working on support for FPU 72// TODO/WiP - Used while working on support for FPU
71const u32 TODO_DUMMY_REG_997 = 997; 73constexpr u32 TODO_DUMMY_REG_997 = 997;
72const u32 TODO_DUMMY_REG_998 = 998; 74constexpr u32 TODO_DUMMY_REG_998 = 998;
73 75
74// For sample XML files see the GDB source /gdb/features 76// For sample XML files see the GDB source /gdb/features
75// GDB also wants the l character at the start 77// GDB also wants the l character at the start
76// This XML defines what the registers are for this specific ARM device 78// This XML defines what the registers are for this specific ARM device
77static const char* target_xml = 79constexpr char target_xml[] =
78 R"(l<?xml version="1.0"?> 80 R"(l<?xml version="1.0"?>
79<!DOCTYPE target SYSTEM "gdb-target.dtd"> 81<!DOCTYPE target SYSTEM "gdb-target.dtd">
80<target version="1.0"> 82<target version="1.0">
@@ -140,30 +142,28 @@ static const char* target_xml =
140</target> 142</target>
141)"; 143)";
142 144
143namespace GDBStub { 145int gdbserver_socket = -1;
144
145static int gdbserver_socket = -1;
146 146
147static u8 command_buffer[GDB_BUFFER_SIZE]; 147u8 command_buffer[GDB_BUFFER_SIZE];
148static u32 command_length; 148u32 command_length;
149 149
150static u32 latest_signal = 0; 150u32 latest_signal = 0;
151static bool memory_break = false; 151bool memory_break = false;
152 152
153static Kernel::Thread* current_thread = nullptr; 153Kernel::Thread* current_thread = nullptr;
154static u32 current_core = 0; 154u32 current_core = 0;
155 155
156// Binding to a port within the reserved ports range (0-1023) requires root permissions, 156// Binding to a port within the reserved ports range (0-1023) requires root permissions,
157// so default to a port outside of that range. 157// so default to a port outside of that range.
158static u16 gdbstub_port = 24689; 158u16 gdbstub_port = 24689;
159 159
160static bool halt_loop = true; 160bool halt_loop = true;
161static bool step_loop = false; 161bool step_loop = false;
162static bool send_trap = false; 162bool send_trap = false;
163 163
164// If set to false, the server will never be started and no 164// If set to false, the server will never be started and no
165// gdbstub-related functions will be executed. 165// gdbstub-related functions will be executed.
166static std::atomic<bool> server_enabled(false); 166std::atomic<bool> server_enabled(false);
167 167
168#ifdef _WIN32 168#ifdef _WIN32
169WSADATA InitData; 169WSADATA InitData;
@@ -171,23 +171,26 @@ WSADATA InitData;
171 171
172struct Breakpoint { 172struct Breakpoint {
173 bool active; 173 bool active;
174 PAddr addr; 174 VAddr addr;
175 u64 len; 175 u64 len;
176 std::array<u8, 4> inst;
176}; 177};
177 178
178static std::map<u64, Breakpoint> breakpoints_execute; 179using BreakpointMap = std::map<VAddr, Breakpoint>;
179static std::map<u64, Breakpoint> breakpoints_read; 180BreakpointMap breakpoints_execute;
180static std::map<u64, Breakpoint> breakpoints_write; 181BreakpointMap breakpoints_read;
182BreakpointMap breakpoints_write;
181 183
182struct Module { 184struct Module {
183 std::string name; 185 std::string name;
184 PAddr beg; 186 VAddr beg;
185 PAddr end; 187 VAddr end;
186}; 188};
187 189
188static std::vector<Module> modules; 190std::vector<Module> modules;
191} // Anonymous namespace
189 192
190void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { 193void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
191 Module module; 194 Module module;
192 if (add_elf_ext) { 195 if (add_elf_ext) {
193 Common::SplitPath(name, nullptr, &module.name, nullptr); 196 Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -418,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
418} 421}
419 422
420/** 423/**
421 * Get the list of breakpoints for a given breakpoint type. 424 * Get the map of breakpoints for a given breakpoint type.
422 * 425 *
423 * @param type Type of breakpoint list. 426 * @param type Type of breakpoint map.
424 */ 427 */
425static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { 428static BreakpointMap& GetBreakpointMap(BreakpointType type) {
426 switch (type) { 429 switch (type) {
427 case BreakpointType::Execute: 430 case BreakpointType::Execute:
428 return breakpoints_execute; 431 return breakpoints_execute;
@@ -441,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
441 * @param type Type of breakpoint. 444 * @param type Type of breakpoint.
442 * @param addr Address of breakpoint. 445 * @param addr Address of breakpoint.
443 */ 446 */
444static void RemoveBreakpoint(BreakpointType type, PAddr addr) { 447static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
445 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 448 BreakpointMap& p = GetBreakpointMap(type);
446 449
447 auto bp = p.find(static_cast<u64>(addr)); 450 const auto bp = p.find(addr);
448 if (bp != p.end()) { 451 if (bp == p.end()) {
449 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", 452 return;
450 bp->second.len, bp->second.addr, static_cast<int>(type));
451 p.erase(static_cast<u64>(addr));
452 } 453 }
454
455 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
456 bp->second.len, bp->second.addr, static_cast<int>(type));
457 Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
458 Core::System::GetInstance().InvalidateCpuInstructionCaches();
459 p.erase(addr);
453} 460}
454 461
455BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { 462BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
456 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 463 const BreakpointMap& p = GetBreakpointMap(type);
457 auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); 464 const auto next_breakpoint = p.lower_bound(addr);
458 BreakpointAddress breakpoint; 465 BreakpointAddress breakpoint;
459 466
460 if (next_breakpoint != p.end()) { 467 if (next_breakpoint != p.end()) {
@@ -468,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type)
468 return breakpoint; 475 return breakpoint;
469} 476}
470 477
471bool CheckBreakpoint(PAddr addr, BreakpointType type) { 478bool CheckBreakpoint(VAddr addr, BreakpointType type) {
472 if (!IsConnected()) { 479 if (!IsConnected()) {
473 return false; 480 return false;
474 } 481 }
475 482
476 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 483 const BreakpointMap& p = GetBreakpointMap(type);
484 const auto bp = p.find(addr);
477 485
478 auto bp = p.find(static_cast<u64>(addr)); 486 if (bp == p.end()) {
479 if (bp != p.end()) { 487 return false;
480 u64 len = bp->second.len; 488 }
481 489
482 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints 490 u64 len = bp->second.len;
483 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
484 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
485 // two instructions instead of the single instruction you placed the breakpoint
486 // on. So, as a way to make sure that execution breakpoints are only breaking
487 // on the instruction that was specified, set the length of an execution
488 // breakpoint to 1. This should be fine since the CPU should never begin executing
489 // an instruction anywhere except the beginning of the instruction.
490 if (type == BreakpointType::Execute) {
491 len = 1;
492 }
493 491
494 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { 492 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
495 LOG_DEBUG(Debug_GDBStub, 493 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
496 "Found breakpoint type {} @ {:016X}, range: {:016X}" 494 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
497 " - {:016X} ({:X} bytes)", 495 // two instructions instead of the single instruction you placed the breakpoint
498 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); 496 // on. So, as a way to make sure that execution breakpoints are only breaking
499 return true; 497 // on the instruction that was specified, set the length of an execution
500 } 498 // breakpoint to 1. This should be fine since the CPU should never begin executing
499 // an instruction anywhere except the beginning of the instruction.
500 if (type == BreakpointType::Execute) {
501 len = 1;
502 }
503
504 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
505 LOG_DEBUG(Debug_GDBStub,
506 "Found breakpoint type {} @ {:016X}, range: {:016X}"
507 " - {:016X} ({:X} bytes)",
508 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
509 return true;
501 } 510 }
502 511
503 return false; 512 return false;
@@ -931,6 +940,7 @@ static void WriteMemory() {
931 940
932 GdbHexToMem(data.data(), len_pos + 1, len); 941 GdbHexToMem(data.data(), len_pos + 1, len);
933 Memory::WriteBlock(addr, data.data(), len); 942 Memory::WriteBlock(addr, data.data(), len);
943 Core::System::GetInstance().InvalidateCpuInstructionCaches();
934 SendReply("OK"); 944 SendReply("OK");
935} 945}
936 946
@@ -950,6 +960,7 @@ static void Step() {
950 step_loop = true; 960 step_loop = true;
951 halt_loop = true; 961 halt_loop = true;
952 send_trap = true; 962 send_trap = true;
963 Core::System::GetInstance().InvalidateCpuInstructionCaches();
953} 964}
954 965
955/// Tell the CPU if we hit a memory breakpoint. 966/// Tell the CPU if we hit a memory breakpoint.
@@ -966,6 +977,7 @@ static void Continue() {
966 memory_break = false; 977 memory_break = false;
967 step_loop = false; 978 step_loop = false;
968 halt_loop = false; 979 halt_loop = false;
980 Core::System::GetInstance().InvalidateCpuInstructionCaches();
969} 981}
970 982
971/** 983/**
@@ -975,13 +987,17 @@ static void Continue() {
975 * @param addr Address of breakpoint. 987 * @param addr Address of breakpoint.
976 * @param len Length of breakpoint. 988 * @param len Length of breakpoint.
977 */ 989 */
978static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { 990static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
979 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 991 BreakpointMap& p = GetBreakpointMap(type);
980 992
981 Breakpoint breakpoint; 993 Breakpoint breakpoint;
982 breakpoint.active = true; 994 breakpoint.active = true;
983 breakpoint.addr = addr; 995 breakpoint.addr = addr;
984 breakpoint.len = len; 996 breakpoint.len = len;
997 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
998 static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
999 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1000 Core::System::GetInstance().InvalidateCpuInstructionCaches();
985 p.insert({addr, breakpoint}); 1001 p.insert({addr, breakpoint});
986 1002
987 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", 1003 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1015,7 +1031,7 @@ static void AddBreakpoint() {
1015 1031
1016 auto start_offset = command_buffer + 3; 1032 auto start_offset = command_buffer + 3;
1017 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1033 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1018 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1034 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1019 1035
1020 start_offset = addr_pos + 1; 1036 start_offset = addr_pos + 1;
1021 u64 len = 1037 u64 len =
@@ -1064,7 +1080,7 @@ static void RemoveBreakpoint() {
1064 1080
1065 auto start_offset = command_buffer + 3; 1081 auto start_offset = command_buffer + 3;
1066 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1082 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1067 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1083 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1068 1084
1069 if (type == BreakpointType::Access) { 1085 if (type == BreakpointType::Access) {
1070 // Access is made up of Read and Write types, so add both breakpoints 1086 // Access is made up of Read and Write types, so add both breakpoints
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index a6b50c26c..5a36524b2 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -22,7 +22,7 @@ enum class BreakpointType {
22}; 22};
23 23
24struct BreakpointAddress { 24struct BreakpointAddress {
25 PAddr address; 25 VAddr address;
26 BreakpointType type; 26 BreakpointType type;
27}; 27};
28 28
@@ -53,7 +53,7 @@ bool IsServerEnabled();
53bool IsConnected(); 53bool IsConnected();
54 54
55/// Register module. 55/// Register module.
56void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); 56void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
57 57
58/** 58/**
59 * Signal to the gdbstub server that it should halt CPU execution. 59 * Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
74 * @param addr Address to search from. 74 * @param addr Address to search from.
75 * @param type Type of breakpoint. 75 * @param type Type of breakpoint.
76 */ 76 */
77BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); 77BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
78 78
79/** 79/**
80 * Check if a breakpoint of the specified type exists at the given address. 80 * Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
82 * @param addr Address of breakpoint. 82 * @param addr Address of breakpoint.
83 * @param type Type of breakpoint. 83 * @param type Type of breakpoint.
84 */ 84 */
85bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); 85bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
86 86
87/// If set to true, the CPU will halt at the beginning of the next CPU loop. 87/// If set to true, the CPU will halt at the beginning of the next CPU loop.
88bool GetCpuHaltFlag(); 88bool GetCpuHaltFlag();
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 7a17ed162..03a954a9f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -32,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
32} 32}
33 33
34// Gets the threads waiting on an address. 34// Gets the threads waiting on an address.
35static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, 35static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
36 VAddr address) { 36 const auto RetrieveWaitingThreads =
37 auto RetrieveWaitingThreads =
38 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { 37 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
39 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 38 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
40 auto& thread_list = scheduler->GetThreadList(); 39 auto& thread_list = scheduler->GetThreadList();
@@ -45,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t
45 } 44 }
46 }; 45 };
47 46
48 // Retrieve a list of all threads that are waiting for this address. 47 // Retrieve all threads that are waiting for this address.
49 RetrieveWaitingThreads(0, waiting_threads, address); 48 std::vector<SharedPtr<Thread>> threads;
50 RetrieveWaitingThreads(1, waiting_threads, address); 49 RetrieveWaitingThreads(0, threads, address);
51 RetrieveWaitingThreads(2, waiting_threads, address); 50 RetrieveWaitingThreads(1, threads, address);
52 RetrieveWaitingThreads(3, waiting_threads, address); 51 RetrieveWaitingThreads(2, threads, address);
52 RetrieveWaitingThreads(3, threads, address);
53
53 // Sort them by priority, such that the highest priority ones come first. 54 // Sort them by priority, such that the highest priority ones come first.
54 std::sort(waiting_threads.begin(), waiting_threads.end(), 55 std::sort(threads.begin(), threads.end(),
55 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { 56 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
56 return lhs->current_priority < rhs->current_priority; 57 return lhs->current_priority < rhs->current_priority;
57 }); 58 });
59
60 return threads;
58} 61}
59 62
60// Wake up num_to_wake (or all) threads in a vector. 63// Wake up num_to_wake (or all) threads in a vector.
@@ -76,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
76 79
77// Signals an address being waited on. 80// Signals an address being waited on.
78ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 81ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
79 // Get threads waiting on the address. 82 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
80 std::vector<SharedPtr<Thread>> waiting_threads;
81 GetThreadsWaitingOnAddress(waiting_threads, address);
82 83
83 WakeThreads(waiting_threads, num_to_wake); 84 WakeThreads(waiting_threads, num_to_wake);
84 return RESULT_SUCCESS; 85 return RESULT_SUCCESS;
@@ -110,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
110 } 111 }
111 112
112 // Get threads waiting on the address. 113 // Get threads waiting on the address.
113 std::vector<SharedPtr<Thread>> waiting_threads; 114 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
114 GetThreadsWaitingOnAddress(waiting_threads, address);
115 115
116 // Determine the modified value depending on the waiting count. 116 // Determine the modified value depending on the waiting count.
117 s32 updated_value; 117 s32 updated_value;
118 if (waiting_threads.size() == 0) { 118 if (waiting_threads.empty()) {
119 updated_value = value - 1; 119 updated_value = value - 1;
120 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { 120 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
121 updated_value = value + 1; 121 updated_value = value + 1;
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 7933c105c..134e41ebc 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -14,8 +14,8 @@
14 14
15namespace Kernel { 15namespace Kernel {
16 16
17ClientPort::ClientPort() {} 17ClientPort::ClientPort() = default;
18ClientPort::~ClientPort() {} 18ClientPort::~ClientPort() = default;
19 19
20ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { 20ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
21 // Note: Threads do not wait for the server endpoint to call 21 // Note: Threads do not wait for the server endpoint to call
@@ -40,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
40 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); 40 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
41} 41}
42 42
43void ClientPort::ConnectionClosed() {
44 if (active_sessions == 0) {
45 return;
46 }
47
48 --active_sessions;
49}
50
43} // namespace Kernel 51} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index b42c94bde..b1269ea5c 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -37,14 +37,20 @@ public:
37 */ 37 */
38 ResultVal<SharedPtr<ClientSession>> Connect(); 38 ResultVal<SharedPtr<ClientSession>> Connect();
39 39
40 SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. 40 /**
41 u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have 41 * Signifies that a previously active connection has been closed,
42 u32 active_sessions; ///< Number of currently open sessions to this port 42 * decreasing the total number of active connections to this port.
43 std::string name; ///< Name of client port (optional) 43 */
44 void ConnectionClosed();
44 45
45private: 46private:
46 ClientPort(); 47 ClientPort();
47 ~ClientPort() override; 48 ~ClientPort() override;
49
50 SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
51 u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
52 u32 active_sessions = 0; ///< Number of currently open sessions to this port
53 std::string name; ///< Name of client port (optional)
48}; 54};
49 55
50} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 1c99911b2..3c20c05e8 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -31,10 +31,9 @@ public:
31 return HANDLE_TYPE; 31 return HANDLE_TYPE;
32 } 32 }
33 33
34 ResetType reset_type; ///< Current ResetType 34 ResetType GetResetType() const {
35 35 return reset_type;
36 bool signaled; ///< Whether the event has already been signaled 36 }
37 std::string name; ///< Name of event (optional)
38 37
39 bool ShouldWait(Thread* thread) const override; 38 bool ShouldWait(Thread* thread) const override;
40 void Acquire(Thread* thread) override; 39 void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
47private: 46private:
48 Event(); 47 Event();
49 ~Event() override; 48 ~Event() override;
49
50 ResetType reset_type; ///< Current ResetType
51
52 bool signaled; ///< Whether the event has already been signaled
53 std::string name; ///< Name of event (optional)
50}; 54};
51 55
52} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 60370e9ec..93560152f 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
27 27
28 // Decrease the port's connection count. 28 // Decrease the port's connection count.
29 if (parent->port) 29 if (parent->port)
30 parent->port->active_sessions--; 30 parent->port->ConnectionClosed();
31 31
32 // TODO(Subv): Wake up all the ClientSession's waiting threads and set 32 // TODO(Subv): Wake up all the ClientSession's waiting threads and set
33 // the SendSyncRequest result to 0xC920181A. 33 // the SendSyncRequest result to 0xC920181A.
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 7a185c6c8..4109cb7f7 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
13 auto module_ = std::make_shared<Module>(); 13 auto module_ = std::make_shared<Module>();
14 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); 14 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
15 std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); 15 std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
16 std::make_shared<APM_Sys>()->InstallAsService(service_manager);
16} 17}
17 18
18} // namespace Service::APM 19} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index ce943d829..4cd8132f5 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 74 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
75 rb.Push(RESULT_SUCCESS); 75 rb.Push(RESULT_SUCCESS);
76 rb.PushIpcInterface<ISession>(); 76 rb.PushIpcInterface<ISession>();
77
78 LOG_DEBUG(Service_APM, "called");
79}
80
81APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
82 // clang-format off
83 static const FunctionInfo functions[] = {
84 {0, nullptr, "RequestPerformanceMode"},
85 {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"},
86 {2, nullptr, "GetThrottlingState"},
87 {3, nullptr, "GetLastThrottlingState"},
88 {4, nullptr, "ClearLastThrottlingState"},
89 {5, nullptr, "LoadAndApplySettings"},
90 };
91 // clang-format on
92
93 RegisterHandlers(functions);
94}
95
96void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
97 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
98 rb.Push(RESULT_SUCCESS);
99 rb.PushIpcInterface<ISession>();
100
101 LOG_DEBUG(Service_APM, "called");
77} 102}
78 103
79} // namespace Service::APM 104} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index fa68c7d93..d14264ad7 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -19,4 +19,12 @@ private:
19 std::shared_ptr<Module> apm; 19 std::shared_ptr<Module> apm;
20}; 20};
21 21
22class APM_Sys final : public ServiceFramework<APM_Sys> {
23public:
24 explicit APM_Sys();
25
26private:
27 void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
28};
29
22} // namespace Service::APM 30} // namespace Service::APM
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
new file mode 100644
index 000000000..358ef2576
--- /dev/null
+++ b/src/core/hle/service/arp/arp.cpp
@@ -0,0 +1,75 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/arp/arp.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::ARP {
15
16class ARP_R final : public ServiceFramework<ARP_R> {
17public:
18 explicit ARP_R() : ServiceFramework{"arp:r"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "GetApplicationLaunchProperty"},
22 {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
23 {2, nullptr, "GetApplicationControlProperty"},
24 {3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
25 };
26 // clang-format on
27
28 RegisterHandlers(functions);
29 }
30};
31
32class IRegistrar final : public ServiceFramework<IRegistrar> {
33public:
34 explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
35 // clang-format off
36 static const FunctionInfo functions[] = {
37 {0, nullptr, "Issue"},
38 {1, nullptr, "SetApplicationLaunchProperty"},
39 {2, nullptr, "SetApplicationControlProperty"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44 }
45};
46
47class ARP_W final : public ServiceFramework<ARP_W> {
48public:
49 explicit ARP_W() : ServiceFramework{"arp:w"} {
50 // clang-format off
51 static const FunctionInfo functions[] = {
52 {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
53 {1, nullptr, "DeleteProperties"},
54 };
55 // clang-format on
56
57 RegisterHandlers(functions);
58 }
59
60private:
61 void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
62 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
63 rb.Push(RESULT_SUCCESS);
64 rb.PushIpcInterface<IRegistrar>();
65
66 LOG_DEBUG(Service_ARP, "called");
67 }
68};
69
70void InstallInterfaces(SM::ServiceManager& sm) {
71 std::make_shared<ARP_R>()->InstallAsService(sm);
72 std::make_shared<ARP_W>()->InstallAsService(sm);
73}
74
75} // namespace Service::ARP
diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h
new file mode 100644
index 000000000..9d100187c
--- /dev/null
+++ b/src/core/hle/service/arp/arp.h
@@ -0,0 +1,16 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::ARP {
12
13/// Registers all ARP services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm);
15
16} // namespace Service::ARP
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
index e62a27945..a70d5bca4 100644
--- a/src/core/hle/service/audio/audin_a.cpp
+++ b/src/core/hle/service/audio/audin_a.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/audio/audin_a.h" 5#include "core/hle/service/audio/audin_a.h"
8 6
9namespace Service::Audio { 7namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
index 57b934dd6..bf8d40157 100644
--- a/src/core/hle/service/audio/audout_a.cpp
+++ b/src/core/hle/service/audio/audout_a.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/audio/audout_a.h" 5#include "core/hle/service/audio/audout_a.h"
8 6
9namespace Service::Audio { 7namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index b317027b6..108a7c6eb 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -4,6 +4,8 @@
4 4
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7
8#include "audio_core/codec.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
@@ -48,7 +50,7 @@ public:
48 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); 50 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
49 51
50 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, 52 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
51 [=]() { buffer_event->Signal(); }); 53 "IAudioOut", [=]() { buffer_event->Signal(); });
52 } 54 }
53 55
54private: 56private:
@@ -111,10 +113,10 @@ private:
111 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer)); 113 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
112 const u64 tag{rp.Pop<u64>()}; 114 const u64 tag{rp.Pop<u64>()};
113 115
114 std::vector<u8> data(audio_buffer.buffer_size); 116 std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
115 Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size()); 117 Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
116 118
117 if (!audio_core.QueueBuffer(stream, tag, std::move(data))) { 119 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
118 IPC::ResponseBuilder rb{ctx, 2}; 120 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); 121 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
120 } 122 }
@@ -200,7 +202,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
200 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
201 rb.Push<u32>(DefaultSampleRate); 203 rb.Push<u32>(DefaultSampleRate);
202 rb.Push<u32>(params.channel_count); 204 rb.Push<u32>(params.channel_count);
203 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 205 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
204 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 206 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
205 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 207 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
206} 208}
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index e5c2184d5..fd491f65d 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -38,16 +38,6 @@ private:
38 38
39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
41
42 enum class PcmFormat : u32 {
43 Invalid = 0,
44 Int8 = 1,
45 Int16 = 2,
46 Int24 = 3,
47 Int32 = 4,
48 PcmFloat = 5,
49 Adpcm = 6,
50 };
51}; 41};
52 42
53} // namespace Service::Audio 43} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp
index 9c32f9b98..016eabf53 100644
--- a/src/core/hle/service/audio/audrec_a.cpp
+++ b/src/core/hle/service/audio/audrec_a.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/audio/audrec_a.h" 5#include "core/hle/service/audio/audrec_a.h"
8 6
9namespace Service::Audio { 7namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
index bc9930d79..616ff3dc4 100644
--- a/src/core/hle/service/audio/audren_a.cpp
+++ b/src/core/hle/service/audio/audren_a.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/audio/audren_a.h" 5#include "core/hle/service/audio/audren_a.h"
8 6
9namespace Service::Audio { 7namespace Service::Audio {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6aed9e2fa..f99304de5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -15,13 +15,10 @@
15 15
16namespace Service::Audio { 16namespace Service::Audio {
17 17
18/// TODO(bunnei): Find a proper value for the audio_ticks
19constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
20
21class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 18class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 19public:
23 explicit IAudioRenderer(AudioRendererParameter audren_params) 20 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { 21 : ServiceFramework("IAudioRenderer") {
25 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
26 {0, nullptr, "GetAudioRendererSampleRate"}, 23 {0, nullptr, "GetAudioRendererSampleRate"},
27 {1, nullptr, "GetAudioRendererSampleCount"}, 24 {1, nullptr, "GetAudioRendererSampleCount"},
@@ -39,21 +36,8 @@ public:
39 RegisterHandlers(functions); 36 RegisterHandlers(functions);
40 37
41 system_event = 38 system_event =
42 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); 39 Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
43 40 renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
44 // Register event callback to update the Audio Buffer
45 audio_event = CoreTiming::RegisterEvent(
46 "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
47 UpdateAudioCallback();
48 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
49 });
50
51 // Start the audio event
52 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
53 voice_status_list.resize(worker_params.voice_count);
54 }
55 ~IAudioRenderer() {
56 CoreTiming::UnscheduleEvent(audio_event, 0);
57 } 41 }
58 42
59private: 43private:
@@ -62,60 +46,9 @@ private:
62 } 46 }
63 47
64 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 48 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
65 UpdateDataHeader config{}; 49 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
66 auto buf = ctx.ReadBuffer();
67 std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
68 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
69
70 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
71 std::memcpy(mem_pool_info.data(),
72 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
73 memory_pool_count * sizeof(MemoryPoolInfo));
74
75 std::vector<VoiceInfo> voice_info(worker_params.voice_count);
76 std::memcpy(voice_info.data(),
77 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
78 config.memory_pools_size + config.voice_resource_size,
79 worker_params.voice_count * sizeof(VoiceInfo));
80
81 UpdateDataHeader response_data{worker_params};
82
83 ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
84
85 std::vector<u8> output(response_data.total_size);
86 std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
87 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
88 for (unsigned i = 0; i < memory_pool.size(); i++) {
89 if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
90 memory_pool[i].state = MemoryPoolStates::Attached;
91 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
92 memory_pool[i].state = MemoryPoolStates::Detached;
93 }
94 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
95 response_data.memory_pools_size);
96
97 for (unsigned i = 0; i < voice_info.size(); i++) {
98 if (voice_info[i].is_new) {
99 voice_status_list[i].played_sample_count = 0;
100 voice_status_list[i].wave_buffer_consumed = 0;
101 } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
102 for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
103 voice_status_list[i].played_sample_count +=
104 (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
105 voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
106 2;
107 voice_status_list[i].wave_buffer_consumed++;
108 }
109 }
110 }
111 std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
112 voice_status_list.data(), response_data.voices_size);
113
114 ctx.WriteBuffer(output);
115
116 IPC::ResponseBuilder rb{ctx, 2}; 50 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(RESULT_SUCCESS); 51 rb.Push(RESULT_SUCCESS);
118
119 LOG_WARNING(Service_Audio, "(STUBBED) called"); 52 LOG_WARNING(Service_Audio, "(STUBBED) called");
120 } 53 }
121 54
@@ -136,8 +69,6 @@ private:
136 } 69 }
137 70
138 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 71 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
139 // system_event->Signal();
140
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 72 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 73 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(system_event); 74 rb.PushCopyObjects(system_event);
@@ -145,131 +76,8 @@ private:
145 LOG_WARNING(Service_Audio, "(STUBBED) called"); 76 LOG_WARNING(Service_Audio, "(STUBBED) called");
146 } 77 }
147 78
148 enum class MemoryPoolStates : u32 { // Should be LE
149 Invalid = 0x0,
150 Unknown = 0x1,
151 RequestDetach = 0x2,
152 Detached = 0x3,
153 RequestAttach = 0x4,
154 Attached = 0x5,
155 Released = 0x6,
156 };
157
158 enum class PlayStates : u8 {
159 Started = 0,
160 Stopped = 1,
161 };
162
163 struct MemoryPoolEntry {
164 MemoryPoolStates state;
165 u32_le unknown_4;
166 u32_le unknown_8;
167 u32_le unknown_c;
168 };
169 static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
170
171 struct MemoryPoolInfo {
172 u64_le pool_address;
173 u64_le pool_size;
174 MemoryPoolStates pool_state;
175 INSERT_PADDING_WORDS(3); // Unknown
176 };
177 static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
178
179 struct UpdateDataHeader {
180 UpdateDataHeader() {}
181
182 explicit UpdateDataHeader(const AudioRendererParameter& config) {
183 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
184 behavior_size = 0xb0;
185 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
186 voices_size = config.voice_count * 0x10;
187 voice_resource_size = 0x0;
188 effects_size = config.effect_count * 0x10;
189 mixes_size = 0x0;
190 sinks_size = config.sink_count * 0x20;
191 performance_manager_size = 0x10;
192 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
193 voices_size + effects_size + sinks_size + performance_manager_size;
194 }
195
196 u32_le revision;
197 u32_le behavior_size;
198 u32_le memory_pools_size;
199 u32_le voices_size;
200 u32_le voice_resource_size;
201 u32_le effects_size;
202 u32_le mixes_size;
203 u32_le sinks_size;
204 u32_le performance_manager_size;
205 INSERT_PADDING_WORDS(6);
206 u32_le total_size;
207 };
208 static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
209
210 struct BiquadFilter {
211 u8 enable;
212 INSERT_PADDING_BYTES(1);
213 s16_le numerator[3];
214 s16_le denominator[2];
215 };
216 static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
217
218 struct WaveBuffer {
219 u64_le buffer_addr;
220 u64_le buffer_sz;
221 s32_le start_sample_offset;
222 s32_le end_sample_offset;
223 u8 loop;
224 u8 end_of_stream;
225 u8 sent_to_server;
226 INSERT_PADDING_BYTES(5);
227 u64 context_addr;
228 u64 context_sz;
229 INSERT_PADDING_BYTES(8);
230 };
231 static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
232
233 struct VoiceInfo {
234 u32_le id;
235 u32_le node_id;
236 u8 is_new;
237 u8 is_in_use;
238 u8 play_state;
239 u8 sample_format;
240 u32_le sample_rate;
241 u32_le priority;
242 u32_le sorting_order;
243 u32_le channel_count;
244 float_le pitch;
245 float_le volume;
246 BiquadFilter biquad_filter[2];
247 u32_le wave_buffer_count;
248 u16_le wave_buffer_head;
249 INSERT_PADDING_BYTES(6);
250 u64_le additional_params_addr;
251 u64_le additional_params_sz;
252 u32_le mix_id;
253 u32_le splitter_info_id;
254 WaveBuffer wave_buffer[4];
255 u32_le voice_channel_resource_ids[6];
256 INSERT_PADDING_BYTES(24);
257 };
258 static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
259
260 struct VoiceOutStatus {
261 u64_le played_sample_count;
262 u32_le wave_buffer_consumed;
263 INSERT_PADDING_WORDS(1);
264 };
265 static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
266
267 /// This is used to trigger the audio event callback.
268 CoreTiming::EventType* audio_event;
269
270 Kernel::SharedPtr<Kernel::Event> system_event; 79 Kernel::SharedPtr<Kernel::Event> system_event;
271 AudioRendererParameter worker_params; 80 std::unique_ptr<AudioCore::AudioRenderer> renderer;
272 std::vector<VoiceOutStatus> voice_status_list;
273}; 81};
274 82
275class IAudioDevice final : public ServiceFramework<IAudioDevice> { 83class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
368 176
369void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 177void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
370 IPC::RequestParser rp{ctx}; 178 IPC::RequestParser rp{ctx};
371 auto params = rp.PopRaw<AudioRendererParameter>(); 179 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
372 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 180 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
373 181
374 rb.Push(RESULT_SUCCESS); 182 rb.Push(RESULT_SUCCESS);
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
379 187
380void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 188void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
381 IPC::RequestParser rp{ctx}; 189 IPC::RequestParser rp{ctx};
382 auto params = rp.PopRaw<AudioRendererParameter>(); 190 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
383 191
384 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); 192 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
385 buffer_sz += params.unknown_c * 1024; 193 buffer_sz += params.unknown_c * 1024;
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index b9b81db4f..14907f8ae 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_renderer.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -12,24 +13,6 @@ class HLERequestContext;
12 13
13namespace Service::Audio { 14namespace Service::Audio {
14 15
15struct AudioRendererParameter {
16 u32_le sample_rate;
17 u32_le sample_count;
18 u32_le unknown_8;
19 u32_le unknown_c;
20 u32_le voice_count;
21 u32_le sink_count;
22 u32_le effect_count;
23 u32_le unknown_1c;
24 u8 unknown_20;
25 INSERT_PADDING_BYTES(3);
26 u32_le splitter_count;
27 u32_le unknown_2c;
28 INSERT_PADDING_WORDS(1);
29 u32_le revision;
30};
31static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
32
33class AudRenU final : public ServiceFramework<AudRenU> { 16class AudRenU final : public ServiceFramework<AudRenU> {
34public: 17public:
35 explicit AudRenU(); 18 explicit AudRenU();
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp
index ee6d4d055..0ab9c2606 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.cpp
+++ b/src/core/hle/service/filesystem/fsp_ldr.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/filesystem/fsp_ldr.h" 5#include "core/hle/service/filesystem/fsp_ldr.h"
8#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
9 7
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp
index 0b51385ee..32b0ae454 100644
--- a/src/core/hle/service/filesystem/fsp_pr.cpp
+++ b/src/core/hle/service/filesystem/fsp_pr.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "core/hle/service/filesystem/fsp_pr.h" 5#include "core/hle/service/filesystem/fsp_pr.h"
8#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
9 7
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index ed53f96c5..8f0262e34 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -337,6 +337,7 @@ public:
337 "AcquireNpadStyleSetUpdateEventHandle"}, 337 "AcquireNpadStyleSetUpdateEventHandle"},
338 {107, nullptr, "DisconnectNpad"}, 338 {107, nullptr, "DisconnectNpad"},
339 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, 339 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
340 {109, nullptr, "ActivateNpadWithRevision"},
340 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, 341 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
341 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, 342 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
342 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, 343 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ed69a4325..8bc49935a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -7,8 +7,8 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h" 9#include "core/hle/service/nvdrv/devices/nvmap.h"
10#include "video_core/gpu.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11#include "video_core/video_core.h"
12 12
13namespace Service::Nvidia::Devices { 13namespace Service::Nvidia::Devices {
14 14
@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
30 addr, offset, width, height, stride, static_cast<PixelFormat>(format), 30 addr, offset, width, height, stride, static_cast<PixelFormat>(format),
31 transform, crop_rect}; 31 transform, crop_rect};
32 32
33 Core::System::GetInstance().perf_stats.EndGameFrame(); 33 auto& instance = Core::System::GetInstance();
34 34 instance.perf_stats.EndGameFrame();
35 VideoCore::g_renderer->SwapBuffers(framebuffer); 35 instance.Renderer().SwapBuffers(framebuffer);
36} 36}
37 37
38} // namespace Service::Nvidia::Devices 38} // namespace Service::Nvidia::Devices
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 57b128b40..be2b79256 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -2,14 +2,15 @@
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 <cinttypes> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
10#include "core/hle/service/nvdrv/devices/nvmap.h" 10#include "core/hle/service/nvdrv/devices/nvmap.h"
11#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h"
11#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
12#include "video_core/video_core.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -150,15 +151,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
150 151
151 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 152 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
152 153
153 auto& gpu = Core::System::GetInstance().GPU(); 154 const auto itr = buffer_mappings.find(params.offset);
154
155 auto itr = buffer_mappings.find(params.offset);
156
157 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); 155 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
158 156
157 auto& system_instance = Core::System::GetInstance();
158
159 // Remove this memory region from the rasterizer cache. 159 // Remove this memory region from the rasterizer cache.
160 VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size); 160 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
161 itr->second.size);
161 162
163 auto& gpu = system_instance.GPU();
162 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); 164 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
163 165
164 buffer_mappings.erase(itr->second.offset); 166 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 671b092e1..5685eb2be 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -2,6 +2,9 @@
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 <cstdlib>
6#include <cstring>
7
5#include "common/assert.h" 8#include "common/assert.h"
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 090261a60..6b496e9fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstdlib>
9#include <cstring>
10#include <vector> 8#include <vector>
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
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 010072a5b..ae421247d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -2,7 +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 <cinttypes> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 5a1123ad2..116dabedb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -2,12 +2,14 @@
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 <cinttypes> 5#include <cstring>
6#include <map>
7#include "common/assert.h" 6#include "common/assert.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9#include "core/core.h" 8#include "core/core.h"
10#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/memory.h"
11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h"
11 13
12namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
13 15
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index aa8df2e6e..650ed8fbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/bit_field.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b51c73ee8..364619e67 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -2,6 +2,8 @@
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 <cstring>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0192aecdd..6ad74421b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <cstdlib>
9#include <cstring>
10#include <vector> 7#include <vector>
11#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
13 11
14namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 724eeb139..e9305bfb3 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cstring>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 959b5ba29..1c3529bb6 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
9#include "core/hle/kernel/event.h" 8#include "core/hle/kernel/event.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 9#include "core/hle/service/nvdrv/nvdrv.h"
11#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 1555ea806..e8b30921a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -54,7 +54,7 @@ u32 Module::Open(const std::string& device_name) {
54 return fd; 54 return fd;
55} 55}
56 56
57u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { 57u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
58 auto itr = open_files.find(fd); 58 auto itr = open_files.find(fd);
59 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 59 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
60 60
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index 9ca6e5512..0e8e21bad 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -4,8 +4,6 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/nvdrv/nvdrv.h"
9#include "core/hle/service/nvdrv/nvmemp.h" 7#include "core/hle/service/nvdrv/nvmemp.h"
10 8
11namespace Service::Nvidia { 9namespace Service::Nvidia {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5344441e1..570aa8493 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,8 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <boost/optional.hpp>
6 7
7#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h"
10#include "common/logging/log.h"
8#include "common/microprofile.h" 11#include "common/microprofile.h"
9#include "common/scope_exit.h" 12#include "common/scope_exit.h"
10#include "core/core.h" 13#include "core/core.h"
@@ -31,7 +34,7 @@ NVFlinger::NVFlinger() {
31 34
32 // Schedule the screen composition events 35 // Schedule the screen composition events
33 composition_event = 36 composition_event =
34 CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { 37 CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
35 Compose(); 38 Compose();
36 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); 39 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
37 }); 40 });
@@ -43,7 +46,7 @@ NVFlinger::~NVFlinger() {
43 CoreTiming::UnscheduleEvent(composition_event, 0); 46 CoreTiming::UnscheduleEvent(composition_event, 0);
44} 47}
45 48
46u64 NVFlinger::OpenDisplay(const std::string& name) { 49u64 NVFlinger::OpenDisplay(std::string_view name) {
47 LOG_WARNING(Service, "Opening display {}", name); 50 LOG_WARNING(Service, "Opening display {}", name);
48 51
49 // TODO(Subv): Currently we only support the Default display. 52 // TODO(Subv): Currently we only support the Default display.
@@ -127,9 +130,11 @@ void NVFlinger::Compose() {
127 MicroProfileFlip(); 130 MicroProfileFlip();
128 131
129 if (buffer == boost::none) { 132 if (buffer == boost::none) {
133 auto& system_instance = Core::System::GetInstance();
134
130 // There was no queued buffer to draw, render previous frame 135 // There was no queued buffer to draw, render previous frame
131 Core::System::GetInstance().perf_stats.EndGameFrame(); 136 system_instance.perf_stats.EndGameFrame();
132 VideoCore::g_renderer->SwapBuffers({}); 137 system_instance.Renderer().SwapBuffers({});
133 continue; 138 continue;
134 } 139 }
135 140
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 2c908297b..5374df175 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -5,7 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <boost/optional.hpp> 8#include <string>
9#include <string_view>
10#include <vector>
11
12#include "common/common_types.h"
9#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
10 14
11namespace CoreTiming { 15namespace CoreTiming {
@@ -41,7 +45,7 @@ public:
41 ~NVFlinger(); 45 ~NVFlinger();
42 46
43 /// Opens the specified display and returns the id. 47 /// Opens the specified display and returns the id.
44 u64 OpenDisplay(const std::string& name); 48 u64 OpenDisplay(std::string_view name);
45 49
46 /// Creates a layer on the specified display and returns the layer id. 50 /// Creates a layer on the specified display and returns the layer id.
47 u64 CreateLayer(u64 display_id); 51 u64 CreateLayer(u64 display_id);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 61e0c34a0..889cdd41a 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -19,6 +19,7 @@
19#include "core/hle/service/am/am.h" 19#include "core/hle/service/am/am.h"
20#include "core/hle/service/aoc/aoc_u.h" 20#include "core/hle/service/aoc/aoc_u.h"
21#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
22#include "core/hle/service/arp/arp.h"
22#include "core/hle/service/audio/audio.h" 23#include "core/hle/service/audio/audio.h"
23#include "core/hle/service/bcat/bcat.h" 24#include "core/hle/service/bcat/bcat.h"
24#include "core/hle/service/bpc/bpc.h" 25#include "core/hle/service/bpc/bpc.h"
@@ -62,6 +63,7 @@
62#include "core/hle/service/spl/module.h" 63#include "core/hle/service/spl/module.h"
63#include "core/hle/service/ssl/ssl.h" 64#include "core/hle/service/ssl/ssl.h"
64#include "core/hle/service/time/time.h" 65#include "core/hle/service/time/time.h"
66#include "core/hle/service/usb/usb.h"
65#include "core/hle/service/vi/vi.h" 67#include "core/hle/service/vi/vi.h"
66#include "core/hle/service/wlan/wlan.h" 68#include "core/hle/service/wlan/wlan.h"
67 69
@@ -207,6 +209,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
207 AM::InstallInterfaces(*sm, nv_flinger); 209 AM::InstallInterfaces(*sm, nv_flinger);
208 AOC::InstallInterfaces(*sm); 210 AOC::InstallInterfaces(*sm);
209 APM::InstallInterfaces(*sm); 211 APM::InstallInterfaces(*sm);
212 ARP::InstallInterfaces(*sm);
210 Audio::InstallInterfaces(*sm); 213 Audio::InstallInterfaces(*sm);
211 BCAT::InstallInterfaces(*sm); 214 BCAT::InstallInterfaces(*sm);
212 BPC::InstallInterfaces(*sm); 215 BPC::InstallInterfaces(*sm);
@@ -247,6 +250,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
247 SPL::InstallInterfaces(*sm); 250 SPL::InstallInterfaces(*sm);
248 SSL::InstallInterfaces(*sm); 251 SSL::InstallInterfaces(*sm);
249 Time::InstallInterfaces(*sm); 252 Time::InstallInterfaces(*sm);
253 USB::InstallInterfaces(*sm);
250 VI::InstallInterfaces(*sm, nv_flinger); 254 VI::InstallInterfaces(*sm, nv_flinger);
251 WLAN::InstallInterfaces(*sm); 255 WLAN::InstallInterfaces(*sm);
252 256
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 37b58bb77..2172c681b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -80,8 +80,8 @@ public:
80 {5, nullptr, "GetTimeZoneRuleVersion"}, 80 {5, nullptr, "GetTimeZoneRuleVersion"},
81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
83 {200, nullptr, "ToPosixTime"}, 83 {201, nullptr, "ToPosixTime"},
84 {201, nullptr, "ToPosixTimeWithMyRule"}, 84 {202, nullptr, "ToPosixTimeWithMyRule"},
85 }; 85 };
86 RegisterHandlers(functions); 86 RegisterHandlers(functions);
87 } 87 }
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
new file mode 100644
index 000000000..e7fb5a419
--- /dev/null
+++ b/src/core/hle/service/usb/usb.cpp
@@ -0,0 +1,238 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12#include "core/hle/service/usb/usb.h"
13
14namespace Service::USB {
15
16class IDsInterface final : public ServiceFramework<IDsInterface> {
17public:
18 explicit IDsInterface() : ServiceFramework{"IDsInterface"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "GetDsEndpoint"},
22 {1, nullptr, "GetSetupEvent"},
23 {2, nullptr, "Unknown"},
24 {3, nullptr, "EnableInterface"},
25 {4, nullptr, "DisableInterface"},
26 {5, nullptr, "CtrlInPostBufferAsync"},
27 {6, nullptr, "CtrlOutPostBufferAsync"},
28 {7, nullptr, "GetCtrlInCompletionEvent"},
29 {8, nullptr, "GetCtrlInReportData"},
30 {9, nullptr, "GetCtrlOutCompletionEvent"},
31 {10, nullptr, "GetCtrlOutReportData"},
32 {11, nullptr, "StallCtrl"},
33 {12, nullptr, "AppendConfigurationData"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38 }
39};
40
41class USB_DS final : public ServiceFramework<USB_DS> {
42public:
43 explicit USB_DS() : ServiceFramework{"usb:ds"} {
44 // clang-format off
45 static const FunctionInfo functions[] = {
46 {0, nullptr, "BindDevice"},
47 {1, nullptr, "BindClientProcess"},
48 {2, nullptr, "GetDsInterface"},
49 {3, nullptr, "GetStateChangeEvent"},
50 {4, nullptr, "GetState"},
51 {5, nullptr, "ClearDeviceData"},
52 {6, nullptr, "AddUsbStringDescriptor"},
53 {7, nullptr, "DeleteUsbStringDescriptor"},
54 {8, nullptr, "SetUsbDeviceDescriptor"},
55 {9, nullptr, "SetBinaryObjectStore"},
56 {10, nullptr, "Enable"},
57 {11, nullptr, "Disable"},
58 };
59 // clang-format on
60
61 RegisterHandlers(functions);
62 }
63};
64
65class IClientEpSession final : public ServiceFramework<IClientEpSession> {
66public:
67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
68 // clang-format off
69 static const FunctionInfo functions[] = {
70 {0, nullptr, "Unknown1"},
71 {1, nullptr, "Unknown2"},
72 {2, nullptr, "Unknown3"},
73 {3, nullptr, "Unknown4"},
74 {4, nullptr, "PostBufferAsync"},
75 {5, nullptr, "Unknown5"},
76 {6, nullptr, "Unknown6"},
77 {7, nullptr, "Unknown7"},
78 {8, nullptr, "Unknown8"},
79 };
80 // clang-format on
81
82 RegisterHandlers(functions);
83 }
84};
85
86class IClientIfSession final : public ServiceFramework<IClientIfSession> {
87public:
88 explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
89 // clang-format off
90 static const FunctionInfo functions[] = {
91 {0, nullptr, "Unknown1"},
92 {1, nullptr, "Unknown2"},
93 {2, nullptr, "Unknown3"},
94 {3, nullptr, "Unknown4"},
95 {4, nullptr, "Unknown5"},
96 {5, nullptr, "CtrlXferAsync"},
97 {6, nullptr, "Unknown6"},
98 {7, nullptr, "GetCtrlXferReport"},
99 {8, nullptr, "Unknown7"},
100 {9, nullptr, "GetClientEpSession"},
101 };
102 // clang-format on
103
104 RegisterHandlers(functions);
105 }
106};
107
108class USB_HS final : public ServiceFramework<USB_HS> {
109public:
110 explicit USB_HS() : ServiceFramework{"usb:hs"} {
111 // clang-format off
112 static const FunctionInfo functions[] = {
113 {0, nullptr, "BindClientProcess"},
114 {1, nullptr, "Unknown1"},
115 {2, nullptr, "Unknown2"},
116 {3, nullptr, "Unknown3"},
117 {4, nullptr, "Unknown4"},
118 {5, nullptr, "Unknown5"},
119 {6, nullptr, "GetInterfaceStateChangeEvent"},
120 {7, nullptr, "GetClientIfSession"},
121 };
122 // clang-format on
123
124 RegisterHandlers(functions);
125 }
126};
127
128class IPdSession final : public ServiceFramework<IPdSession> {
129public:
130 explicit IPdSession() : ServiceFramework{"IPdSession"} {
131 // clang-format off
132 static const FunctionInfo functions[] = {
133 {0, nullptr, "BindNoticeEvent"},
134 {1, nullptr, "Unknown1"},
135 {2, nullptr, "GetStatus"},
136 {3, nullptr, "GetNotice"},
137 {4, nullptr, "Unknown2"},
138 {5, nullptr, "Unknown3"},
139 {6, nullptr, "ReplyPowerRequest"},
140 };
141 // clang-format on
142
143 RegisterHandlers(functions);
144 }
145};
146
147class USB_PD final : public ServiceFramework<USB_PD> {
148public:
149 explicit USB_PD() : ServiceFramework{"usb:pd"} {
150 // clang-format off
151 static const FunctionInfo functions[] = {
152 {0, &USB_PD::GetPdSession, "GetPdSession"},
153 };
154 // clang-format on
155
156 RegisterHandlers(functions);
157 }
158
159private:
160 void GetPdSession(Kernel::HLERequestContext& ctx) {
161 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
162 rb.Push(RESULT_SUCCESS);
163 rb.PushIpcInterface<IPdSession>();
164
165 LOG_DEBUG(Service_USB, "called");
166 }
167};
168
169class IPdCradleSession final : public ServiceFramework<IPdCradleSession> {
170public:
171 explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} {
172 // clang-format off
173 static const FunctionInfo functions[] = {
174 {0, nullptr, "VdmUserWrite"},
175 {1, nullptr, "VdmUserRead"},
176 {2, nullptr, "Vdm20Init"},
177 {3, nullptr, "GetFwType"},
178 {4, nullptr, "GetFwRevision"},
179 {5, nullptr, "GetManufacturerId"},
180 {6, nullptr, "GetDeviceId"},
181 {7, nullptr, "Unknown1"},
182 {8, nullptr, "Unknown2"},
183 };
184 // clang-format on
185
186 RegisterHandlers(functions);
187 }
188};
189
190class USB_PD_C final : public ServiceFramework<USB_PD_C> {
191public:
192 explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} {
193 // clang-format off
194 static const FunctionInfo functions[] = {
195 {0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"},
196 };
197 // clang-format on
198
199 RegisterHandlers(functions);
200 }
201
202private:
203 void GetPdCradleSession(Kernel::HLERequestContext& ctx) {
204 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
205 rb.Push(RESULT_SUCCESS);
206 rb.PushIpcInterface<IPdCradleSession>();
207
208 LOG_DEBUG(Service_USB, "called");
209 }
210};
211
212class USB_PM final : public ServiceFramework<USB_PM> {
213public:
214 explicit USB_PM() : ServiceFramework{"usb:pm"} {
215 // clang-format off
216 static const FunctionInfo functions[] = {
217 {0, nullptr, "Unknown1"},
218 {1, nullptr, "Unknown2"},
219 {2, nullptr, "Unknown3"},
220 {3, nullptr, "Unknown4"},
221 {4, nullptr, "Unknown5"},
222 {5, nullptr, "Unknown6"},
223 };
224 // clang-format on
225
226 RegisterHandlers(functions);
227 }
228};
229
230void InstallInterfaces(SM::ServiceManager& sm) {
231 std::make_shared<USB_DS>()->InstallAsService(sm);
232 std::make_shared<USB_HS>()->InstallAsService(sm);
233 std::make_shared<USB_PD>()->InstallAsService(sm);
234 std::make_shared<USB_PD_C>()->InstallAsService(sm);
235 std::make_shared<USB_PM>()->InstallAsService(sm);
236}
237
238} // namespace Service::USB
diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h
new file mode 100644
index 000000000..970a11fe8
--- /dev/null
+++ b/src/core/hle/service/usb/usb.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::USB {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::USB
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index b0277a875..9a8cdd0ff 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -20,6 +20,10 @@ namespace Loader {
20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) 20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
21 : AppLoader(std::move(file)) {} 21 : AppLoader(std::move(file)) {}
22 22
23AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
24 FileSys::VirtualDir directory)
25 : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
26
23FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { 27FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
24 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { 28 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
25 return FileType::DeconstructedRomDirectory; 29 return FileType::DeconstructedRomDirectory;
@@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
34 return ResultStatus::ErrorAlreadyLoaded; 38 return ResultStatus::ErrorAlreadyLoaded;
35 } 39 }
36 40
37 const FileSys::VirtualDir dir = file->GetContainingDirectory(); 41 if (dir == nullptr) {
42 if (file == nullptr)
43 return ResultStatus::ErrorInvalidFormat;
44 dir = file->GetContainingDirectory();
45 }
46
38 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); 47 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
39 if (npdm == nullptr) 48 if (npdm == nullptr)
40 return ResultStatus::ErrorInvalidFormat; 49 return ResultStatus::ErrorInvalidFormat;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 7319ba6ea..7d5433563 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader {
22public: 22public:
23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); 23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
24 24
25 // Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
26 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
27
25 /** 28 /**
26 * Returns the type of the file 29 * Returns the type of the file
27 * @param file std::shared_ptr<VfsFile> open file 30 * @param file std::shared_ptr<VfsFile> open file
@@ -40,6 +43,7 @@ public:
40private: 43private:
41 FileSys::ProgramMetadata metadata; 44 FileSys::ProgramMetadata metadata;
42 FileSys::VirtualFile romfs; 45 FileSys::VirtualFile romfs;
46 FileSys::VirtualDir dir;
43}; 47};
44 48
45} // namespace Loader 49} // namespace Loader
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index a1f8235d1..dbc67c0b5 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -22,7 +22,8 @@
22 22
23namespace Loader { 23namespace Loader {
24 24
25AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} 25AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
26 : AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
26 27
27FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { 28FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
28 FileSys::NCA nca(file); 29 FileSys::NCA nca(file);
@@ -39,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
39 return ResultStatus::ErrorAlreadyLoaded; 40 return ResultStatus::ErrorAlreadyLoaded;
40 } 41 }
41 42
42 nca = std::make_unique<FileSys::NCA>(file); 43 const auto result = nca->GetStatus();
43 ResultStatus result = nca->GetStatus();
44 if (result != ResultStatus::Success) { 44 if (result != ResultStatus::Success) {
45 return result; 45 return result;
46 } 46 }
@@ -48,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
48 if (nca->GetType() != FileSys::NCAContentType::Program) 48 if (nca->GetType() != FileSys::NCAContentType::Program)
49 return ResultStatus::ErrorInvalidFormat; 49 return ResultStatus::ErrorInvalidFormat;
50 50
51 auto exefs = nca->GetExeFS(); 51 const auto exefs = nca->GetExeFS();
52 52
53 if (exefs == nullptr) 53 if (exefs == nullptr)
54 return ResultStatus::ErrorInvalidFormat; 54 return ResultStatus::ErrorInvalidFormat;
55 55
56 result = metadata.Load(exefs->GetFile("main.npdm")); 56 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
57 if (result != ResultStatus::Success) {
58 return result;
59 }
60 metadata.Print();
61
62 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
63 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
64 return ResultStatus::ErrorUnsupportedArch;
65 }
66
67 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
68 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
69 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
70 const VAddr load_addr = next_load_addr;
71
72 next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
73 if (next_load_addr) {
74 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
75 // Register module with GDBStub
76 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
77 } else {
78 next_load_addr = load_addr;
79 }
80 }
81 57
82 process->program_id = metadata.GetTitleID(); 58 const auto load_result = directory_loader->Load(process);
83 process->svc_access_mask.set(); 59 if (load_result != ResultStatus::Success)
84 process->address_mappings = default_address_mappings; 60 return load_result;
85 process->resource_limit =
86 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
87 process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
88 metadata.GetMainThreadStackSize());
89 61
90 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) 62 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
91 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 63 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index e14d618b3..0fd2d0417 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -10,6 +10,7 @@
10#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/object.h" 11#include "core/hle/kernel/object.h"
12#include "core/loader/loader.h" 12#include "core/loader/loader.h"
13#include "deconstructed_rom_directory.h"
13 14
14namespace Loader { 15namespace Loader {
15 16
@@ -41,6 +42,7 @@ private:
41 FileSys::ProgramMetadata metadata; 42 FileSys::ProgramMetadata metadata;
42 43
43 std::unique_ptr<FileSys::NCA> nca; 44 std::unique_ptr<FileSys::NCA> nca;
45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
44}; 46};
45 47
46} // namespace Loader 48} // namespace Loader
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4b3bb7b31..1133bcbaf 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -326,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
326} 326}
327 327
328void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { 328void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
329 auto& system_instance = Core::System::GetInstance();
330
329 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 331 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
330 // null here 332 // null here
331 if (VideoCore::g_renderer == nullptr) { 333 if (!system_instance.IsPoweredOn()) {
332 return; 334 return;
333 } 335 }
334 336
335 VAddr end = start + size; 337 VAddr end = start + size;
336 338
337 auto CheckRegion = [&](VAddr region_start, VAddr region_end) { 339 const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
338 if (start >= region_end || end <= region_start) { 340 if (start >= region_end || end <= region_start) {
339 // No overlap with region 341 // No overlap with region
340 return; 342 return;
341 } 343 }
342 344
343 VAddr overlap_start = std::max(start, region_start); 345 const VAddr overlap_start = std::max(start, region_start);
344 VAddr overlap_end = std::min(end, region_end); 346 const VAddr overlap_end = std::min(end, region_end);
345 347
346 std::vector<Tegra::GPUVAddr> gpu_addresses = 348 const std::vector<Tegra::GPUVAddr> gpu_addresses =
347 Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start); 349 system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
348 350
349 if (gpu_addresses.empty()) { 351 if (gpu_addresses.empty()) {
350 return; 352 return;
351 } 353 }
352 354
353 u64 overlap_size = overlap_end - overlap_start; 355 const u64 overlap_size = overlap_end - overlap_start;
354 356
355 for (const auto& gpu_address : gpu_addresses) { 357 for (const auto& gpu_address : gpu_addresses) {
356 auto* rasterizer = VideoCore::g_renderer->Rasterizer(); 358 auto& rasterizer = system_instance.Renderer().Rasterizer();
357 switch (mode) { 359 switch (mode) {
358 case FlushMode::Flush: 360 case FlushMode::Flush:
359 rasterizer->FlushRegion(gpu_address, overlap_size); 361 rasterizer.FlushRegion(gpu_address, overlap_size);
360 break; 362 break;
361 case FlushMode::Invalidate: 363 case FlushMode::Invalidate:
362 rasterizer->InvalidateRegion(gpu_address, overlap_size); 364 rasterizer.InvalidateRegion(gpu_address, overlap_size);
363 break; 365 break;
364 case FlushMode::FlushAndInvalidate: 366 case FlushMode::FlushAndInvalidate:
365 rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size); 367 rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
366 break; 368 break;
367 } 369 }
368 } 370 }
diff --git a/src/core/memory.h b/src/core/memory.h
index b5d885b8a..b7fb3b9ed 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -140,10 +140,10 @@ void SetCurrentPageTable(PageTable* page_table);
140PageTable* GetCurrentPageTable(); 140PageTable* GetCurrentPageTable();
141 141
142/// Determines if the given VAddr is valid for the specified process. 142/// Determines if the given VAddr is valid for the specified process.
143bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); 143bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
144bool IsValidVirtualAddress(const VAddr addr); 144bool IsValidVirtualAddress(VAddr vaddr);
145/// Determines if the given VAddr is a kernel address 145/// Determines if the given VAddr is a kernel address
146bool IsKernelVirtualAddress(const VAddr addr); 146bool IsKernelVirtualAddress(VAddr vaddr);
147 147
148u8 Read8(VAddr addr); 148u8 Read8(VAddr addr);
149u16 Read16(VAddr addr); 149u16 Read16(VAddr addr);
@@ -155,18 +155,17 @@ void Write16(VAddr addr, u16 data);
155void Write32(VAddr addr, u32 data); 155void Write32(VAddr addr, u32 data);
156void Write64(VAddr addr, u64 data); 156void Write64(VAddr addr, u64 data);
157 157
158void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, 158void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
159 size_t size); 159void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
160void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); 160void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
161void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
162 size_t size); 161 size_t size);
163void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); 162void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
164void ZeroBlock(const VAddr dest_addr, const size_t size); 163void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
165void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 164void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
166 165
167u8* GetPointer(VAddr virtual_address); 166u8* GetPointer(VAddr vaddr);
168 167
169std::string ReadCString(VAddr virtual_address, std::size_t max_length); 168std::string ReadCString(VAddr vaddr, std::size_t max_length);
170 169
171enum class FlushMode { 170enum class FlushMode {
172 /// Write back modified surfaces to RAM 171 /// Write back modified surfaces to RAM
@@ -180,7 +179,7 @@ enum class FlushMode {
180/** 179/**
181 * Mark each page touching the region as cached. 180 * Mark each page touching the region as cached.
182 */ 181 */
183void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached); 182void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
184 183
185/** 184/**
186 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual 185 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 5f53b16d3..8e09b9b63 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 40 game_frames += 1;
41} 41}
42 42
43PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { 43PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard<std::mutex> lock(object_mutex); 44 std::lock_guard<std::mutex> lock(object_mutex);
45 45
46 auto now = Clock::now(); 46 const auto now = Clock::now();
47 // Walltime elapsed since stats were reset 47 // Walltime elapsed since stats were reset
48 auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); 48 const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
49 49
50 auto system_us_per_second = 50 const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
51 static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
52 51
53 Results results{}; 52 Results results{};
54 results.system_fps = static_cast<double>(system_frames) / interval; 53 results.system_fps = static_cast<double>(system_frames) / interval;
55 results.game_fps = static_cast<double>(game_frames) / interval; 54 results.game_fps = static_cast<double>(game_frames) / interval;
56 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / 55 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
57 static_cast<double>(system_frames); 56 static_cast<double>(system_frames);
58 results.emulation_speed = system_us_per_second / 1'000'000.0; 57 results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
59 58
60 // Reset counters 59 // Reset counters
61 reset_point = now; 60 reset_point = now;
@@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() {
74 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 73 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
75} 74}
76 75
77void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
78 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher 77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
79 // values increase the time needed to recover and limit framerate again after spikes. 78 // values increase the time needed to recover and limit framerate again after spikes.
80 constexpr microseconds MAX_LAG_TIME_US = 25ms; 79 constexpr microseconds MAX_LAG_TIME_US = 25us;
81 80
82 if (!Settings::values.toggle_framelimit) { 81 if (!Settings::values.toggle_framelimit) {
83 return; 82 return;
@@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
85 84
86 auto now = Clock::now(); 85 auto now = Clock::now();
87 86
88 frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); 87 frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
89 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); 88 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
90 frame_limiting_delta_err = 89 frame_limiting_delta_err =
91 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); 90 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 362b205c8..6e4619701 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
33 void EndSystemFrame(); 33 void EndSystemFrame();
34 void EndGameFrame(); 34 void EndGameFrame();
35 35
36 Results GetAndResetStats(u64 current_system_time_us); 36 Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 37
38 /** 38 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
47 /// Point when the cumulative counters were reset 47 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 48 Clock::time_point reset_point = Clock::now();
49 /// System time when the cumulative counters were reset 49 /// System time when the cumulative counters were reset
50 u64 reset_point_system_us = 0; 50 std::chrono::microseconds reset_point_system_us{0};
51 51
52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset 52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
53 Clock::duration accumulated_frametime = Clock::duration::zero(); 53 Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
68public: 68public:
69 using Clock = std::chrono::high_resolution_clock; 69 using Clock = std::chrono::high_resolution_clock;
70 70
71 void DoFrameLimiting(u64 current_system_time_us); 71 void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
72 72
73private: 73private:
74 /// Emulated system time (in microseconds) at the last limiter invocation 74 /// Emulated system time (in microseconds) at the last limiter invocation
75 u64 previous_system_time_us = 0; 75 std::chrono::microseconds previous_system_time_us{0};
76 /// Walltime at the last limiter invocation 76 /// Walltime at the last limiter invocation
77 Clock::time_point previous_walltime = Clock::now(); 77 Clock::time_point previous_walltime = Clock::now();
78 78
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 79e0b347b..a4623223d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.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 "core/core.h"
5#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
6#include "core/hle/service/hid/hid.h" 7#include "core/hle/service/hid/hid.h"
7#include "core/settings.h" 8#include "core/settings.h"
@@ -19,8 +20,9 @@ void Apply() {
19 20
20 VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; 21 VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
21 22
22 if (VideoCore::g_renderer) { 23 auto& system_instance = Core::System::GetInstance();
23 VideoCore::g_renderer->UpdateCurrentFramebufferLayout(); 24 if (system_instance.IsPoweredOn()) {
25 system_instance.Renderer().UpdateCurrentFramebufferLayout();
24 } 26 }
25 27
26 Service::HID::ReloadInputDevices(); 28 Service::HID::ReloadInputDevices();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 0e205ed72..5c0ae8009 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -19,8 +19,8 @@ namespace Engines {
19/// First register id that is actually a Macro call. 19/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 20constexpr u32 MacroRegistersStart = 0xE00;
21 21
22Maxwell3D::Maxwell3D(MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
23 : memory_manager(memory_manager), macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
24 24
25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
26 auto macro_code = uploaded_macros.find(method); 26 auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
130 break; 130 break;
131 } 131 }
132 132
133 VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method); 133 rasterizer.NotifyMaxwellRegisterChanged(method);
134 134
135 if (debug_context) { 135 if (debug_context) {
136 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); 136 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
218 } 218 }
219 219
220 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 220 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
221 VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed); 221 rasterizer.AccelerateDrawBatch(is_indexed);
222 222
223 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 223 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
224 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - 224 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
285 285
286 // TODO(Subv): Different data types for separate components are not supported 286 // TODO(Subv): Different data types for separate components are not supported
287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); 287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
288 // TODO(Subv): Only UNORM formats are supported for now.
289 ASSERT(r_type == Texture::ComponentType::UNORM);
290 288
291 return tic_entry; 289 return tic_entry;
292} 290}
@@ -393,7 +391,7 @@ void Maxwell3D::ProcessClearBuffers() {
393 regs.clear_buffers.R == regs.clear_buffers.B && 391 regs.clear_buffers.R == regs.clear_buffers.B &&
394 regs.clear_buffers.R == regs.clear_buffers.A); 392 regs.clear_buffers.R == regs.clear_buffers.A);
395 393
396 VideoCore::g_renderer->Rasterizer()->Clear(); 394 rasterizer.Clear();
397} 395}
398 396
399} // namespace Engines 397} // namespace Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3c32f1067..4d0ff96a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -17,6 +17,10 @@
17#include "video_core/memory_manager.h" 17#include "video_core/memory_manager.h"
18#include "video_core/textures/texture.h" 18#include "video_core/textures/texture.h"
19 19
20namespace VideoCore {
21class RasterizerInterface;
22}
23
20namespace Tegra::Engines { 24namespace Tegra::Engines {
21 25
22#define MAXWELL3D_REG_INDEX(field_name) \ 26#define MAXWELL3D_REG_INDEX(field_name) \
@@ -24,7 +28,7 @@ namespace Tegra::Engines {
24 28
25class Maxwell3D final { 29class Maxwell3D final {
26public: 30public:
27 explicit Maxwell3D(MemoryManager& memory_manager); 31 explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
28 ~Maxwell3D() = default; 32 ~Maxwell3D() = default;
29 33
30 /// Register structure of the Maxwell3D engine. 34 /// Register structure of the Maxwell3D engine.
@@ -818,6 +822,8 @@ public:
818 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; 822 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
819 823
820private: 824private:
825 VideoCore::RasterizerInterface& rasterizer;
826
821 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 827 std::unordered_map<u32, std::vector<u32>> uploaded_macros;
822 828
823 /// Macro method that is currently being executed / being fed parameters. 829 /// Macro method that is currently being executed / being fed parameters.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 141e20444..b2a83ce0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -7,12 +7,13 @@
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8#include "video_core/engines/maxwell_dma.h" 8#include "video_core/engines/maxwell_dma.h"
9#include "video_core/gpu.h" 9#include "video_core/gpu.h"
10#include "video_core/rasterizer_interface.h"
10 11
11namespace Tegra { 12namespace Tegra {
12 13
13GPU::GPU() { 14GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
14 memory_manager = std::make_unique<MemoryManager>(); 15 memory_manager = std::make_unique<MemoryManager>();
15 maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager); 16 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
16 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); 17 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
17 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 18 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
18 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 19 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 08aa75503..440505c9d 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,10 @@
11#include "core/hle/service/nvflinger/buffer_queue.h" 11#include "core/hle/service/nvflinger/buffer_queue.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14namespace VideoCore {
15class RasterizerInterface;
16}
17
14namespace Tegra { 18namespace Tegra {
15 19
16enum class RenderTargetFormat : u32 { 20enum class RenderTargetFormat : u32 {
@@ -98,7 +102,7 @@ enum class EngineID {
98 102
99class GPU final { 103class GPU final {
100public: 104public:
101 GPU(); 105 explicit GPU(VideoCore::RasterizerInterface& rasterizer);
102 ~GPU(); 106 ~GPU();
103 107
104 /// Processes a command list stored at the specified address in GPU memory. 108 /// Processes a command list stored at the specified address in GPU memory.
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index dbe3edf09..3ca350243 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -7,6 +7,8 @@
7#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/gl_rasterizer.h" 8#include "video_core/renderer_opengl/gl_rasterizer.h"
9 9
10namespace VideoCore {
11
10RendererBase::RendererBase(EmuWindow& window) : render_window{window} {} 12RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
11RendererBase::~RendererBase() = default; 13RendererBase::~RendererBase() = default;
12 14
@@ -21,3 +23,5 @@ void RendererBase::RefreshRasterizerSetting() {
21 rasterizer = std::make_unique<RasterizerOpenGL>(render_window); 23 rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
22 } 24 }
23} 25}
26
27} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 1cb161b7f..235de23a1 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -13,6 +13,8 @@
13 13
14class EmuWindow; 14class EmuWindow;
15 15
16namespace VideoCore {
17
16class RendererBase : NonCopyable { 18class RendererBase : NonCopyable {
17public: 19public:
18 /// Used to reference a framebuffer 20 /// Used to reference a framebuffer
@@ -44,15 +46,21 @@ public:
44 return m_current_frame; 46 return m_current_frame;
45 } 47 }
46 48
47 VideoCore::RasterizerInterface* Rasterizer() const { 49 RasterizerInterface& Rasterizer() {
48 return rasterizer.get(); 50 return *rasterizer;
51 }
52
53 const RasterizerInterface& Rasterizer() const {
54 return *rasterizer;
49 } 55 }
50 56
51 void RefreshRasterizerSetting(); 57 void RefreshRasterizerSetting();
52 58
53protected: 59protected:
54 EmuWindow& render_window; ///< Reference to the render window handle. 60 EmuWindow& render_window; ///< Reference to the render window handle.
55 std::unique_ptr<VideoCore::RasterizerInterface> rasterizer; 61 std::unique_ptr<RasterizerInterface> rasterizer;
56 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer 62 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
57 int m_current_frame = 0; ///< Current frame, should be set by the renderer 63 int m_current_frame = 0; ///< Current frame, should be set by the renderer
58}; 64};
65
66} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6555db5bb..c2a931469 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -169,8 +169,14 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
169 ASSERT(buffer.IsEnabled()); 169 ASSERT(buffer.IsEnabled());
170 170
171 glEnableVertexAttribArray(index); 171 glEnableVertexAttribArray(index);
172 glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), 172 if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
173 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); 173 attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
174 glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
175 attrib.offset);
176 } else {
177 glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
178 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
179 }
174 glVertexAttribBinding(index, attrib.buffer); 180 glVertexAttribBinding(index, attrib.buffer);
175 } 181 }
176 182
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index c8f0c4e28..257aa9571 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -46,6 +46,8 @@ struct FormatTuple {
46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); 46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
47 params.unaligned_height = config.tic.Height(); 47 params.unaligned_height = config.tic.Height();
48 params.size_in_bytes = params.SizeInBytes(); 48 params.size_in_bytes = params.SizeInBytes();
49 params.cache_width = Common::AlignUp(params.width, 16);
50 params.cache_height = Common::AlignUp(params.height, 16);
49 return params; 51 return params;
50} 52}
51 53
@@ -63,6 +65,8 @@ struct FormatTuple {
63 params.height = config.height; 65 params.height = config.height;
64 params.unaligned_height = config.height; 66 params.unaligned_height = config.height;
65 params.size_in_bytes = params.SizeInBytes(); 67 params.size_in_bytes = params.SizeInBytes();
68 params.cache_width = Common::AlignUp(params.width, 16);
69 params.cache_height = Common::AlignUp(params.height, 16);
66 return params; 70 return params;
67} 71}
68 72
@@ -82,6 +86,8 @@ struct FormatTuple {
82 params.height = zeta_height; 86 params.height = zeta_height;
83 params.unaligned_height = zeta_height; 87 params.unaligned_height = zeta_height;
84 params.size_in_bytes = params.SizeInBytes(); 88 params.size_in_bytes = params.SizeInBytes();
89 params.cache_width = Common::AlignUp(params.width, 16);
90 params.cache_height = Common::AlignUp(params.height, 16);
85 return params; 91 return params;
86} 92}
87 93
@@ -680,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
680 // If use_accurate_framebuffers is enabled, always load from memory 686 // If use_accurate_framebuffers is enabled, always load from memory
681 FlushSurface(surface); 687 FlushSurface(surface);
682 UnregisterSurface(surface); 688 UnregisterSurface(surface);
683 } else if (surface->GetSurfaceParams() != params) { 689 } else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
684 // If surface parameters changed, recreate the surface from the old one
685 return RecreateSurface(surface, params);
686 } else {
687 // Use the cached surface as-is 690 // Use the cached surface as-is
688 return surface; 691 return surface;
692 } else {
693 // If surface parameters changed, recreate the surface from the old one
694 return RecreateSurface(surface, params);
689 } 695 }
690 } 696 }
691 697
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 4e1e18d9c..39fcf22b4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -9,6 +9,7 @@
9#include <memory> 9#include <memory>
10#include <vector> 10#include <vector>
11#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/math_util.h" 14#include "common/math_util.h"
14#include "video_core/engines/maxwell_3d.h" 15#include "video_core/engines/maxwell_3d.h"
@@ -546,6 +547,12 @@ struct SurfaceParams {
546 return !operator==(other); 547 return !operator==(other);
547 } 548 }
548 549
550 /// Checks if surfaces are compatible for caching
551 bool IsCompatibleSurface(const SurfaceParams& other) const {
552 return std::tie(pixel_format, type, cache_width, cache_height) ==
553 std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
554 }
555
549 Tegra::GPUVAddr addr; 556 Tegra::GPUVAddr addr;
550 bool is_tiled; 557 bool is_tiled;
551 u32 block_height; 558 u32 block_height;
@@ -556,6 +563,10 @@ struct SurfaceParams {
556 u32 height; 563 u32 height;
557 u32 unaligned_height; 564 u32 unaligned_height;
558 size_t size_in_bytes; 565 size_t size_in_bytes;
566
567 // Parameters used for caching only
568 u32 cache_width;
569 u32 cache_height;
559}; 570};
560 571
561class CachedSurface final { 572class CachedSurface final {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 68db3c22a..e3217db81 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -766,13 +766,16 @@ private:
766 // goes into gpr28+0 and gpr28+1 766 // goes into gpr28+0 and gpr28+1
767 size_t texs_offset{}; 767 size_t texs_offset{};
768 768
769 size_t src_elem{};
769 for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) { 770 for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
771 size_t dest_elem{};
770 for (unsigned elem = 0; elem < 2; ++elem) { 772 for (unsigned elem = 0; elem < 2; ++elem) {
771 if (!instr.texs.IsComponentEnabled(elem)) { 773 if (!instr.texs.IsComponentEnabled(src_elem++)) {
772 // Skip disabled components 774 // Skip disabled components
773 continue; 775 continue;
774 } 776 }
775 regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem); 777 regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
778 dest_elem++);
776 } 779 }
777 780
778 if (!instr.texs.HasTwoDestinations()) { 781 if (!instr.texs.HasTwoDestinations()) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 74383c7cf..bf9131193 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -103,7 +103,7 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
103 } 103 }
104} 104}
105 105
106RendererOpenGL::RendererOpenGL(EmuWindow& window) : RendererBase{window} {} 106RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
107RendererOpenGL::~RendererOpenGL() = default; 107RendererOpenGL::~RendererOpenGL() = default;
108 108
109/// Swap buffers (render frame) 109/// Swap buffers (render frame)
@@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
160 // only allows rows to have a memory alignement of 4. 160 // only allows rows to have a memory alignement of 4.
161 ASSERT(framebuffer.stride % 4 == 0); 161 ASSERT(framebuffer.stride % 4 == 0);
162 162
163 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride, 163 if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
164 screen_info)) { 164 screen_info)) {
165 // Reset the screen info's display texture to its own permanent texture 165 // Reset the screen info's display texture to its own permanent texture
166 screen_info.display_texture = screen_info.texture.resource.handle; 166 screen_info.display_texture = screen_info.texture.resource.handle;
167 167
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index ab7de41c8..428afa3b7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -41,7 +41,7 @@ private:
41 EmuWindow& emu_window; 41 EmuWindow& emu_window;
42}; 42};
43 43
44class RendererOpenGL : public RendererBase { 44class RendererOpenGL : public VideoCore::RendererBase {
45public: 45public:
46 explicit RendererOpenGL(EmuWindow& window); 46 explicit RendererOpenGL(EmuWindow& window);
47 ~RendererOpenGL() override; 47 ~RendererOpenGL() override;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 06b13e681..5085ef96b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,37 +3,16 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include "common/logging/log.h"
7#include "video_core/renderer_base.h" 6#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/renderer_opengl.h" 7#include "video_core/renderer_opengl/renderer_opengl.h"
9#include "video_core/video_core.h" 8#include "video_core/video_core.h"
10 9
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// Video Core namespace
13
14namespace VideoCore { 10namespace VideoCore {
15 11
16std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
17
18std::atomic<bool> g_toggle_framelimit_enabled; 12std::atomic<bool> g_toggle_framelimit_enabled;
19 13
20/// Initialize the video core 14std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
21bool Init(EmuWindow& emu_window) { 15 return std::make_unique<RendererOpenGL>(emu_window);
22 g_renderer = std::make_unique<RendererOpenGL>(emu_window);
23 if (g_renderer->Init()) {
24 LOG_DEBUG(Render, "initialized OK");
25 } else {
26 LOG_CRITICAL(Render, "initialization failed !");
27 return false;
28 }
29 return true;
30}
31
32/// Shutdown the video core
33void Shutdown() {
34 g_renderer.reset();
35
36 LOG_DEBUG(Render, "shutdown OK");
37} 16}
38 17
39} // namespace VideoCore 18} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 519b757f5..7c01c0b8d 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -8,25 +8,23 @@
8#include <memory> 8#include <memory>
9 9
10class EmuWindow; 10class EmuWindow;
11class RendererBase;
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Video Core namespace
15 11
16namespace VideoCore { 12namespace VideoCore {
17 13
18enum class Renderer { Software, OpenGL }; 14class RendererBase;
19 15
20extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin 16enum class Renderer { Software, OpenGL };
21 17
22// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from 18// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
23// qt ui) 19// qt ui)
24extern std::atomic<bool> g_toggle_framelimit_enabled; 20extern std::atomic<bool> g_toggle_framelimit_enabled;
25 21
26/// Initialize the video core 22/**
27bool Init(EmuWindow& emu_window); 23 * Creates a renderer instance.
28 24 *
29/// Shutdown the video core 25 * @note The returned renderer instance is simply allocated. Its Init()
30void Shutdown(); 26 * function still needs to be called to fully complete its setup.
27 */
28std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
31 29
32} // namespace VideoCore 30} // namespace VideoCore
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index 39ed3bccf..a81ad2888 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -15,4 +15,4 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
15 Common::g_scm_desc, QString(Common::g_build_date).left(10))); 15 Common::g_scm_desc, QString(Common::g_build_date).left(10)));
16} 16}
17 17
18AboutDialog::~AboutDialog() {} 18AboutDialog::~AboutDialog() = default;
diff --git a/src/yuzu/about_dialog.h b/src/yuzu/about_dialog.h
index 2eb6e28f5..18e8c11a7 100644
--- a/src/yuzu/about_dialog.h
+++ b/src/yuzu/about_dialog.h
@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
16 16
17public: 17public:
18 explicit AboutDialog(QWidget* parent); 18 explicit AboutDialog(QWidget* parent);
19 ~AboutDialog(); 19 ~AboutDialog() override;
20 20
21private: 21private:
22 std::unique_ptr<Ui::AboutDialog> ui; 22 std::unique_ptr<Ui::AboutDialog> ui;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 130bc613b..d0f990c64 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
106 106
107public: 107public:
108 GRenderWindow(QWidget* parent, EmuThread* emu_thread); 108 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
109 ~GRenderWindow(); 109 ~GRenderWindow() override;
110 110
111 // EmuWindow implementation 111 // EmuWindow implementation
112 void SwapBuffers() override; 112 void SwapBuffers() override;
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 7fd07539a..45d84f19a 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
24 }); 24 });
25} 25}
26 26
27ConfigureDebug::~ConfigureDebug() {} 27ConfigureDebug::~ConfigureDebug() = default;
28 28
29void ConfigureDebug::setConfiguration() { 29void ConfigureDebug::setConfiguration() {
30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); 30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 118e91cf1..5ae7276bd 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -23,13 +23,6 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <widget class="QLabel" name="label_1">
27 <property name="text">
28 <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <layout class="QHBoxLayout" name="horizontalLayout_1"> 26 <layout class="QHBoxLayout" name="horizontalLayout_1">
34 <item> 27 <item>
35 <widget class="QCheckBox" name="toggle_gdbstub"> 28 <widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index f66abf870..cc4b326ae 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,13 +6,16 @@
6#include "ui_configure.h" 6#include "ui_configure.h"
7#include "yuzu/configuration/config.h" 7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_dialog.h" 8#include "yuzu/configuration/configure_dialog.h"
9#include "yuzu/hotkeys.h"
9 10
10ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { 11ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
12 : QDialog(parent), ui(new Ui::ConfigureDialog) {
11 ui->setupUi(this); 13 ui->setupUi(this);
14 ui->generalTab->PopulateHotkeyList(registry);
12 this->setConfiguration(); 15 this->setConfiguration();
13} 16}
14 17
15ConfigureDialog::~ConfigureDialog() {} 18ConfigureDialog::~ConfigureDialog() = default;
16 19
17void ConfigureDialog::setConfiguration() {} 20void ConfigureDialog::setConfiguration() {}
18 21
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 21fa1f501..bbbdacc29 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <QDialog> 8#include <QDialog>
9 9
10class HotkeyRegistry;
11
10namespace Ui { 12namespace Ui {
11class ConfigureDialog; 13class ConfigureDialog;
12} 14}
@@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog {
15 Q_OBJECT 17 Q_OBJECT
16 18
17public: 19public:
18 explicit ConfigureDialog(QWidget* parent); 20 explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
19 ~ConfigureDialog(); 21 ~ConfigureDialog();
20 22
21 void applyConfiguration(); 23 void applyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb7d3f8bf..d8caee1e8 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
25} 25}
26 26
27ConfigureGeneral::~ConfigureGeneral() {} 27ConfigureGeneral::~ConfigureGeneral() = default;
28 28
29void ConfigureGeneral::setConfiguration() { 29void ConfigureGeneral::setConfiguration() {
30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); 30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
@@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
35 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 35 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
36} 36}
37 37
38void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
39 ui->widget->Populate(registry);
40}
41
38void ConfigureGeneral::applyConfiguration() { 42void ConfigureGeneral::applyConfiguration() {
39 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
40 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 447552d8c..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9 9
10class HotkeyRegistry;
11
10namespace Ui { 12namespace Ui {
11class ConfigureGeneral; 13class ConfigureGeneral;
12} 14}
@@ -18,11 +20,11 @@ public:
18 explicit ConfigureGeneral(QWidget* parent = nullptr); 20 explicit ConfigureGeneral(QWidget* parent = nullptr);
19 ~ConfigureGeneral(); 21 ~ConfigureGeneral();
20 22
23 void PopulateHotkeyList(const HotkeyRegistry& registry);
21 void applyConfiguration(); 24 void applyConfiguration();
22 25
23private: 26private:
24 void setConfiguration(); 27 void setConfiguration();
25 28
26private:
27 std::unique_ptr<Ui::ConfigureGeneral> ui; 29 std::unique_ptr<Ui::ConfigureGeneral> ui;
28}; 30};
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 3379b7963..4afe0f81b 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
14 this->setConfiguration(); 14 this->setConfiguration();
15} 15}
16 16
17ConfigureGraphics::~ConfigureGraphics() {} 17ConfigureGraphics::~ConfigureGraphics() = default;
18 18
19enum class Resolution : int { 19enum class Resolution : int {
20 Auto, 20 Auto,
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9be2c939c..e9ed9c38f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -35,7 +35,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
35 this->setConfiguration(); 35 this->setConfiguration();
36} 36}
37 37
38ConfigureSystem::~ConfigureSystem() {} 38ConfigureSystem::~ConfigureSystem() = default;
39 39
40void ConfigureSystem::setConfiguration() { 40void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 41 enabled = !Core::System::GetInstance().IsPoweredOn();
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index ff3efcdaa..3f7103ab9 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
34 34
35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) 35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
36 : QLabel(parent), surface_widget(surface_widget_) {} 36 : QLabel(parent), surface_widget(surface_widget_) {}
37SurfacePicture::~SurfacePicture() {} 37
38SurfacePicture::~SurfacePicture() = default;
38 39
39void SurfacePicture::mousePressEvent(QMouseEvent* event) { 40void SurfacePicture::mousePressEvent(QMouseEvent* event) {
40 // Only do something while the left mouse button is held down 41 // Only do something while the left mouse button is held down
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 58f9db465..323e39d94 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
22public: 22public:
23 explicit SurfacePicture(QWidget* parent = nullptr, 23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr); 24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture(); 25 ~SurfacePicture() override;
26 26
27protected slots: 27protected slots:
28 virtual void mouseMoveEvent(QMouseEvent* event); 28 void mouseMoveEvent(QMouseEvent* event) override;
29 virtual void mousePressEvent(QMouseEvent* event); 29 void mousePressEvent(QMouseEvent* event) override;
30 30
31private: 31private:
32 GraphicsSurfaceWidget* surface_widget; 32 GraphicsSurfaceWidget* surface_widget;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f5a5697a0..d0926d723 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -14,7 +14,7 @@
14#include "core/hle/kernel/timer.h" 14#include "core/hle/kernel/timer.h"
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16 16
17WaitTreeItem::~WaitTreeItem() {} 17WaitTreeItem::~WaitTreeItem() = default;
18 18
19QColor WaitTreeItem::GetColor() const { 19QColor WaitTreeItem::GetColor() const {
20 return QColor(Qt::GlobalColor::black); 20 return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
316 316
317 list.push_back(std::make_unique<WaitTreeText>( 317 list.push_back(std::make_unique<WaitTreeText>(
318 tr("reset type = %1") 318 tr("reset type = %1")
319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type)))); 319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
320 return list; 320 return list;
321} 321}
322 322
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 6cbce6856..513b3c45d 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -25,11 +25,13 @@ class WaitTreeThread;
25class WaitTreeItem : public QObject { 25class WaitTreeItem : public QObject {
26 Q_OBJECT 26 Q_OBJECT
27public: 27public:
28 ~WaitTreeItem() override;
29
28 virtual bool IsExpandable() const; 30 virtual bool IsExpandable() const;
29 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const; 31 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
30 virtual QString GetText() const = 0; 32 virtual QString GetText() const = 0;
31 virtual QColor GetColor() const; 33 virtual QColor GetColor() const;
32 virtual ~WaitTreeItem(); 34
33 void Expand(); 35 void Expand();
34 WaitTreeItem* Parent() const; 36 WaitTreeItem* Parent() const;
35 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const; 37 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 71e24a9e2..24f38a3c7 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) {
162 } 162 }
163 search_field->setFilterResult(rowCount, rowCount); 163 search_field->setFilterResult(rowCount, rowCount);
164 } else { 164 } else {
165 QStandardItem* child_file;
166 QString file_path, file_name, file_title, file_programmid;
167 int result_count = 0; 165 int result_count = 0;
168 for (int i = 0; i < rowCount; ++i) { 166 for (int i = 0; i < rowCount; ++i) {
169 child_file = item_model->item(i, 0); 167 const QStandardItem* child_file = item_model->item(i, 0);
170 file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); 168 const QString file_path =
171 file_name = file_path.mid(file_path.lastIndexOf("/") + 1); 169 child_file->data(GameListItemPath::FullPathRole).toString().toLower();
172 file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); 170 QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
173 file_programmid = 171 const QString file_title =
172 child_file->data(GameListItemPath::TitleRole).toString().toLower();
173 const QString file_programmid =
174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); 174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
175 175
176 // Only items which filename in combination with its title contains all words 176 // Only items which filename in combination with its title contains all words
@@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
258 258
259void GameList::ValidateEntry(const QModelIndex& item) { 259void GameList::ValidateEntry(const QModelIndex& item) {
260 // We don't care about the individual QStandardItem that was selected, but its row. 260 // We don't care about the individual QStandardItem that was selected, but its row.
261 int row = item_model->itemFromIndex(item)->row(); 261 const int row = item_model->itemFromIndex(item)->row();
262 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); 262 const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
263 QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); 263 const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
264 264
265 if (file_path.isEmpty()) 265 if (file_path.isEmpty())
266 return; 266 return;
267 std::string std_file_path(file_path.toStdString()); 267
268 if (!FileUtil::Exists(std_file_path)) 268 if (!QFileInfo::exists(file_path))
269 return; 269 return;
270 if (FileUtil::IsDirectory(std_file_path)) { 270
271 QDir dir(std_file_path.c_str()); 271 const QFileInfo file_info{file_path};
272 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 272 if (file_info.isDir()) {
273 const QDir dir{file_path};
274 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
273 if (matching_main.size() == 1) { 275 if (matching_main.size() == 1) {
274 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); 276 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
275 } 277 }
@@ -368,21 +370,23 @@ void GameList::LoadInterfaceLayout() {
368const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; 370const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
369 371
370static bool HasSupportedFileExtension(const std::string& file_name) { 372static bool HasSupportedFileExtension(const std::string& file_name) {
371 QFileInfo file = QFileInfo(file_name.c_str()); 373 const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
372 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); 374 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
373} 375}
374 376
375static bool IsExtractedNCAMain(const std::string& file_name) { 377static bool IsExtractedNCAMain(const std::string& file_name) {
376 return QFileInfo(file_name.c_str()).fileName() == "main"; 378 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
377} 379}
378 380
379static QString FormatGameName(const std::string& physical_name) { 381static QString FormatGameName(const std::string& physical_name) {
380 QFileInfo file_info(physical_name.c_str()); 382 const QString physical_name_as_qstring = QString::fromStdString(physical_name);
383 const QFileInfo file_info(physical_name_as_qstring);
384
381 if (IsExtractedNCAMain(physical_name)) { 385 if (IsExtractedNCAMain(physical_name)) {
382 return file_info.dir().path(); 386 return file_info.dir().path();
383 } else {
384 return QString::fromStdString(physical_name);
385 } 387 }
388
389 return physical_name_as_qstring;
386} 390}
387 391
388void GameList::RefreshGameDirectory() { 392void GameList::RefreshGameDirectory() {
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a758b77aa..aa69a098f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <utility>
8#include <QImage> 9#include <QImage>
9#include <QRunnable> 10#include <QRunnable>
10#include <QStandardItem> 11#include <QStandardItem>
@@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) {
27class GameListItem : public QStandardItem { 28class GameListItem : public QStandardItem {
28 29
29public: 30public:
30 GameListItem() : QStandardItem() {} 31 GameListItem() = default;
31 GameListItem(const QString& string) : QStandardItem(string) {} 32 explicit GameListItem(const QString& string) : QStandardItem(string) {}
32 virtual ~GameListItem() override {}
33}; 33};
34 34
35/** 35/**
@@ -45,9 +45,8 @@ public:
45 static const int TitleRole = Qt::UserRole + 2; 45 static const int TitleRole = Qt::UserRole + 2;
46 static const int ProgramIdRole = Qt::UserRole + 3; 46 static const int ProgramIdRole = Qt::UserRole + 3;
47 47
48 GameListItemPath() : GameListItem() {} 48 GameListItemPath() = default;
49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) 49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
50 : GameListItem() {
51 setData(game_path, FullPathRole); 50 setData(game_path, FullPathRole);
52 setData(qulonglong(program_id), ProgramIdRole); 51 setData(qulonglong(program_id), ProgramIdRole);
53 } 52 }
@@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem {
75public: 74public:
76 static const int SizeRole = Qt::UserRole + 1; 75 static const int SizeRole = Qt::UserRole + 1;
77 76
78 GameListItemSize() : GameListItem() {} 77 GameListItemSize() = default;
79 GameListItemSize(const qulonglong size_bytes) : GameListItem() { 78 explicit GameListItemSize(const qulonglong size_bytes) {
80 setData(size_bytes, SizeRole); 79 setData(size_bytes, SizeRole);
81 } 80 }
82 81
@@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable {
111 110
112public: 111public:
113 GameListWorker(QString dir_path, bool deep_scan) 112 GameListWorker(QString dir_path, bool deep_scan)
114 : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} 113 : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
115 114
116public slots: 115public slots:
117 /// Starts the processing of directory tree information. 116 /// Starts the processing of directory tree information.
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 61acb38ee..dce399774 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -10,58 +10,53 @@
10#include "yuzu/hotkeys.h" 10#include "yuzu/hotkeys.h"
11#include "yuzu/ui_settings.h" 11#include "yuzu/ui_settings.h"
12 12
13struct Hotkey { 13HotkeyRegistry::HotkeyRegistry() = default;
14 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} 14HotkeyRegistry::~HotkeyRegistry() = default;
15 15
16 QKeySequence keyseq; 16void HotkeyRegistry::LoadHotkeys() {
17 QShortcut* shortcut;
18 Qt::ShortcutContext context;
19};
20
21typedef std::map<QString, Hotkey> HotkeyMap;
22typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
23
24HotkeyGroupMap hotkey_groups;
25
26void SaveHotkeys() {
27 UISettings::values.shortcuts.clear();
28 for (auto group : hotkey_groups) {
29 for (auto hotkey : group.second) {
30 UISettings::values.shortcuts.emplace_back(
31 UISettings::Shortcut(group.first + "/" + hotkey.first,
32 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
33 hotkey.second.context)));
34 }
35 }
36}
37
38void LoadHotkeys() {
39 // Make sure NOT to use a reference here because it would become invalid once we call 17 // Make sure NOT to use a reference here because it would become invalid once we call
40 // beginGroup() 18 // beginGroup()
41 for (auto shortcut : UISettings::values.shortcuts) { 19 for (auto shortcut : UISettings::values.shortcuts) {
42 QStringList cat = shortcut.first.split("/"); 20 const QStringList cat = shortcut.first.split('/');
43 Q_ASSERT(cat.size() >= 2); 21 Q_ASSERT(cat.size() >= 2);
44 22
45 // RegisterHotkey assigns default keybindings, so use old values as default parameters 23 // RegisterHotkey assigns default keybindings, so use old values as default parameters
46 Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; 24 Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
47 if (!shortcut.second.first.isEmpty()) { 25 if (!shortcut.second.first.isEmpty()) {
48 hk.keyseq = QKeySequence::fromString(shortcut.second.first); 26 hk.keyseq = QKeySequence::fromString(shortcut.second.first);
49 hk.context = (Qt::ShortcutContext)shortcut.second.second; 27 hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
50 } 28 }
51 if (hk.shortcut) 29 if (hk.shortcut)
52 hk.shortcut->setKey(hk.keyseq); 30 hk.shortcut->setKey(hk.keyseq);
53 } 31 }
54} 32}
55 33
56void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, 34void HotkeyRegistry::SaveHotkeys() {
57 Qt::ShortcutContext default_context) { 35 UISettings::values.shortcuts.clear();
58 if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) { 36 for (const auto& group : hotkey_groups) {
59 hotkey_groups[group][action].keyseq = default_keyseq; 37 for (const auto& hotkey : group.second) {
60 hotkey_groups[group][action].context = default_context; 38 UISettings::values.shortcuts.emplace_back(
39 UISettings::Shortcut(group.first + '/' + hotkey.first,
40 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
41 hotkey.second.context)));
42 }
61 } 43 }
62} 44}
63 45
64QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) { 46void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action,
47 const QKeySequence& default_keyseq,
48 Qt::ShortcutContext default_context) {
49 auto& hotkey_group = hotkey_groups[group];
50 if (hotkey_group.find(action) != hotkey_group.end()) {
51 return;
52 }
53
54 auto& hotkey_action = hotkey_groups[group][action];
55 hotkey_action.keyseq = default_keyseq;
56 hotkey_action.context = default_context;
57}
58
59QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
65 Hotkey& hk = hotkey_groups[group][action]; 60 Hotkey& hk = hotkey_groups[group][action];
66 61
67 if (!hk.shortcut) 62 if (!hk.shortcut)
@@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
72 67
73GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { 68GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
74 ui.setupUi(this); 69 ui.setupUi(this);
70}
75 71
76 for (auto group : hotkey_groups) { 72void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
73 for (const auto& group : registry.hotkey_groups) {
77 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); 74 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
78 for (auto hotkey : group.second) { 75 for (const auto& hotkey : group.second) {
79 QStringList columns; 76 QStringList columns;
80 columns << hotkey.first << hotkey.second.keyseq.toString(); 77 columns << hotkey.first << hotkey.second.keyseq.toString();
81 QTreeWidgetItem* item = new QTreeWidgetItem(columns); 78 QTreeWidgetItem* item = new QTreeWidgetItem(columns);
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index a4ccc193b..f38e6c002 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include "ui_hotkeys.h" 8#include "ui_hotkeys.h"
8 9
9class QDialog; 10class QDialog;
@@ -11,47 +12,69 @@ class QKeySequence;
11class QSettings; 12class QSettings;
12class QShortcut; 13class QShortcut;
13 14
14/** 15class HotkeyRegistry final {
15 * Register a hotkey. 16public:
16 * 17 friend class GHotkeysDialog;
17 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") 18
18 * @param action Name of the action (e.g. "Start Emulation", "Load Image") 19 explicit HotkeyRegistry();
19 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings 20 ~HotkeyRegistry();
20 * file before 21
21 * @param default_context Default context to assign if the hotkey wasn't present in the settings 22 /**
22 * file before 23 * Loads hotkeys from the settings file.
23 * @warning Both the group and action strings will be displayed in the hotkey settings dialog 24 *
24 */ 25 * @note Yet unregistered hotkeys which are present in the settings will automatically be
25void RegisterHotkey(const QString& group, const QString& action, 26 * registered.
26 const QKeySequence& default_keyseq = QKeySequence(), 27 */
27 Qt::ShortcutContext default_context = Qt::WindowShortcut); 28 void LoadHotkeys();
28 29
29/** 30 /**
30 * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. 31 * Saves all registered hotkeys to the settings file.
31 * 32 *
32 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). 33 * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
33 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 34 * settings group will be created to store the key sequence and the hotkey context.
34 * @param widget Parent widget of the returned QShortcut. 35 */
35 * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut 36 void SaveHotkeys();
36 * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. 37
37 */ 38 /**
38QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 39 * Returns a QShortcut object whose activated() signal can be connected to other QObjects'
39 40 * slots.
40/** 41 *
41 * Saves all registered hotkeys to the settings file. 42 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
42 * 43 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
43 * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a 44 * @param widget Parent widget of the returned QShortcut.
44 * settings group will be created to store the key sequence and the hotkey context. 45 * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
45 */ 46 * will be the same. Thus, you shouldn't rely on the caller really being the
46void SaveHotkeys(); 47 * QShortcut's parent.
47 48 */
48/** 49 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
49 * Loads hotkeys from the settings file. 50
50 * 51 /**
51 * @note Yet unregistered hotkeys which are present in the settings will automatically be 52 * Register a hotkey.
52 * registered. 53 *
53 */ 54 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
54void LoadHotkeys(); 55 * @param action Name of the action (e.g. "Start Emulation", "Load Image")
56 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the
57 * settings file before
58 * @param default_context Default context to assign if the hotkey wasn't present in the settings
59 * file before
60 * @warning Both the group and action strings will be displayed in the hotkey settings dialog
61 */
62 void RegisterHotkey(const QString& group, const QString& action,
63 const QKeySequence& default_keyseq = {},
64 Qt::ShortcutContext default_context = Qt::WindowShortcut);
65
66private:
67 struct Hotkey {
68 QKeySequence keyseq;
69 QShortcut* shortcut = nullptr;
70 Qt::ShortcutContext context = Qt::WindowShortcut;
71 };
72
73 using HotkeyMap = std::map<QString, Hotkey>;
74 using HotkeyGroupMap = std::map<QString, HotkeyMap>;
75
76 HotkeyGroupMap hotkey_groups;
77};
55 78
56class GHotkeysDialog : public QWidget { 79class GHotkeysDialog : public QWidget {
57 Q_OBJECT 80 Q_OBJECT
@@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
59public: 82public:
60 explicit GHotkeysDialog(QWidget* parent = nullptr); 83 explicit GHotkeysDialog(QWidget* parent = nullptr);
61 84
85 void Populate(const HotkeyRegistry& registry);
86
62private: 87private:
63 Ui::hotkeys ui; 88 Ui::hotkeys ui;
64}; 89};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index eac41553a..17ed62c72 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -81,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
81 81
82void GMainWindow::ShowCallouts() {} 82void GMainWindow::ShowCallouts() {}
83 83
84const int GMainWindow::max_recent_files_item;
85
84GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 86GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
85 87
86 debug_context = Tegra::DebugContext::Construct(); 88 debug_context = Tegra::DebugContext::Construct();
@@ -206,43 +208,46 @@ void GMainWindow::InitializeRecentFileMenuActions() {
206} 208}
207 209
208void GMainWindow::InitializeHotkeys() { 210void GMainWindow::InitializeHotkeys() {
209 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 211 hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
210 RegisterHotkey("Main Window", "Start Emulation"); 212 hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
211 RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); 213 hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
212 RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); 214 hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
213 RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), 215 hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
214 Qt::ApplicationShortcut); 216 Qt::ApplicationShortcut);
215 RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), 217 hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
216 Qt::ApplicationShortcut); 218 Qt::ApplicationShortcut);
217 LoadHotkeys(); 219 hotkey_registry.LoadHotkeys();
218 220
219 connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, 221 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
220 &GMainWindow::OnMenuLoadFile); 222 this, &GMainWindow::OnMenuLoadFile);
221 connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this, 223 connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
222 &GMainWindow::OnStartGame); 224 &QShortcut::activated, this, &GMainWindow::OnStartGame);
223 connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] { 225 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
224 if (emulation_running) { 226 this, [&] {
225 if (emu_thread->IsRunning()) { 227 if (emulation_running) {
226 OnPauseGame(); 228 if (emu_thread->IsRunning()) {
227 } else { 229 OnPauseGame();
228 OnStartGame(); 230 } else {
229 } 231 OnStartGame();
230 } 232 }
231 }); 233 }
232 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated, 234 });
233 ui.action_Fullscreen, &QAction::trigger); 235 connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
234 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously, 236 &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger);
235 ui.action_Fullscreen, &QAction::trigger); 237 connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
236 connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] { 238 &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger);
237 if (emulation_running) { 239 connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this),
238 ui.action_Fullscreen->setChecked(false); 240 &QShortcut::activated, this, [&] {
239 ToggleFullscreen(); 241 if (emulation_running) {
240 } 242 ui.action_Fullscreen->setChecked(false);
241 }); 243 ToggleFullscreen();
242 connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] { 244 }
243 Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit; 245 });
244 UpdateStatusBar(); 246 connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
245 }); 247 &QShortcut::activated, this, [&] {
248 Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
249 UpdateStatusBar();
250 });
246} 251}
247 252
248void GMainWindow::SetDefaultUIGeometry() { 253void GMainWindow::SetDefaultUIGeometry() {
@@ -321,7 +326,8 @@ void GMainWindow::ConnectMenuEvents() {
321 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 326 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
322 327
323 // Fullscreen 328 // Fullscreen
324 ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); 329 ui.action_Fullscreen->setShortcut(
330 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
325 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 331 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
326 332
327 // Help 333 // Help
@@ -477,7 +483,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
477 483
478 case Core::System::ResultStatus::ErrorVideoCore: 484 case Core::System::ResultStatus::ErrorVideoCore:
479 QMessageBox::critical( 485 QMessageBox::critical(
480 this, tr("An error occured in the video core."), 486 this, tr("An error occurred initializing the video core."),
481 tr("yuzu has encountered an error while running the video core, please see the " 487 tr("yuzu has encountered an error while running the video core, please see the "
482 "log for more details." 488 "log for more details."
483 "For more information on accessing the log, please see the following page: " 489 "For more information on accessing the log, please see the following page: "
@@ -491,7 +497,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
491 default: 497 default:
492 QMessageBox::critical( 498 QMessageBox::critical(
493 this, tr("Error while loading ROM!"), 499 this, tr("Error while loading ROM!"),
494 tr("An unknown error occured. Please see the log for more details.")); 500 tr("An unknown error occurred. Please see the log for more details."));
495 break; 501 break;
496 } 502 }
497 return false; 503 return false;
@@ -579,11 +585,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
579} 585}
580 586
581void GMainWindow::UpdateRecentFiles() { 587void GMainWindow::UpdateRecentFiles() {
582 unsigned int num_recent_files = 588 const int num_recent_files =
583 std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); 589 std::min(UISettings::values.recent_files.size(), max_recent_files_item);
584 590
585 for (unsigned int i = 0; i < num_recent_files; i++) { 591 for (int i = 0; i < num_recent_files; i++) {
586 QString text = QString("&%1. %2").arg(i + 1).arg( 592 const QString text = QString("&%1. %2").arg(i + 1).arg(
587 QFileInfo(UISettings::values.recent_files[i]).fileName()); 593 QFileInfo(UISettings::values.recent_files[i]).fileName());
588 actions_recent_files[i]->setText(text); 594 actions_recent_files[i]->setText(text);
589 actions_recent_files[i]->setData(UISettings::values.recent_files[i]); 595 actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -595,12 +601,8 @@ void GMainWindow::UpdateRecentFiles() {
595 actions_recent_files[j]->setVisible(false); 601 actions_recent_files[j]->setVisible(false);
596 } 602 }
597 603
598 // Grey out the recent files menu if the list is empty 604 // Enable the recent files menu if the list isn't empty
599 if (num_recent_files == 0) { 605 ui.menu_recent_files->setEnabled(num_recent_files != 0);
600 ui.menu_recent_files->setEnabled(false);
601 } else {
602 ui.menu_recent_files->setEnabled(true);
603 }
604} 606}
605 607
606void GMainWindow::OnGameListLoadFile(QString game_path) { 608void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -631,9 +633,15 @@ void GMainWindow::OnMenuLoadFile() {
631} 633}
632 634
633void GMainWindow::OnMenuLoadFolder() { 635void GMainWindow::OnMenuLoadFolder() {
634 QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); 636 const QString dir_path =
637 QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
638
639 if (dir_path.isNull()) {
640 return;
641 }
635 642
636 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 643 const QDir dir{dir_path};
644 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
637 if (matching_main.size() == 1) { 645 if (matching_main.size() == 1) {
638 BootGame(dir.path() + DIR_SEP + matching_main[0]); 646 BootGame(dir.path() + DIR_SEP + matching_main[0]);
639 } else { 647 } else {
@@ -654,9 +662,8 @@ void GMainWindow::OnMenuRecentFile() {
654 QAction* action = qobject_cast<QAction*>(sender()); 662 QAction* action = qobject_cast<QAction*>(sender());
655 assert(action); 663 assert(action);
656 664
657 QString filename = action->data().toString(); 665 const QString filename = action->data().toString();
658 QFileInfo file_info(filename); 666 if (QFileInfo::exists(filename)) {
659 if (file_info.exists()) {
660 BootGame(filename); 667 BootGame(filename);
661 } else { 668 } else {
662 // Display an error message and remove the file from the list. 669 // Display an error message and remove the file from the list.
@@ -754,7 +761,7 @@ void GMainWindow::ToggleWindowMode() {
754} 761}
755 762
756void GMainWindow::OnConfigure() { 763void GMainWindow::OnConfigure() {
757 ConfigureDialog configureDialog(this); 764 ConfigureDialog configureDialog(this, hotkey_registry);
758 auto old_theme = UISettings::values.theme; 765 auto old_theme = UISettings::values.theme;
759 auto result = configureDialog.exec(); 766 auto result = configureDialog.exec();
760 if (result == QDialog::Accepted) { 767 if (result == QDialog::Accepted) {
@@ -893,7 +900,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
893 UISettings::values.first_start = false; 900 UISettings::values.first_start = false;
894 901
895 game_list->SaveInterfaceLayout(); 902 game_list->SaveInterfaceLayout();
896 SaveHotkeys(); 903 hotkey_registry.SaveHotkeys();
897 904
898 // Shutdown session if the emu thread is active... 905 // Shutdown session if the emu thread is active...
899 if (emu_thread != nullptr) 906 if (emu_thread != nullptr)
@@ -947,15 +954,14 @@ void GMainWindow::UpdateUITheme() {
947 QStringList theme_paths(default_theme_paths); 954 QStringList theme_paths(default_theme_paths);
948 if (UISettings::values.theme != UISettings::themes[0].second && 955 if (UISettings::values.theme != UISettings::themes[0].second &&
949 !UISettings::values.theme.isEmpty()) { 956 !UISettings::values.theme.isEmpty()) {
950 QString theme_uri(":" + UISettings::values.theme + "/style.qss"); 957 const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
951 QFile f(theme_uri); 958 QFile f(theme_uri);
952 if (!f.exists()) { 959 if (f.open(QFile::ReadOnly | QFile::Text)) {
953 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
954 } else {
955 f.open(QFile::ReadOnly | QFile::Text);
956 QTextStream ts(&f); 960 QTextStream ts(&f);
957 qApp->setStyleSheet(ts.readAll()); 961 qApp->setStyleSheet(ts.readAll());
958 GMainWindow::setStyleSheet(ts.readAll()); 962 GMainWindow::setStyleSheet(ts.readAll());
963 } else {
964 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
959 } 965 }
960 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); 966 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
961 QIcon::setThemeName(":/icons/" + UISettings::values.theme); 967 QIcon::setThemeName(":/icons/" + UISettings::values.theme);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 074bba3f9..6e335b8f8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -9,6 +9,7 @@
9#include <QTimer> 9#include <QTimer>
10#include "core/core.h" 10#include "core/core.h"
11#include "ui_main.h" 11#include "ui_main.h"
12#include "yuzu/hotkeys.h"
12 13
13class Config; 14class Config;
14class EmuThread; 15class EmuThread;
@@ -43,7 +44,7 @@ public:
43 void filterBarSetChecked(bool state); 44 void filterBarSetChecked(bool state);
44 void UpdateUITheme(); 45 void UpdateUITheme();
45 GMainWindow(); 46 GMainWindow();
46 ~GMainWindow(); 47 ~GMainWindow() override;
47 48
48signals: 49signals:
49 50
@@ -172,6 +173,8 @@ private:
172 // stores default icon theme search paths for the platform 173 // stores default icon theme search paths for the platform
173 QStringList default_theme_paths; 174 QStringList default_theme_paths;
174 175
176 HotkeyRegistry hotkey_registry;
177
175protected: 178protected:
176 void dropEvent(QDropEvent* event) override; 179 void dropEvent(QDropEvent* event) override;
177 void dragEnterEvent(QDragEnterEvent* event) override; 180 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b23213cf6..d637dbd0c 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -193,7 +193,7 @@ int main(int argc, char** argv) {
193 LOG_CRITICAL(Frontend, "Failed to determine system mode!"); 193 LOG_CRITICAL(Frontend, "Failed to determine system mode!");
194 return -1; 194 return -1;
195 case Core::System::ResultStatus::ErrorVideoCore: 195 case Core::System::ResultStatus::ErrorVideoCore:
196 LOG_CRITICAL(Frontend, "VideoCore not initialized"); 196 LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
197 return -1; 197 return -1;
198 case Core::System::ResultStatus::Success: 198 case Core::System::ResultStatus::Success:
199 break; // Expected case 199 break; // Expected case