summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/audio_renderer.cpp89
-rw-r--r--src/audio_core/audio_renderer.h49
-rw-r--r--src/common/hex_util.cpp19
-rw-r--r--src/common/hex_util.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp2
-rw-r--r--src/core/core.cpp7
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp26
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h6
-rw-r--r--src/core/file_sys/ips_layer.cpp270
-rw-r--r--src/core/file_sys/ips_layer.h29
-rw-r--r--src/core/file_sys/patch_manager.cpp107
-rw-r--r--src/core/file_sys/patch_manager.h6
-rw-r--r--src/core/file_sys/romfs.cpp4
-rw-r--r--src/core/file_sys/romfs.h2
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/kernel.cpp12
-rw-r--r--src/core/hle/kernel/kernel.h10
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp24
-rw-r--r--src/core/hle/kernel/process.h28
-rw-r--r--src/core/hle/kernel/scheduler.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp130
-rw-r--r--src/core/hle/kernel/svc_wrap.h56
-rw-r--r--src/core/hle/kernel/thread.cpp13
-rw-r--r--src/core/hle/kernel/thread.h8
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp10
-rw-r--r--src/core/hle/service/audio/hwopus.cpp37
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp72
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp4
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/nro.cpp34
-rw-r--r--src/core/loader/nso.cpp36
-rw-r--r--src/core/loader/nso.h11
-rw-r--r--src/core/loader/nsp.cpp2
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp2
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/core/telemetry_session.h4
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/video_core/engines/fermi_2d.h14
-rw-r--r--src/video_core/engines/maxwell_3d.h41
-rw-r--r--src/video_core/engines/shader_bytecode.h148
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp57
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h46
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp388
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp84
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h7
-rw-r--r--src/video_core/textures/decoders.cpp16
-rw-r--r--src/video_core/textures/decoders.h6
-rw-r--r--src/video_core/textures/texture.h17
-rw-r--r--src/video_core/utils.h24
-rw-r--r--src/yuzu/bootmanager.cpp71
-rw-r--r--src/yuzu/bootmanager.h10
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui23
-rw-r--r--src/yuzu/game_list_worker.cpp11
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp48
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h12
-rw-r--r--src/yuzu_cmd/yuzu.cpp19
72 files changed, 1917 insertions, 446 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 6f0ff953a..23e5d3f10 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -30,7 +30,7 @@ public:
30 return info; 30 return info;
31 } 31 }
32 32
33 VoiceInfo& Info() { 33 VoiceInfo& GetInfo() {
34 return info; 34 return info;
35 } 35 }
36 36
@@ -51,9 +51,30 @@ private:
51 VoiceInfo info{}; 51 VoiceInfo info{};
52}; 52};
53 53
54class AudioRenderer::EffectState {
55public:
56 const EffectOutStatus& GetOutStatus() const {
57 return out_status;
58 }
59
60 const EffectInStatus& GetInfo() const {
61 return info;
62 }
63
64 EffectInStatus& GetInfo() {
65 return info;
66 }
67
68 void UpdateState();
69
70private:
71 EffectOutStatus out_status{};
72 EffectInStatus info{};
73};
54AudioRenderer::AudioRenderer(AudioRendererParameter params, 74AudioRenderer::AudioRenderer(AudioRendererParameter params,
55 Kernel::SharedPtr<Kernel::Event> buffer_event) 75 Kernel::SharedPtr<Kernel::Event> buffer_event)
56 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { 76 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
77 effects(params.effect_count) {
57 78
58 audio_out = std::make_unique<AudioCore::AudioOut>(); 79 audio_out = std::make_unique<AudioCore::AudioOut>();
59 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", 80 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
@@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
96 memory_pool_count * sizeof(MemoryPoolInfo)); 117 memory_pool_count * sizeof(MemoryPoolInfo));
97 118
98 // Copy VoiceInfo structs 119 // Copy VoiceInfo structs
99 std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + 120 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
100 config.voice_resource_size}; 121 config.memory_pools_size + config.voice_resource_size};
101 for (auto& voice : voices) { 122 for (auto& voice : voices) {
102 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); 123 std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
103 offset += sizeof(VoiceInfo); 124 voice_offset += sizeof(VoiceInfo);
125 }
126
127 std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
128 config.memory_pools_size + config.voice_resource_size +
129 config.voices_size};
130 for (auto& effect : effects) {
131 std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
132 effect_offset += sizeof(EffectInStatus);
133 }
134
135 // Update memory pool state
136 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
137 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
138 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
139 memory_pool[index].state = MemoryPoolStates::Attached;
140 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
141 memory_pool[index].state = MemoryPoolStates::Detached;
142 }
104 } 143 }
105 144
106 // Update voices 145 // Update voices
@@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
114 } 153 }
115 } 154 }
116 155
117 // Update memory pool state 156 for (auto& effect : effects) {
118 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); 157 effect.UpdateState();
119 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
120 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
121 memory_pool[index].state = MemoryPoolStates::Attached;
122 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
123 memory_pool[index].state = MemoryPoolStates::Detached;
124 }
125 } 158 }
126 159
127 // Release previous buffers and queue next ones for playback 160 // Release previous buffers and queue next ones for playback
@@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
144 voice_out_status_offset += sizeof(VoiceOutStatus); 177 voice_out_status_offset += sizeof(VoiceOutStatus);
145 } 178 }
146 179
180 std::size_t effect_out_status_offset{
181 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
182 response_data.voice_resource_size};
183 for (const auto& effect : effects) {
184 std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
185 sizeof(EffectOutStatus));
186 effect_out_status_offset += sizeof(EffectOutStatus);
187 }
147 return output_params; 188 return output_params;
148} 189}
149 190
@@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
244 break; 285 break;
245 } 286 }
246 287
247 samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); 288 samples =
289 Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
248 290
249 is_refresh_pending = false; 291 is_refresh_pending = false;
250} 292}
251 293
294void AudioRenderer::EffectState::UpdateState() {
295 if (info.is_new) {
296 out_status.state = EffectStatus::New;
297 } else {
298 if (info.type == Effect::Aux) {
299 ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
300 "Aux buffers tried to update");
301 ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
302 "Aux buffers tried to update");
303 ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
304 "Aux buffers tried to update");
305 ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
306 "Aux buffers tried to update");
307 }
308 }
309}
310
252static constexpr s16 ClampToS16(s32 value) { 311static constexpr s16 ClampToS16(s32 value) {
253 return static_cast<s16>(std::clamp(value, -32768, 32767)); 312 return static_cast<s16>(std::clamp(value, -32768, 32767));
254} 313}
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index dfef89e1d..046417da3 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -28,6 +28,16 @@ enum class PlayState : u8 {
28 Paused = 2, 28 Paused = 2,
29}; 29};
30 30
31enum class Effect : u8 {
32 None = 0,
33 Aux = 2,
34};
35
36enum class EffectStatus : u8 {
37 None = 0,
38 New = 1,
39};
40
31struct AudioRendererParameter { 41struct AudioRendererParameter {
32 u32_le sample_rate; 42 u32_le sample_rate;
33 u32_le sample_count; 43 u32_le sample_count;
@@ -128,6 +138,43 @@ struct VoiceOutStatus {
128}; 138};
129static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); 139static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
130 140
141struct AuxInfo {
142 std::array<u8, 24> input_mix_buffers;
143 std::array<u8, 24> output_mix_buffers;
144 u32_le mix_buffer_count;
145 u32_le sample_rate; // Stored in the aux buffer currently
146 u32_le sampe_count;
147 u64_le send_buffer_info;
148 u64_le send_buffer_base;
149
150 u64_le return_buffer_info;
151 u64_le return_buffer_base;
152};
153static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
154
155struct EffectInStatus {
156 Effect type;
157 u8 is_new;
158 u8 is_enabled;
159 INSERT_PADDING_BYTES(1);
160 u32_le mix_id;
161 u64_le buffer_base;
162 u64_le buffer_sz;
163 s32_le priority;
164 INSERT_PADDING_BYTES(4);
165 union {
166 std::array<u8, 0xa0> raw;
167 AuxInfo aux_info;
168 };
169};
170static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
171
172struct EffectOutStatus {
173 EffectStatus state;
174 INSERT_PADDING_BYTES(0xf);
175};
176static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
177
131struct UpdateDataHeader { 178struct UpdateDataHeader {
132 UpdateDataHeader() {} 179 UpdateDataHeader() {}
133 180
@@ -173,11 +220,13 @@ public:
173 Stream::State GetStreamState() const; 220 Stream::State GetStreamState() const;
174 221
175private: 222private:
223 class EffectState;
176 class VoiceState; 224 class VoiceState;
177 225
178 AudioRendererParameter worker_params; 226 AudioRendererParameter worker_params;
179 Kernel::SharedPtr<Kernel::Event> buffer_event; 227 Kernel::SharedPtr<Kernel::Event> buffer_event;
180 std::vector<VoiceState> voices; 228 std::vector<VoiceState> voices;
229 std::vector<EffectState> effects;
181 std::unique_ptr<AudioOut> audio_out; 230 std::unique_ptr<AudioOut> audio_out;
182 AudioCore::StreamPtr stream; 231 AudioCore::StreamPtr stream;
183}; 232};
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index 589ae5cbf..5b63f9e81 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) {
18 return 0; 18 return 0;
19} 19}
20 20
21std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
22 std::vector<u8> out(str.size() / 2);
23 if (little_endian) {
24 for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
25 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
26 } else {
27 for (std::size_t i = 0; i < str.size(); i += 2)
28 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
29 }
30 return out;
31}
32
33std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
34 std::string out;
35 for (u8 c : vector)
36 out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
37 return out;
38}
39
21std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { 40std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
22 if (len != 32) { 41 if (len != 32) {
23 LOG_ERROR(Common, 42 LOG_ERROR(Common,
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 863a5ccd9..68f003cb6 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <vector>
10#include <fmt/format.h> 11#include <fmt/format.h>
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
@@ -14,6 +15,8 @@ namespace Common {
14 15
15u8 ToHexNibble(char c1); 16u8 ToHexNibble(char c1);
16 17
18std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
19
17template <std::size_t Size, bool le = false> 20template <std::size_t Size, bool le = false>
18std::array<u8, Size> HexStringToArray(std::string_view str) { 21std::array<u8, Size> HexStringToArray(std::string_view str) {
19 std::array<u8, Size> out{}; 22 std::array<u8, Size> out{};
@@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
27 return out; 30 return out;
28} 31}
29 32
33std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
34
30template <std::size_t Size> 35template <std::size_t Size>
31std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { 36std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
32 std::string out; 37 std::string out;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 7e978cf7a..0762321a9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -129,7 +129,7 @@ public:
129}; 129};
130 130
131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
132 auto& current_process = Core::CurrentProcess(); 132 auto* current_process = Core::CurrentProcess();
133 auto** const page_table = current_process->VMManager().page_table.pointers.data(); 133 auto** const page_table = current_process->VMManager().page_table.pointers.data();
134 134
135 Dynarmic::A64::UserConfig config; 135 Dynarmic::A64::UserConfig config;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b6acfb3e4..e2fb9e038 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -136,7 +136,8 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 136 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 138
139 kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main")); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get());
140 141
141 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_shared<CpuBarrier>();
142 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
@@ -361,11 +362,11 @@ const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_ind
361 return impl->cpu_cores[core_index]->Scheduler(); 362 return impl->cpu_cores[core_index]->Scheduler();
362} 363}
363 364
364Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { 365Kernel::Process* System::CurrentProcess() {
365 return impl->kernel.CurrentProcess(); 366 return impl->kernel.CurrentProcess();
366} 367}
367 368
368const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { 369const Kernel::Process* System::CurrentProcess() const {
369 return impl->kernel.CurrentProcess(); 370 return impl->kernel.CurrentProcess();
370} 371}
371 372
diff --git a/src/core/core.h b/src/core/core.h
index f9a3e97e3..ea4d53914 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -174,11 +174,11 @@ public:
174 /// Gets the scheduler for the CPU core with the specified index 174 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); 175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
176 176
177 /// Provides a reference to the current process 177 /// Provides a pointer to the current process
178 Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); 178 Kernel::Process* CurrentProcess();
179 179
180 /// Provides a constant reference to the current process. 180 /// Provides a constant pointer to the current process.
181 const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const; 181 const Kernel::Process* CurrentProcess() const;
182 182
183 /// Provides a reference to the kernel instance. 183 /// Provides a reference to the kernel instance.
184 Kernel::KernelCore& Kernel(); 184 Kernel::KernelCore& Kernel();
@@ -246,7 +246,7 @@ inline TelemetrySession& Telemetry() {
246 return System::GetInstance().TelemetrySession(); 246 return System::GetInstance().TelemetrySession();
247} 247}
248 248
249inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { 249inline Kernel::Process* CurrentProcess() {
250 return System::GetInstance().CurrentProcess(); 250 return System::GetInstance().CurrentProcess();
251} 251}
252 252
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 2a913ce82..47b7526c7 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -26,6 +26,7 @@
26#include "common/alignment.h" 26#include "common/alignment.h"
27#include "common/assert.h" 27#include "common/assert.h"
28#include "core/file_sys/fsmitm_romfsbuild.h" 28#include "core/file_sys/fsmitm_romfsbuild.h"
29#include "core/file_sys/ips_layer.h"
29#include "core/file_sys/vfs.h" 30#include "core/file_sys/vfs.h"
30#include "core/file_sys/vfs_vector.h" 31#include "core/file_sys/vfs_vector.h"
31 32
@@ -123,7 +124,7 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
123 return count; 124 return count;
124} 125}
125 126
126void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, 127void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
127 std::shared_ptr<RomFSBuildDirectoryContext> parent) { 128 std::shared_ptr<RomFSBuildDirectoryContext> parent) {
128 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; 129 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
129 130
@@ -144,6 +145,9 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
144 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); 145 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
145 child->path = parent->path + "/" + kv.first; 146 child->path = parent->path + "/" + kv.first;
146 147
148 if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
149 continue;
150
147 // Sanity check on path_len 151 // Sanity check on path_len
148 ASSERT(child->path_len < FS_MAX_PATH); 152 ASSERT(child->path_len < FS_MAX_PATH);
149 153
@@ -157,11 +161,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
157 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); 161 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
158 child->path = parent->path + "/" + kv.first; 162 child->path = parent->path + "/" + kv.first;
159 163
164 if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
165 continue;
166
160 // Sanity check on path_len 167 // Sanity check on path_len
161 ASSERT(child->path_len < FS_MAX_PATH); 168 ASSERT(child->path_len < FS_MAX_PATH);
162 169
163 child->source = root_romfs->GetFileRelative(child->path); 170 child->source = root_romfs->GetFileRelative(child->path);
164 171
172 if (ext != nullptr) {
173 const auto ips = ext->GetFileRelative(child->path + ".ips");
174
175 if (ips != nullptr) {
176 auto patched = PatchIPS(child->source, ips);
177 if (patched != nullptr)
178 child->source = std::move(patched);
179 }
180 }
181
165 child->size = child->source->GetSize(); 182 child->size = child->source->GetSize();
166 183
167 AddFile(parent, child); 184 AddFile(parent, child);
@@ -169,7 +186,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
169 } 186 }
170 187
171 for (auto& child : child_dirs) { 188 for (auto& child : child_dirs) {
172 this->VisitDirectory(root_romfs, child); 189 this->VisitDirectory(root_romfs, ext, child);
173 } 190 }
174} 191}
175 192
@@ -208,14 +225,15 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare
208 return true; 225 return true;
209} 226}
210 227
211RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { 228RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
229 : base(std::move(base_)), ext(std::move(ext_)) {
212 root = std::make_shared<RomFSBuildDirectoryContext>(); 230 root = std::make_shared<RomFSBuildDirectoryContext>();
213 root->path = "\0"; 231 root->path = "\0";
214 directories.emplace(root->path, root); 232 directories.emplace(root->path, root);
215 num_dirs = 1; 233 num_dirs = 1;
216 dir_table_size = 0x18; 234 dir_table_size = 0x18;
217 235
218 VisitDirectory(base, root); 236 VisitDirectory(base, ext, root);
219} 237}
220 238
221RomFSBuildContext::~RomFSBuildContext() = default; 239RomFSBuildContext::~RomFSBuildContext() = default;
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index b0c3c123b..3d377b0af 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -40,7 +40,7 @@ struct RomFSFileEntry;
40 40
41class RomFSBuildContext { 41class RomFSBuildContext {
42public: 42public:
43 explicit RomFSBuildContext(VirtualDir base); 43 explicit RomFSBuildContext(VirtualDir base, VirtualDir ext = nullptr);
44 ~RomFSBuildContext(); 44 ~RomFSBuildContext();
45 45
46 // This finalizes the context. 46 // This finalizes the context.
@@ -48,6 +48,7 @@ public:
48 48
49private: 49private:
50 VirtualDir base; 50 VirtualDir base;
51 VirtualDir ext;
51 std::shared_ptr<RomFSBuildDirectoryContext> root; 52 std::shared_ptr<RomFSBuildDirectoryContext> root;
52 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; 53 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
53 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; 54 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
@@ -59,7 +60,8 @@ private:
59 u64 file_hash_table_size = 0; 60 u64 file_hash_table_size = 0;
60 u64 file_partition_size = 0; 61 u64 file_partition_size = 0;
61 62
62 void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent); 63 void VisitDirectory(VirtualDir filesys, VirtualDir ext,
64 std::shared_ptr<RomFSBuildDirectoryContext> parent);
63 65
64 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 66 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
65 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); 67 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index df933ee36..554eae9bc 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -2,7 +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 "common/assert.h" 5#include <algorithm>
6#include <cstring>
7#include <map>
8#include <sstream>
9#include <string>
10#include <utility>
11
12#include "common/hex_util.h"
13#include "common/logging/log.h"
6#include "common/swap.h" 14#include "common/swap.h"
7#include "core/file_sys/ips_layer.h" 15#include "core/file_sys/ips_layer.h"
8#include "core/file_sys/vfs_vector.h" 16#include "core/file_sys/vfs_vector.h"
@@ -15,16 +23,48 @@ enum class IPSFileType {
15 Error, 23 Error,
16}; 24};
17 25
26constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
27 {"\\a", "\a"},
28 {"\\b", "\b"},
29 {"\\f", "\f"},
30 {"\\n", "\n"},
31 {"\\r", "\r"},
32 {"\\t", "\t"},
33 {"\\v", "\v"},
34 {"\\\\", "\\"},
35 {"\\\'", "\'"},
36 {"\\\"", "\""},
37 {"\\\?", "\?"},
38}};
39
18static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { 40static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
19 if (magic.size() != 5) 41 if (magic.size() != 5) {
20 return IPSFileType::Error; 42 return IPSFileType::Error;
21 if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) 43 }
44
45 constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
46 if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
22 return IPSFileType::IPS; 47 return IPSFileType::IPS;
23 if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) 48 }
49
50 constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
51 if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
24 return IPSFileType::IPS32; 52 return IPSFileType::IPS32;
53 }
54
25 return IPSFileType::Error; 55 return IPSFileType::Error;
26} 56}
27 57
58static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
59 constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
60 if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
61 return true;
62 }
63
64 constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
65 return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
66}
67
28VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { 68VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
29 if (in == nullptr || ips == nullptr) 69 if (in == nullptr || ips == nullptr)
30 return nullptr; 70 return nullptr;
@@ -39,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
39 u64 offset = 5; // After header 79 u64 offset = 5; // After header
40 while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { 80 while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
41 offset += temp.size(); 81 offset += temp.size();
42 if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || 82 if (IsEOF(type, temp)) {
43 type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) {
44 break; 83 break;
45 } 84 }
46 85
@@ -68,21 +107,232 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
68 return nullptr; 107 return nullptr;
69 108
70 if (real_offset + rle_size > in_data.size()) 109 if (real_offset + rle_size > in_data.size())
71 rle_size = in_data.size() - real_offset; 110 rle_size = static_cast<u16>(in_data.size() - real_offset);
72 std::memset(in_data.data() + real_offset, data.get(), rle_size); 111 std::memset(in_data.data() + real_offset, data.get(), rle_size);
73 } else { // Standard Patch 112 } else { // Standard Patch
74 auto read = data_size; 113 auto read = data_size;
75 if (real_offset + read > in_data.size()) 114 if (real_offset + read > in_data.size())
76 read = in_data.size() - real_offset; 115 read = static_cast<u16>(in_data.size() - real_offset);
77 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) 116 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
78 return nullptr; 117 return nullptr;
79 offset += data_size; 118 offset += data_size;
80 } 119 }
81 } 120 }
82 121
83 if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) 122 if (!IsEOF(type, temp)) {
84 return nullptr; 123 return nullptr;
85 return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); 124 }
125
126 return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
127 in->GetContainingDirectory());
128}
129
130struct IPSwitchCompiler::IPSwitchPatch {
131 std::string name;
132 bool enabled;
133 std::map<u32, std::vector<u8>> records;
134};
135
136IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
137 Parse();
138}
139
140IPSwitchCompiler::~IPSwitchCompiler() = default;
141
142std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
143 return nso_build_id;
144}
145
146bool IPSwitchCompiler::IsValid() const {
147 return valid;
148}
149
150static bool StartsWith(std::string_view base, std::string_view check) {
151 return base.size() >= check.size() && base.substr(0, check.size()) == check;
152}
153
154static std::string EscapeStringSequences(std::string in) {
155 for (const auto& seq : ESCAPE_CHARACTER_MAP) {
156 for (auto index = in.find(seq.first); index != std::string::npos;
157 index = in.find(seq.first, index)) {
158 in.replace(index, std::strlen(seq.first), seq.second);
159 index += std::strlen(seq.second);
160 }
161 }
162
163 return in;
164}
165
166void IPSwitchCompiler::ParseFlag(const std::string& line) {
167 if (StartsWith(line, "@flag offset_shift ")) {
168 // Offset Shift Flag
169 offset_shift = std::stoll(line.substr(19), nullptr, 0);
170 } else if (StartsWith(line, "@little-endian")) {
171 // Set values to read as little endian
172 is_little_endian = true;
173 } else if (StartsWith(line, "@big-endian")) {
174 // Set values to read as big endian
175 is_little_endian = false;
176 } else if (StartsWith(line, "@flag print_values")) {
177 // Force printing of applied values
178 print_values = true;
179 }
180}
181
182void IPSwitchCompiler::Parse() {
183 const auto bytes = patch_text->ReadAllBytes();
184 std::stringstream s;
185 s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
186
187 std::vector<std::string> lines;
188 std::string stream_line;
189 while (std::getline(s, stream_line)) {
190 // Remove a trailing \r
191 if (!stream_line.empty() && stream_line.back() == '\r')
192 stream_line.pop_back();
193 lines.push_back(std::move(stream_line));
194 }
195
196 for (std::size_t i = 0; i < lines.size(); ++i) {
197 auto line = lines[i];
198
199 // Remove midline comments
200 std::size_t comment_index = std::string::npos;
201 bool within_string = false;
202 for (std::size_t k = 0; k < line.size(); ++k) {
203 if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
204 within_string = !within_string;
205 } else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
206 comment_index = k;
207 break;
208 }
209 }
210
211 if (!StartsWith(line, "//") && comment_index != std::string::npos) {
212 last_comment = line.substr(comment_index + 2);
213 line = line.substr(0, comment_index);
214 }
215
216 if (StartsWith(line, "@stop")) {
217 // Force stop
218 break;
219 } else if (StartsWith(line, "@nsobid-")) {
220 // NSO Build ID Specifier
221 auto raw_build_id = line.substr(8);
222 if (raw_build_id.size() != 0x40)
223 raw_build_id.resize(0x40, '0');
224 nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
225 } else if (StartsWith(line, "#")) {
226 // Mandatory Comment
227 LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
228 patch_text->GetName(), line.substr(1));
229 } else if (StartsWith(line, "//")) {
230 // Normal Comment
231 last_comment = line.substr(2);
232 if (last_comment.find_first_not_of(' ') == std::string::npos)
233 continue;
234 if (last_comment.find_first_not_of(' ') != 0)
235 last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
236 } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
237 // Start of patch
238 const auto enabled = StartsWith(line, "@enabled");
239 if (i == 0)
240 return;
241 LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
242 patch_text->GetName(), last_comment, line.substr(1));
243
244 IPSwitchPatch patch{last_comment, enabled, {}};
245
246 // Read rest of patch
247 while (true) {
248 if (i + 1 >= lines.size())
249 break;
250 const auto patch_line = lines[++i];
251
252 // Start of new patch
253 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
254 --i;
255 break;
256 }
257
258 // Check for a flag
259 if (StartsWith(patch_line, "@")) {
260 ParseFlag(patch_line);
261 continue;
262 }
263
264 // 11 - 8 hex digit offset + space + minimum two digit overwrite val
265 if (patch_line.length() < 11)
266 break;
267 auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16);
268 offset += static_cast<unsigned long>(offset_shift);
269
270 std::vector<u8> replace;
271 // 9 - first char of replacement val
272 if (patch_line[9] == '\"') {
273 // string replacement
274 auto end_index = patch_line.find('\"', 10);
275 if (end_index == std::string::npos || end_index < 10)
276 return;
277 while (patch_line[end_index - 1] == '\\') {
278 end_index = patch_line.find('\"', end_index + 1);
279 if (end_index == std::string::npos || end_index < 10)
280 return;
281 }
282
283 auto value = patch_line.substr(10, end_index - 10);
284 value = EscapeStringSequences(value);
285 replace.reserve(value.size());
286 std::copy(value.begin(), value.end(), std::back_inserter(replace));
287 } else {
288 // hex replacement
289 const auto value = patch_line.substr(9);
290 replace.reserve(value.size() / 2);
291 replace = Common::HexStringToVector(value, is_little_endian);
292 }
293
294 if (print_values) {
295 LOG_INFO(Loader,
296 "[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
297 "with byte string '{}'",
298 patch_text->GetName(), offset, Common::HexVectorToString(replace));
299 }
300
301 patch.records.insert_or_assign(offset, std::move(replace));
302 }
303
304 patches.push_back(std::move(patch));
305 } else if (StartsWith(line, "@")) {
306 ParseFlag(line);
307 }
308 }
309
310 valid = true;
311}
312
313VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
314 if (in == nullptr || !valid)
315 return nullptr;
316
317 auto in_data = in->ReadAllBytes();
318
319 for (const auto& patch : patches) {
320 if (!patch.enabled)
321 continue;
322
323 for (const auto& record : patch.records) {
324 if (record.first >= in_data.size())
325 continue;
326 auto replace_size = record.second.size();
327 if (record.first + replace_size > in_data.size())
328 replace_size = in_data.size() - record.first;
329 for (std::size_t i = 0; i < replace_size; ++i)
330 in_data[i + record.first] = record.second[i];
331 }
332 }
333
334 return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
335 in->GetContainingDirectory());
86} 336}
87 337
88} // namespace FileSys 338} // namespace FileSys
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h
index 81c163494..450b2f71e 100644
--- a/src/core/file_sys/ips_layer.h
+++ b/src/core/file_sys/ips_layer.h
@@ -4,12 +4,41 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <memory> 8#include <memory>
9#include <vector>
8 10
11#include "common/common_types.h"
9#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
10 13
11namespace FileSys { 14namespace FileSys {
12 15
13VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); 16VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips);
14 17
18class IPSwitchCompiler {
19public:
20 explicit IPSwitchCompiler(VirtualFile patch_text);
21 ~IPSwitchCompiler();
22
23 std::array<u8, 0x20> GetBuildID() const;
24 bool IsValid() const;
25 VirtualFile Apply(const VirtualFile& in) const;
26
27private:
28 struct IPSwitchPatch;
29
30 void ParseFlag(const std::string& flag);
31 void Parse();
32
33 bool valid = false;
34
35 VirtualFile patch_text;
36 std::vector<IPSwitchPatch> patches;
37 std::array<u8, 0x20> nso_build_id{};
38 bool is_little_endian = false;
39 s64 offset_shift = 0;
40 bool print_values = false;
41 std::string last_comment = "";
42};
43
15} // namespace FileSys 44} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 1ac00ebb0..019caebe9 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -73,27 +73,38 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
73 return exefs; 73 return exefs;
74} 74}
75 75
76static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, 76static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
77 const std::string& build_id) { 77 const std::string& build_id) {
78 std::vector<VirtualFile> ips; 78 std::vector<VirtualFile> out;
79 ips.reserve(patch_dirs.size()); 79 out.reserve(patch_dirs.size());
80 for (const auto& subdir : patch_dirs) { 80 for (const auto& subdir : patch_dirs) {
81 auto exefs_dir = subdir->GetSubdirectory("exefs"); 81 auto exefs_dir = subdir->GetSubdirectory("exefs");
82 if (exefs_dir != nullptr) { 82 if (exefs_dir != nullptr) {
83 for (const auto& file : exefs_dir->GetFiles()) { 83 for (const auto& file : exefs_dir->GetFiles()) {
84 if (file->GetExtension() != "ips") 84 if (file->GetExtension() == "ips") {
85 continue; 85 auto name = file->GetName();
86 auto name = file->GetName(); 86 const auto p1 = name.substr(0, name.find('.'));
87 const auto p1 = name.substr(0, name.find('.')); 87 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
88 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); 88
89 89 if (build_id == this_build_id)
90 if (build_id == this_build_id) 90 out.push_back(file);
91 ips.push_back(file); 91 } else if (file->GetExtension() == "pchtxt") {
92 IPSwitchCompiler compiler{file};
93 if (!compiler.IsValid())
94 continue;
95
96 auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
97 this_build_id =
98 this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
99
100 if (build_id == this_build_id)
101 out.push_back(file);
102 }
92 } 103 }
93 } 104 }
94 } 105 }
95 106
96 return ips; 107 return out;
97} 108}
98 109
99std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 110std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
@@ -115,15 +126,24 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
115 auto patch_dirs = load_dir->GetSubdirectories(); 126 auto patch_dirs = load_dir->GetSubdirectories();
116 std::sort(patch_dirs.begin(), patch_dirs.end(), 127 std::sort(patch_dirs.begin(), patch_dirs.end(),
117 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 128 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
118 const auto ips = CollectIPSPatches(patch_dirs, build_id); 129 const auto patches = CollectPatches(patch_dirs, build_id);
119 130
120 auto out = nso; 131 auto out = nso;
121 for (const auto& ips_file : ips) { 132 for (const auto& patch_file : patches) {
122 LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", 133 if (patch_file->GetExtension() == "ips") {
123 ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); 134 LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"",
124 const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); 135 patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
125 if (patched != nullptr) 136 const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file);
126 out = patched->ReadAllBytes(); 137 if (patched != nullptr)
138 out = patched->ReadAllBytes();
139 } else if (patch_file->GetExtension() == "pchtxt") {
140 LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"",
141 patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
142 const IPSwitchCompiler compiler{patch_file};
143 const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out));
144 if (patched != nullptr)
145 out = patched->ReadAllBytes();
146 }
127 } 147 }
128 148
129 if (out.size() < 0x100) 149 if (out.size() < 0x100)
@@ -143,7 +163,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
143 std::sort(patch_dirs.begin(), patch_dirs.end(), 163 std::sort(patch_dirs.begin(), patch_dirs.end(),
144 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 164 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
145 165
146 return !CollectIPSPatches(patch_dirs, build_id).empty(); 166 return !CollectPatches(patch_dirs, build_id).empty();
147} 167}
148 168
149static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
@@ -162,11 +182,17 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
162 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 182 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
163 183
164 std::vector<VirtualDir> layers; 184 std::vector<VirtualDir> layers;
185 std::vector<VirtualDir> layers_ext;
165 layers.reserve(patch_dirs.size() + 1); 186 layers.reserve(patch_dirs.size() + 1);
187 layers_ext.reserve(patch_dirs.size() + 1);
166 for (const auto& subdir : patch_dirs) { 188 for (const auto& subdir : patch_dirs) {
167 auto romfs_dir = subdir->GetSubdirectory("romfs"); 189 auto romfs_dir = subdir->GetSubdirectory("romfs");
168 if (romfs_dir != nullptr) 190 if (romfs_dir != nullptr)
169 layers.push_back(std::move(romfs_dir)); 191 layers.push_back(std::move(romfs_dir));
192
193 auto ext_dir = subdir->GetSubdirectory("romfs_ext");
194 if (ext_dir != nullptr)
195 layers_ext.push_back(std::move(ext_dir));
170 } 196 }
171 layers.push_back(std::move(extracted)); 197 layers.push_back(std::move(extracted));
172 198
@@ -175,7 +201,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
175 return; 201 return;
176 } 202 }
177 203
178 auto packed = CreateRomFS(std::move(layered)); 204 auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext));
205
206 auto packed = CreateRomFS(std::move(layered), std::move(layered_ext));
179 if (packed == nullptr) { 207 if (packed == nullptr) {
180 return; 208 return;
181 } 209 }
@@ -263,8 +291,24 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
263 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 291 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
264 for (const auto& mod : mod_dir->GetSubdirectories()) { 292 for (const auto& mod : mod_dir->GetSubdirectories()) {
265 std::string types; 293 std::string types;
266 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) 294
267 AppendCommaIfNotEmpty(types, "IPS"); 295 const auto exefs_dir = mod->GetSubdirectory("exefs");
296 if (IsDirValidAndNonEmpty(exefs_dir)) {
297 bool ips = false;
298 bool ipswitch = false;
299
300 for (const auto& file : exefs_dir->GetFiles()) {
301 if (file->GetExtension() == "ips")
302 ips = true;
303 else if (file->GetExtension() == "pchtxt")
304 ipswitch = true;
305 }
306
307 if (ips)
308 AppendCommaIfNotEmpty(types, "IPS");
309 if (ipswitch)
310 AppendCommaIfNotEmpty(types, "IPSwitch");
311 }
268 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 312 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
269 AppendCommaIfNotEmpty(types, "LayeredFS"); 313 AppendCommaIfNotEmpty(types, "LayeredFS");
270 314
@@ -301,23 +345,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
301 return out; 345 return out;
302} 346}
303 347
304std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
305 const auto& installed{Service::FileSystem::GetUnionContents()}; 349 const auto& installed{Service::FileSystem::GetUnionContents()};
306 350
307 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
308 if (base_control_nca == nullptr) 352 if (base_control_nca == nullptr)
309 return {}; 353 return {};
310 354
311 return ParseControlNCA(base_control_nca); 355 return ParseControlNCA(*base_control_nca);
312} 356}
313 357
314std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( 358std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
315 const std::shared_ptr<NCA>& nca) const { 359 const auto base_romfs = nca.GetRomFS();
316 const auto base_romfs = nca->GetRomFS();
317 if (base_romfs == nullptr) 360 if (base_romfs == nullptr)
318 return {}; 361 return {};
319 362
320 const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); 363 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
321 if (romfs == nullptr) 364 if (romfs == nullptr)
322 return {}; 365 return {};
323 366
@@ -329,7 +372,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
329 if (nacp_file == nullptr) 372 if (nacp_file == nullptr)
330 nacp_file = extracted->GetFile("Control.nacp"); 373 nacp_file = extracted->GetFile("Control.nacp");
331 374
332 const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); 375 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
333 376
334 VirtualFile icon_file; 377 VirtualFile icon_file;
335 for (const auto& language : FileSys::LANGUAGE_NAMES) { 378 for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -338,6 +381,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
338 break; 381 break;
339 } 382 }
340 383
341 return {nacp, icon_file}; 384 return {std::move(nacp), icon_file};
342} 385}
343} // namespace FileSys 386} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 2ae9322a1..7d168837f 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -36,6 +36,7 @@ public:
36 36
37 // Currently tracked NSO patches: 37 // Currently tracked NSO patches:
38 // - IPS 38 // - IPS
39 // - IPSwitch
39 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; 40 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
40 41
41 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 42 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
@@ -56,11 +57,10 @@ public:
56 57
57 // Given title_id of the program, attempts to get the control data of the update and parse it, 58 // Given title_id of the program, attempts to get the control data of the update and parse it,
58 // falling back to the base control data. 59 // falling back to the base control data.
59 std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; 60 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
60 61
61 // Version of GetControlMetadata that takes an arbitrary NCA 62 // Version of GetControlMetadata that takes an arbitrary NCA
62 std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( 63 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
63 const std::shared_ptr<NCA>& nca) const;
64 64
65private: 65private:
66 u64 title_id; 66 u64 title_id;
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 5910f7046..81e1f66ac 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -129,11 +129,11 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
129 return out; 129 return out;
130} 130}
131 131
132VirtualFile CreateRomFS(VirtualDir dir) { 132VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
133 if (dir == nullptr) 133 if (dir == nullptr)
134 return nullptr; 134 return nullptr;
135 135
136 RomFSBuildContext ctx{dir}; 136 RomFSBuildContext ctx{dir, ext};
137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); 137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
138} 138}
139 139
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index ecd1eb725..0ec404731 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -44,6 +44,6 @@ VirtualDir ExtractRomFS(VirtualFile file,
44 44
45// Converts a VFS filesystem into a RomFS binary 45// Converts a VFS filesystem into a RomFS binary
46// Returns nullptr on failure 46// Returns nullptr on failure
47VirtualFile CreateRomFS(VirtualDir dir); 47VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext = nullptr);
48 48
49} // namespace FileSys 49} // namespace FileSys
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e5fa67ae8..885259618 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -22,6 +22,7 @@ enum {
22 HandleTableFull = 105, 22 HandleTableFull = 105,
23 InvalidMemoryState = 106, 23 InvalidMemoryState = 106,
24 InvalidMemoryPermissions = 108, 24 InvalidMemoryPermissions = 108,
25 InvalidMemoryRange = 110,
25 InvalidThreadPriority = 112, 26 InvalidThreadPriority = 112,
26 InvalidProcessorId = 113, 27 InvalidProcessorId = 113,
27 InvalidHandle = 114, 28 InvalidHandle = 114,
@@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA
56constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 57constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
57constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 58constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
58 ErrCodes::InvalidMemoryPermissions); 59 ErrCodes::InvalidMemoryPermissions);
60constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
59constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 61constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
60constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 62constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
61constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 63constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 98eb74298..bd680adfe 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -116,7 +116,7 @@ struct KernelCore::Impl {
116 next_thread_id = 1; 116 next_thread_id = 1;
117 117
118 process_list.clear(); 118 process_list.clear();
119 current_process.reset(); 119 current_process = nullptr;
120 120
121 handle_table.Clear(); 121 handle_table.Clear();
122 resource_limits.fill(nullptr); 122 resource_limits.fill(nullptr);
@@ -207,7 +207,7 @@ struct KernelCore::Impl {
207 207
208 // Lists all processes that exist in the current session. 208 // Lists all processes that exist in the current session.
209 std::vector<SharedPtr<Process>> process_list; 209 std::vector<SharedPtr<Process>> process_list;
210 SharedPtr<Process> current_process; 210 Process* current_process = nullptr;
211 211
212 Kernel::HandleTable handle_table; 212 Kernel::HandleTable handle_table;
213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
@@ -266,15 +266,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
266 impl->process_list.push_back(std::move(process)); 266 impl->process_list.push_back(std::move(process));
267} 267}
268 268
269void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) { 269void KernelCore::MakeCurrentProcess(Process* process) {
270 impl->current_process = std::move(process); 270 impl->current_process = process;
271} 271}
272 272
273SharedPtr<Process>& KernelCore::CurrentProcess() { 273Process* KernelCore::CurrentProcess() {
274 return impl->current_process; 274 return impl->current_process;
275} 275}
276 276
277const SharedPtr<Process>& KernelCore::CurrentProcess() const { 277const Process* KernelCore::CurrentProcess() const {
278 return impl->current_process; 278 return impl->current_process;
279} 279}
280 280
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c0771ecf0..41554821f 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -66,13 +66,13 @@ public:
66 void AppendNewProcess(SharedPtr<Process> process); 66 void AppendNewProcess(SharedPtr<Process> process);
67 67
68 /// Makes the given process the new current process. 68 /// Makes the given process the new current process.
69 void MakeCurrentProcess(SharedPtr<Process> process); 69 void MakeCurrentProcess(Process* process);
70 70
71 /// Retrieves a reference to the current process. 71 /// Retrieves a pointer to the current process.
72 SharedPtr<Process>& CurrentProcess(); 72 Process* CurrentProcess();
73 73
74 /// Retrieves a const reference to the current process. 74 /// Retrieves a const pointer to the current process.
75 const SharedPtr<Process>& CurrentProcess() const; 75 const Process* CurrentProcess() const;
76 76
77 /// Adds a port to the named port table 77 /// Adds a port to the named port table
78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port); 78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
25 case HandleType::Process: 25 case HandleType::Process:
26 case HandleType::AddressArbiter: 26 case HandleType::AddressArbiter:
27 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
28 case HandleType::CodeSet:
29 case HandleType::ClientPort: 28 case HandleType::ClientPort:
30 case HandleType::ClientSession: 29 case HandleType::ClientSession:
31 return false; 30 return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
26 AddressArbiter, 26 AddressArbiter,
27 Timer, 27 Timer,
28 ResourceLimit, 28 ResourceLimit,
29 CodeSet,
30 ClientPort, 29 ClientPort,
31 ServerPort, 30 ServerPort,
32 ClientSession, 31 ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..c80b2c507 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
20 20
21namespace Kernel { 21namespace Kernel {
22 22
23SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) { 23CodeSet::CodeSet() = default;
24 SharedPtr<CodeSet> codeset(new CodeSet(kernel));
25 codeset->name = std::move(name);
26 return codeset;
27}
28
29CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
30CodeSet::~CodeSet() = default; 24CodeSet::~CodeSet() = default;
31 25
32SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { 26SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -224,20 +218,20 @@ void Process::FreeTLSSlot(VAddr tls_address) {
224 tls_slots[tls_page].reset(tls_slot); 218 tls_slots[tls_page].reset(tls_slot);
225} 219}
226 220
227void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 221void Process::LoadModule(CodeSet module_, VAddr base_addr) {
228 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 222 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
229 MemoryState memory_state) { 223 MemoryState memory_state) {
230 auto vma = vm_manager 224 const auto vma = vm_manager
231 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, 225 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
232 segment.size, memory_state) 226 segment.offset, segment.size, memory_state)
233 .Unwrap(); 227 .Unwrap();
234 vm_manager.Reprotect(vma, permissions); 228 vm_manager.Reprotect(vma, permissions);
235 }; 229 };
236 230
237 // Map CodeSet segments 231 // Map CodeSet segments
238 MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
239 MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
240 MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
241} 235}
242 236
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 237ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..73ec01e11 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -61,26 +61,15 @@ enum class ProcessStatus { Created, Running, Exited };
61 61
62class ResourceLimit; 62class ResourceLimit;
63 63
64struct CodeSet final : public Object { 64struct CodeSet final {
65 struct Segment { 65 struct Segment {
66 std::size_t offset = 0; 66 std::size_t offset = 0;
67 VAddr addr = 0; 67 VAddr addr = 0;
68 u32 size = 0; 68 u32 size = 0;
69 }; 69 };
70 70
71 static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name); 71 explicit CodeSet();
72 72 ~CodeSet();
73 std::string GetTypeName() const override {
74 return "CodeSet";
75 }
76 std::string GetName() const override {
77 return name;
78 }
79
80 static const HandleType HANDLE_TYPE = HandleType::CodeSet;
81 HandleType GetHandleType() const override {
82 return HANDLE_TYPE;
83 }
84 73
85 Segment& CodeSegment() { 74 Segment& CodeSegment() {
86 return segments[0]; 75 return segments[0];
@@ -109,14 +98,7 @@ struct CodeSet final : public Object {
109 std::shared_ptr<std::vector<u8>> memory; 98 std::shared_ptr<std::vector<u8>> memory;
110 99
111 std::array<Segment, 3> segments; 100 std::array<Segment, 3> segments;
112 VAddr entrypoint; 101 VAddr entrypoint = 0;
113
114 /// Name of the process
115 std::string name;
116
117private:
118 explicit CodeSet(KernelCore& kernel);
119 ~CodeSet() override;
120}; 102};
121 103
122class Process final : public Object { 104class Process final : public Object {
@@ -219,7 +201,7 @@ public:
219 */ 201 */
220 void PrepareForTermination(); 202 void PrepareForTermination();
221 203
222 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); 204 void LoadModule(CodeSet module_, VAddr base_addr);
223 205
224 /////////////////////////////////////////////////////////////////////////////////////////////// 206 ///////////////////////////////////////////////////////////////////////////////////////////////
225 // Memory Management 207 // Memory Management
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index cfd6e1bad..1342c597e 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -9,7 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/core_timing.h" 12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
15 15
@@ -78,16 +78,16 @@ void Scheduler::SwitchContext(Thread* new_thread) {
78 // Cancel any outstanding wakeup events for this thread 78 // Cancel any outstanding wakeup events for this thread
79 new_thread->CancelWakeupTimer(); 79 new_thread->CancelWakeupTimer();
80 80
81 auto previous_process = Core::CurrentProcess(); 81 auto* const previous_process = Core::CurrentProcess();
82 82
83 current_thread = new_thread; 83 current_thread = new_thread;
84 84
85 ready_queue.remove(new_thread->GetPriority(), new_thread); 85 ready_queue.remove(new_thread->GetPriority(), new_thread);
86 new_thread->SetStatus(ThreadStatus::Running); 86 new_thread->SetStatus(ThreadStatus::Running);
87 87
88 const auto thread_owner_process = current_thread->GetOwnerProcess(); 88 auto* const thread_owner_process = current_thread->GetOwnerProcess();
89 if (previous_process != thread_owner_process) { 89 if (previous_process != thread_owner_process) {
90 Core::CurrentProcess() = thread_owner_process; 90 Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
91 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); 91 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
92 } 92 }
93 93
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6c4af7e47..e406df829 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -39,6 +39,73 @@ namespace {
39constexpr bool Is4KBAligned(VAddr address) { 39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0; 40 return (address & 0xFFF) == 0;
41} 41}
42
43// Checks if address + size is greater than the given address
44// This can return false if the size causes an overflow of a 64-bit type
45// or if the given size is zero.
46constexpr bool IsValidAddressRange(VAddr address, u64 size) {
47 return address + size > address;
48}
49
50// Checks if a given address range lies within a larger address range.
51constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
52 VAddr address_range_end) {
53 const VAddr end_address = address + size - 1;
54 return address_range_begin <= address && end_address <= address_range_end - 1;
55}
56
57bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
58 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
59 vm.GetAddressSpaceEndAddress());
60}
61
62bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
63 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
64 vm.GetNewMapRegionEndAddress());
65}
66
67// Helper function that performs the common sanity checks for svcMapMemory
68// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
69// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS;
74 }
75
76 if (size == 0 || !Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE;
78 }
79
80 if (!IsValidAddressRange(dst_addr, size)) {
81 return ERR_INVALID_ADDRESS_STATE;
82 }
83
84 if (!IsValidAddressRange(src_addr, size)) {
85 return ERR_INVALID_ADDRESS_STATE;
86 }
87
88 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
89 return ERR_INVALID_ADDRESS_STATE;
90 }
91
92 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
93 return ERR_INVALID_MEMORY_RANGE;
94 }
95
96 const VAddr dst_end_address = dst_addr + size;
97 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
98 vm_manager.GetHeapRegionEndAddress() > dst_addr) {
99 return ERR_INVALID_MEMORY_RANGE;
100 }
101
102 if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
103 vm_manager.GetMapRegionEndAddress() > dst_addr) {
104 return ERR_INVALID_MEMORY_RANGE;
105 }
106
107 return RESULT_SUCCESS;
108}
42} // Anonymous namespace 109} // Anonymous namespace
43 110
44/// Set the process heap to a given Size. It can both extend and shrink the heap. 111/// Set the process heap to a given Size. It can both extend and shrink the heap.
@@ -69,15 +136,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
69 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 136 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
70 src_addr, size); 137 src_addr, size);
71 138
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 139 auto* const current_process = Core::CurrentProcess();
73 return ERR_INVALID_ADDRESS; 140 const auto& vm_manager = current_process->VMManager();
74 }
75 141
76 if (size == 0 || !Is4KBAligned(size)) { 142 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
77 return ERR_INVALID_SIZE; 143 if (result != RESULT_SUCCESS) {
144 return result;
78 } 145 }
79 146
80 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 147 return current_process->MirrorMemory(dst_addr, src_addr, size);
81} 148}
82 149
83/// Unmaps a region that was previously mapped with svcMapMemory 150/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
85 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 152 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
86 src_addr, size); 153 src_addr, size);
87 154
88 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 155 auto* const current_process = Core::CurrentProcess();
89 return ERR_INVALID_ADDRESS; 156 const auto& vm_manager = current_process->VMManager();
90 }
91 157
92 if (size == 0 || !Is4KBAligned(size)) { 158 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
93 return ERR_INVALID_SIZE; 159 if (result != RESULT_SUCCESS) {
160 return result;
94 } 161 }
95 162
96 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 163 return current_process->UnmapMemory(dst_addr, src_addr, size);
97} 164}
98 165
99/// Connect to an OS service given the port name, returns the handle to the port to out 166/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -301,13 +368,28 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
301 return Mutex::Release(mutex_addr); 368 return Mutex::Release(mutex_addr);
302} 369}
303 370
371struct BreakReason {
372 union {
373 u32 raw;
374 BitField<31, 1, u32> signal_debugger;
375 };
376};
377
304/// Break program execution 378/// Break program execution
305static void Break(u64 reason, u64 info1, u64 info2) { 379static void Break(u32 reason, u64 info1, u64 info2) {
306 LOG_CRITICAL( 380 BreakReason break_reason{reason};
307 Debug_Emulated, 381 if (break_reason.signal_debugger) {
308 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 382 LOG_ERROR(
309 reason, info1, info2); 383 Debug_Emulated,
310 ASSERT(false); 384 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
385 reason, info1, info2);
386 } else {
387 LOG_CRITICAL(
388 Debug_Emulated,
389 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
390 reason, info1, info2);
391 ASSERT(false);
392 }
311} 393}
312 394
313/// Used to output a message on a debug hardware unit - does nothing on a retail unit 395/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -326,7 +408,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
326 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 408 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
327 info_sub_id, handle); 409 info_sub_id, handle);
328 410
329 const auto& current_process = Core::CurrentProcess(); 411 const auto* current_process = Core::CurrentProcess();
330 const auto& vm_manager = current_process->VMManager(); 412 const auto& vm_manager = current_process->VMManager();
331 413
332 switch (static_cast<GetInfoType>(info_id)) { 414 switch (static_cast<GetInfoType>(info_id)) {
@@ -424,7 +506,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
424 return ERR_INVALID_HANDLE; 506 return ERR_INVALID_HANDLE;
425 } 507 }
426 508
427 const auto current_process = Core::CurrentProcess(); 509 const auto* current_process = Core::CurrentProcess();
428 if (thread->GetOwnerProcess() != current_process) { 510 if (thread->GetOwnerProcess() != current_process) {
429 return ERR_INVALID_HANDLE; 511 return ERR_INVALID_HANDLE;
430 } 512 }
@@ -516,7 +598,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
516 return ERR_INVALID_HANDLE; 598 return ERR_INVALID_HANDLE;
517 } 599 }
518 600
519 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, 601 return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
520 MemoryPermission::DontCare); 602 MemoryPermission::DontCare);
521} 603}
522 604
@@ -535,7 +617,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
535 auto& kernel = Core::System::GetInstance().Kernel(); 617 auto& kernel = Core::System::GetInstance().Kernel();
536 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 618 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
537 619
538 return shared_memory->Unmap(Core::CurrentProcess().get(), addr); 620 return shared_memory->Unmap(Core::CurrentProcess(), addr);
539} 621}
540 622
541/// Query process memory 623/// Query process memory
@@ -573,7 +655,7 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
573 655
574/// Exits the current process 656/// Exits the current process
575static void ExitProcess() { 657static void ExitProcess() {
576 auto& current_process = Core::CurrentProcess(); 658 auto* current_process = Core::CurrentProcess();
577 659
578 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 660 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
579 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 661 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -621,7 +703,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
621 auto& kernel = Core::System::GetInstance().Kernel(); 703 auto& kernel = Core::System::GetInstance().Kernel();
622 CASCADE_RESULT(SharedPtr<Thread> thread, 704 CASCADE_RESULT(SharedPtr<Thread> thread,
623 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 705 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
624 Core::CurrentProcess())); 706 *Core::CurrentProcess()));
625 const auto new_guest_handle = kernel.HandleTable().Create(thread); 707 const auto new_guest_handle = kernel.HandleTable().Create(thread);
626 if (new_guest_handle.Failed()) { 708 if (new_guest_handle.Failed()) {
627 return new_guest_handle.Code(); 709 return new_guest_handle.Code();
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 22712e64f..cbb80c3c4 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -35,18 +35,18 @@ void SvcWrap() {
35 35
36template <ResultCode func(u32)> 36template <ResultCode func(u32)>
37void SvcWrap() { 37void SvcWrap() {
38 FuncReturn(func((u32)Param(0)).raw); 38 FuncReturn(func(static_cast<u32>(Param(0))).raw);
39} 39}
40 40
41template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
42void SvcWrap() { 42void SvcWrap() {
43 FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); 43 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
44} 44}
45 45
46template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
47void SvcWrap() { 47void SvcWrap() {
48 u32 param_1 = 0; 48 u32 param_1 = 0;
49 u32 retval = func(&param_1, (u32)Param(1)).raw; 49 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
50 Core::CurrentArmInterface().SetReg(1, param_1); 50 Core::CurrentArmInterface().SetReg(1, param_1);
51 FuncReturn(retval); 51 FuncReturn(retval);
52} 52}
@@ -61,7 +61,7 @@ void SvcWrap() {
61 61
62template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
63void SvcWrap() { 63void SvcWrap() {
64 FuncReturn(func(Param(0), (s32)Param(1)).raw); 64 FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
65} 65}
66 66
67template <ResultCode func(u64, u32)> 67template <ResultCode func(u64, u32)>
@@ -79,19 +79,19 @@ void SvcWrap() {
79 79
80template <ResultCode func(u32, u64)> 80template <ResultCode func(u32, u64)>
81void SvcWrap() { 81void SvcWrap() {
82 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); 82 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
83} 83}
84 84
85template <ResultCode func(u32, u32, u64)> 85template <ResultCode func(u32, u32, u64)>
86void SvcWrap() { 86void SvcWrap() {
87 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); 87 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
88} 88}
89 89
90template <ResultCode func(u32, u32*, u64*)> 90template <ResultCode func(u32, u32*, u64*)>
91void SvcWrap() { 91void SvcWrap() {
92 u32 param_1 = 0; 92 u32 param_1 = 0;
93 u64 param_2 = 0; 93 u64 param_2 = 0;
94 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2); 94 ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
95 Core::CurrentArmInterface().SetReg(1, param_1); 95 Core::CurrentArmInterface().SetReg(1, param_1);
96 Core::CurrentArmInterface().SetReg(2, param_2); 96 Core::CurrentArmInterface().SetReg(2, param_2);
97 FuncReturn(retval.raw); 97 FuncReturn(retval.raw);
@@ -100,12 +100,12 @@ void SvcWrap() {
100template <ResultCode func(u64, u64, u32, u32)> 100template <ResultCode func(u64, u64, u32, u32)>
101void SvcWrap() { 101void SvcWrap() {
102 FuncReturn( 102 FuncReturn(
103 func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); 103 func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
104} 104}
105 105
106template <ResultCode func(u32, u64, u32)> 106template <ResultCode func(u32, u64, u32)>
107void SvcWrap() { 107void SvcWrap() {
108 FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); 108 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
109} 109}
110 110
111template <ResultCode func(u64, u64, u64)> 111template <ResultCode func(u64, u64, u64)>
@@ -115,25 +115,28 @@ void SvcWrap() {
115 115
116template <ResultCode func(u32, u64, u64, u32)> 116template <ResultCode func(u32, u64, u64, u32)>
117void SvcWrap() { 117void SvcWrap() {
118 FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); 118 FuncReturn(
119 func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
119} 120}
120 121
121template <ResultCode func(u32, u64, u64)> 122template <ResultCode func(u32, u64, u64)>
122void SvcWrap() { 123void SvcWrap() {
123 FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); 124 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
124} 125}
125 126
126template <ResultCode func(u32*, u64, u64, s64)> 127template <ResultCode func(u32*, u64, u64, s64)>
127void SvcWrap() { 128void SvcWrap() {
128 u32 param_1 = 0; 129 u32 param_1 = 0;
129 ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); 130 ResultCode retval =
131 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
130 Core::CurrentArmInterface().SetReg(1, param_1); 132 Core::CurrentArmInterface().SetReg(1, param_1);
131 FuncReturn(retval.raw); 133 FuncReturn(retval.raw);
132} 134}
133 135
134template <ResultCode func(u64, u64, u32, s64)> 136template <ResultCode func(u64, u64, u32, s64)>
135void SvcWrap() { 137void SvcWrap() {
136 FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); 138 FuncReturn(
139 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
137} 140}
138 141
139template <ResultCode func(u64*, u64, u64, u64)> 142template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +150,9 @@ void SvcWrap() {
147template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 150template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
148void SvcWrap() { 151void SvcWrap() {
149 u32 param_1 = 0; 152 u32 param_1 = 0;
150 u32 retval = 153 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
151 func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) 154 static_cast<s32>(Param(5)))
152 .raw; 155 .raw;
153 Core::CurrentArmInterface().SetReg(1, param_1); 156 Core::CurrentArmInterface().SetReg(1, param_1);
154 FuncReturn(retval); 157 FuncReturn(retval);
155} 158}
@@ -172,7 +175,7 @@ void SvcWrap() {
172template <ResultCode func(u32*, u64, u64, u32)> 175template <ResultCode func(u32*, u64, u64, u32)>
173void SvcWrap() { 176void SvcWrap() {
174 u32 param_1 = 0; 177 u32 param_1 = 0;
175 u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; 178 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
176 Core::CurrentArmInterface().SetReg(1, param_1); 179 Core::CurrentArmInterface().SetReg(1, param_1);
177 FuncReturn(retval); 180 FuncReturn(retval);
178} 181}
@@ -181,22 +184,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
181void SvcWrap() { 184void SvcWrap() {
182 u32 param_1 = 0; 185 u32 param_1 = 0;
183 u32 retval = 186 u32 retval =
184 func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; 187 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
185 Core::CurrentArmInterface().SetReg(1, param_1); 188 Core::CurrentArmInterface().SetReg(1, param_1);
186 FuncReturn(retval); 189 FuncReturn(retval);
187} 190}
188 191
189template <ResultCode func(u64, u32, s32, s64)> 192template <ResultCode func(u64, u32, s32, s64)>
190void SvcWrap() { 193void SvcWrap() {
191 FuncReturn( 194 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
192 func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) 195 static_cast<s64>(Param(3)))
193 .raw); 196 .raw);
194} 197}
195 198
196template <ResultCode func(u64, u32, s32, s32)> 199template <ResultCode func(u64, u32, s32, s32)>
197void SvcWrap() { 200void SvcWrap() {
198 FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), 201 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
199 (s32)(Param(3) & 0xFFFFFFFF)) 202 static_cast<s32>(Param(3)))
200 .raw); 203 .raw);
201} 204}
202 205
@@ -226,7 +229,7 @@ void SvcWrap() {
226 229
227template <void func(s64)> 230template <void func(s64)>
228void SvcWrap() { 231void SvcWrap() {
229 func((s64)Param(0)); 232 func(static_cast<s64>(Param(0)));
230} 233}
231 234
232template <void func(u64, u64 len)> 235template <void func(u64, u64 len)>
@@ -239,4 +242,9 @@ void SvcWrap() {
239 func(Param(0), Param(1), Param(2)); 242 func(Param(0), Param(1), Param(2));
240} 243}
241 244
245template <void func(u32, u64, u64)>
246void SvcWrap() {
247 func(static_cast<u32>(Param(0)), Param(1), Param(2));
248}
249
242} // namespace Kernel 250} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 8e514cf9a..352ce1725 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -183,18 +183,15 @@ void Thread::ResumeFromWait() {
183 */ 183 */
184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, 184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
185 VAddr entry_point, u64 arg) { 185 VAddr entry_point, u64 arg) {
186 memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext)); 186 context = {};
187
188 context.cpu_registers[0] = arg; 187 context.cpu_registers[0] = arg;
189 context.pc = entry_point; 188 context.pc = entry_point;
190 context.sp = stack_top; 189 context.sp = stack_top;
191 context.pstate = 0;
192 context.fpcr = 0;
193} 190}
194 191
195ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 192ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
196 u32 priority, u64 arg, s32 processor_id, 193 u32 priority, u64 arg, s32 processor_id,
197 VAddr stack_top, SharedPtr<Process> owner_process) { 194 VAddr stack_top, Process& owner_process) {
198 // Check if priority is in ranged. Lowest priority -> highest priority id. 195 // Check if priority is in ranged. Lowest priority -> highest priority id.
199 if (priority > THREADPRIO_LOWEST) { 196 if (priority > THREADPRIO_LOWEST) {
200 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 197 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -208,7 +205,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
208 205
209 // TODO(yuriks): Other checks, returning 0xD9001BEA 206 // TODO(yuriks): Other checks, returning 0xD9001BEA
210 207
211 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { 208 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
212 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 209 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
213 // TODO (bunnei): Find the correct error code to use here 210 // TODO (bunnei): Find the correct error code to use here
214 return ResultCode(-1); 211 return ResultCode(-1);
@@ -232,7 +229,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
232 thread->wait_handle = 0; 229 thread->wait_handle = 0;
233 thread->name = std::move(name); 230 thread->name = std::move(name);
234 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
235 thread->owner_process = owner_process; 232 thread->owner_process = &owner_process;
236 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get();
237 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
238 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -264,7 +261,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
264 // Initialize new "main" thread 261 // Initialize new "main" thread
265 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 262 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
266 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, 263 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
267 stack_top, &owner_process); 264 stack_top, owner_process);
268 265
269 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 266 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
270 267
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c6ffbd28c..f4d7bd235 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -89,7 +89,7 @@ public:
89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name, 89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
90 VAddr entry_point, u32 priority, u64 arg, 90 VAddr entry_point, u32 priority, u64 arg,
91 s32 processor_id, VAddr stack_top, 91 s32 processor_id, VAddr stack_top,
92 SharedPtr<Process> owner_process); 92 Process& owner_process);
93 93
94 std::string GetName() const override { 94 std::string GetName() const override {
95 return name; 95 return name;
@@ -262,11 +262,11 @@ public:
262 return processor_id; 262 return processor_id;
263 } 263 }
264 264
265 SharedPtr<Process>& GetOwnerProcess() { 265 Process* GetOwnerProcess() {
266 return owner_process; 266 return owner_process;
267 } 267 }
268 268
269 const SharedPtr<Process>& GetOwnerProcess() const { 269 const Process* GetOwnerProcess() const {
270 return owner_process; 270 return owner_process;
271 } 271 }
272 272
@@ -386,7 +386,7 @@ private:
386 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. 386 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
387 387
388 /// Process that owns this thread 388 /// Process that owns this thread
389 SharedPtr<Process> owner_process; 389 Process* owner_process;
390 390
391 /// Objects that the thread is waiting on, in the same order as they were 391 /// Objects that the thread is waiting on, in the same order as they were
392 /// passed to WaitSynchronization1/N. 392 /// passed to WaitSynchronization1/N.
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 79580bcd9..0ecfb5af1 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -61,13 +61,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
61AOC_U::~AOC_U() = default; 61AOC_U::~AOC_U() = default;
62 62
63void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { 63void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
64 IPC::ResponseBuilder rb{ctx, 4}; 64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS); 65 rb.Push(RESULT_SUCCESS);
66 66
67 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 67 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
68 rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [&current](u64 tid) { 68 rb.Push<u32>(static_cast<u32>(
69 return (tid & DLC_BASE_TITLE_ID_MASK) == current; 69 std::count_if(add_on_content.begin(), add_on_content.end(),
70 })); 70 [&current](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; })));
71} 71}
72 72
73void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 73void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -91,7 +91,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
91 return; 91 return;
92 } 92 }
93 93
94 count = std::min<size_t>(out.size() - offset, count); 94 count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
95 std::rotate(out.begin(), out.begin() + offset, out.end()); 95 std::rotate(out.begin(), out.begin() + offset, out.end());
96 out.resize(count); 96 out.resize(count);
97 97
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fc6067e59..7168c6a10 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,8 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
5#include <cstring> 6#include <cstring>
6#include <memory> 7#include <memory>
8#include <optional>
7#include <vector> 9#include <vector>
8 10
9#include <opus.h> 11#include <opus.h>
@@ -33,7 +35,8 @@ public:
33 {1, nullptr, "SetContext"}, 35 {1, nullptr, "SetContext"},
34 {2, nullptr, "DecodeInterleavedForMultiStream"}, 36 {2, nullptr, "DecodeInterleavedForMultiStream"},
35 {3, nullptr, "SetContextForMultiStream"}, 37 {3, nullptr, "SetContextForMultiStream"},
36 {4, nullptr, "Unknown4"}, 38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
39 "DecodeInterleavedWithPerformance"},
37 {5, nullptr, "Unknown5"}, 40 {5, nullptr, "Unknown5"},
38 {6, nullptr, "Unknown6"}, 41 {6, nullptr, "Unknown6"},
39 {7, nullptr, "Unknown7"}, 42 {7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
59 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 62 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
60 } 63 }
61 64
62 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 65 void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
63 std::vector<opus_int16>& output) { 66 u32 consumed = 0;
67 u32 sample_count = 0;
68 u64 performance = 0;
69 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
70 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
71 performance)) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 // TODO(ogniK): Use correct error code
74 rb.Push(ResultCode(-1));
75 return;
76 }
77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 }
84
85 bool Decoder_DecodeInterleaved(
86 u32& consumed, u32& sample_count, const std::vector<u8>& input,
87 std::vector<opus_int16>& output,
88 std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
89 const auto start_time = std::chrono::high_resolution_clock::now();
64 std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 90 std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
65 if (sizeof(OpusHeader) > input.size()) 91 if (sizeof(OpusHeader) > input.size())
66 return false; 92 return false;
@@ -80,8 +106,13 @@ private:
80 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); 106 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
81 if (out_sample_count < 0) 107 if (out_sample_count < 0)
82 return false; 108 return false;
109 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
83 sample_count = out_sample_count; 110 sample_count = out_sample_count;
84 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 111 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
112 if (performance_time.has_value()) {
113 performance_time->get() =
114 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
115 }
85 return true; 116 return true;
86 } 117 }
87 118
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 d8b8037a8..c41ef7058 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -15,6 +15,11 @@
15#include "video_core/renderer_base.h" 15#include "video_core/renderer_base.h"
16 16
17namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
18namespace NvErrCodes {
19enum {
20 InvalidNmapHandle = -22,
21};
22}
18 23
19nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 24nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
20nvhost_as_gpu::~nvhost_as_gpu() = default; 25nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
79 std::memcpy(entries.data(), input.data(), input.size()); 84 std::memcpy(entries.data(), input.data(), input.size());
80 85
81 auto& gpu = Core::System::GetInstance().GPU(); 86 auto& gpu = Core::System::GetInstance().GPU();
82
83 for (const auto& entry : entries) { 87 for (const auto& entry : entries) {
84 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 88 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
85 entry.offset, entry.nvmap_handle, entry.pages); 89 entry.offset, entry.nvmap_handle, entry.pages);
86 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 90 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
87
88 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 91 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
89 ASSERT(object); 92 if (!object) {
93 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
94 std::memcpy(output.data(), entries.data(), output.size());
95 return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
96 }
90 97
91 ASSERT(object->status == nvmap::Object::Status::Allocated); 98 ASSERT(object->status == nvmap::Object::Status::Allocated);
92 99
@@ -157,15 +164,21 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
157 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 164 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
158 165
159 const auto itr = buffer_mappings.find(params.offset); 166 const auto itr = buffer_mappings.find(params.offset);
160 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); 167 if (itr == buffer_mappings.end()) {
168 LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset);
169 // Hardware tests shows that unmapping an already unmapped buffer always returns successful
170 // and doesn't fail.
171 return 0;
172 }
161 173
162 auto& system_instance = Core::System::GetInstance(); 174 auto& system_instance = Core::System::GetInstance();
163 175
164 // Remove this memory region from the rasterizer cache. 176 // Remove this memory region from the rasterizer cache.
165 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
166 itr->second.size);
167
168 auto& gpu = system_instance.GPU(); 177 auto& gpu = system_instance.GPU();
178 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
179 ASSERT(cpu_addr);
180 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
181
169 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 182 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
170 183
171 buffer_mappings.erase(itr->second.offset); 184 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a2287cc1b..43651d8a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,13 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
14nvmap::nvmap() = default; 21nvmap::nvmap() = default;
15nvmap::~nvmap() = default; 22nvmap::~nvmap() = default;
16 23
@@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
44u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
45 IocCreateParams params; 52 IocCreateParams params;
46 std::memcpy(&params, input.data(), sizeof(params)); 53 std::memcpy(&params, input.data(), sizeof(params));
54 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
47 55
56 if (!params.size) {
57 return static_cast<u32>(NvErrCodes::InvalidValue);
58 }
48 // Create a new nvmap object and obtain a handle to it. 59 // Create a new nvmap object and obtain a handle to it.
49 auto object = std::make_shared<Object>(); 60 auto object = std::make_shared<Object>();
50 object->id = next_id++; 61 object->id = next_id++;
@@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
55 u32 handle = next_handle++; 66 u32 handle = next_handle++;
56 handles[handle] = std::move(object); 67 handles[handle] = std::move(object);
57 68
58 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
59
60 params.handle = handle; 69 params.handle = handle;
61 70
62 std::memcpy(output.data(), &params, sizeof(params)); 71 std::memcpy(output.data(), &params, sizeof(params));
@@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
66u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 75u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
67 IocAllocParams params; 76 IocAllocParams params;
68 std::memcpy(&params, input.data(), sizeof(params)); 77 std::memcpy(&params, input.data(), sizeof(params));
78 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
79
80 if (!params.handle) {
81 return static_cast<u32>(NvErrCodes::InvalidValue);
82 }
83
84 if ((params.align - 1) & params.align) {
85 return static_cast<u32>(NvErrCodes::InvalidValue);
86 }
87
88 const u32 min_alignment = 0x1000;
89 if (params.align < min_alignment) {
90 params.align = min_alignment;
91 }
69 92
70 auto object = GetObject(params.handle); 93 auto object = GetObject(params.handle);
71 ASSERT(object); 94 if (!object) {
95 return static_cast<u32>(NvErrCodes::InvalidValue);
96 }
97
98 if (object->status == Object::Status::Allocated) {
99 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
100 }
72 101
73 object->flags = params.flags; 102 object->flags = params.flags;
74 object->align = params.align; 103 object->align = params.align;
@@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
76 object->addr = params.addr; 105 object->addr = params.addr;
77 object->status = Object::Status::Allocated; 106 object->status = Object::Status::Allocated;
78 107
79 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
80
81 std::memcpy(output.data(), &params, sizeof(params)); 108 std::memcpy(output.data(), &params, sizeof(params));
82 return 0; 109 return 0;
83} 110}
@@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
88 115
89 LOG_WARNING(Service_NVDRV, "called"); 116 LOG_WARNING(Service_NVDRV, "called");
90 117
118 if (!params.handle) {
119 return static_cast<u32>(NvErrCodes::InvalidValue);
120 }
121
91 auto object = GetObject(params.handle); 122 auto object = GetObject(params.handle);
92 ASSERT(object); 123 if (!object) {
124 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
125 }
93 126
94 params.id = object->id; 127 params.id = object->id;
95 128
@@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
105 138
106 auto itr = std::find_if(handles.begin(), handles.end(), 139 auto itr = std::find_if(handles.begin(), handles.end(),
107 [&](const auto& entry) { return entry.second->id == params.id; }); 140 [&](const auto& entry) { return entry.second->id == params.id; });
108 ASSERT(itr != handles.end()); 141 if (itr == handles.end()) {
142 return static_cast<u32>(NvErrCodes::InvalidValue);
143 }
144
145 auto& object = itr->second;
146 if (object->status != Object::Status::Allocated) {
147 return static_cast<u32>(NvErrCodes::InvalidValue);
148 }
109 149
110 itr->second->refcount++; 150 itr->second->refcount++;
111 151
@@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
125 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); 165 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
126 166
127 auto object = GetObject(params.handle); 167 auto object = GetObject(params.handle);
128 ASSERT(object); 168 if (!object) {
129 ASSERT(object->status == Object::Status::Allocated); 169 return static_cast<u32>(NvErrCodes::InvalidValue);
170 }
171
172 if (object->status != Object::Status::Allocated) {
173 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
174 }
130 175
131 switch (static_cast<ParamTypes>(params.param)) { 176 switch (static_cast<ParamTypes>(params.param)) {
132 case ParamTypes::Size: 177 case ParamTypes::Size:
@@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 208 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
164 209
165 auto itr = handles.find(params.handle); 210 auto itr = handles.find(params.handle);
166 ASSERT(itr != handles.end()); 211 if (itr == handles.end()) {
167 212 return static_cast<u32>(NvErrCodes::InvalidValue);
168 ASSERT(itr->second->refcount > 0); 213 }
214 if (!itr->second->refcount) {
215 return static_cast<u32>(NvErrCodes::InvalidValue);
216 }
169 217
170 itr->second->refcount--; 218 itr->second->refcount--;
171 219
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 9a86e5824..951fd8257 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <cstring>
6#include "common/common_funcs.h" 7#include "common/common_funcs.h"
7#include "common/file_util.h" 8#include "common/file_util.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
@@ -140,7 +141,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
140 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
141 if (module_file != nullptr) { 142 if (module_file != nullptr) {
142 const VAddr load_addr = next_load_addr; 143 const VAddr load_addr = next_load_addr;
143 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); 144 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
145 std::strcmp(module, "rtld") == 0, pm);
144 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 146 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
145 // Register module with GDBStub 147 // Register module with GDBStub
146 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); 148 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index e67b49fc9..6057c7f26 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,16 +9,11 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 13#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/elf.h" 14#include "core/loader/elf.h"
17#include "core/memory.h" 15#include "core/memory.h"
18 16
19using Kernel::CodeSet;
20using Kernel::SharedPtr;
21
22//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
23// ELF Header Constants 18// ELF Header Constants
24 19
@@ -211,7 +206,7 @@ public:
211 u32 GetFlags() const { 206 u32 GetFlags() const {
212 return (u32)(header->e_flags); 207 return (u32)(header->e_flags);
213 } 208 }
214 SharedPtr<CodeSet> LoadInto(VAddr vaddr); 209 Kernel::CodeSet LoadInto(VAddr vaddr);
215 210
216 int GetNumSegments() const { 211 int GetNumSegments() const {
217 return (int)(header->e_phnum); 212 return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
274 return nullptr; 269 return nullptr;
275} 270}
276 271
277SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { 272Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
278 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 273 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
279 274
280 // Should we relocate? 275 // Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
302 std::vector<u8> program_image(total_image_size); 297 std::vector<u8> program_image(total_image_size);
303 std::size_t current_image_position = 0; 298 std::size_t current_image_position = 0;
304 299
305 auto& kernel = Core::System::GetInstance().Kernel(); 300 Kernel::CodeSet codeset;
306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
307 301
308 for (unsigned int i = 0; i < header->e_phnum; ++i) { 302 for (unsigned int i = 0; i < header->e_phnum; ++i) {
309 const Elf32_Phdr* p = &segments[i]; 303 const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
311 p->p_vaddr, p->p_filesz, p->p_memsz); 305 p->p_vaddr, p->p_filesz, p->p_memsz);
312 306
313 if (p->p_type == PT_LOAD) { 307 if (p->p_type == PT_LOAD) {
314 CodeSet::Segment* codeset_segment; 308 Kernel::CodeSet::Segment* codeset_segment;
315 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 309 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
316 if (permission_flags == (PF_R | PF_X)) { 310 if (permission_flags == (PF_R | PF_X)) {
317 codeset_segment = &codeset->CodeSegment(); 311 codeset_segment = &codeset.CodeSegment();
318 } else if (permission_flags == (PF_R)) { 312 } else if (permission_flags == (PF_R)) {
319 codeset_segment = &codeset->RODataSegment(); 313 codeset_segment = &codeset.RODataSegment();
320 } else if (permission_flags == (PF_R | PF_W)) { 314 } else if (permission_flags == (PF_R | PF_W)) {
321 codeset_segment = &codeset->DataSegment(); 315 codeset_segment = &codeset.DataSegment();
322 } else { 316 } else {
323 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 317 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
324 p->p_flags); 318 p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
345 } 339 }
346 } 340 }
347 341
348 codeset->entrypoint = base_addr + header->e_entry; 342 codeset.entrypoint = base_addr + header->e_entry;
349 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 343 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
350 344
351 LOG_DEBUG(Loader, "Done loading."); 345 LOG_DEBUG(Loader, "Done loading.");
352 346
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
397 391
398 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 392 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
399 ElfReader elf_reader(&buffer[0]); 393 ElfReader elf_reader(&buffer[0]);
400 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); 394 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
401 codeset->name = file->GetName(); 395 const VAddr entry_point = codeset.entrypoint;
402 396
403 process.LoadModule(codeset, codeset->entrypoint); 397 process.LoadModule(std::move(codeset), entry_point);
404 process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); 398 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
405 399
406 is_loaded = true; 400 is_loaded = true;
407 return ResultStatus::Success; 401 return ResultStatus::Success;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index c10f826a4..576fe692a 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,11 +14,12 @@
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
20#include "core/loader/nro.h" 19#include "core/loader/nro.h"
20#include "core/loader/nso.h"
21#include "core/memory.h" 21#include "core/memory.h"
22#include "core/settings.h"
22 23
23namespace Loader { 24namespace Loader {
24 25
@@ -137,17 +138,29 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
137 } 138 }
138 139
139 // Build program image 140 // Build program image
140 auto& kernel = Core::System::GetInstance().Kernel();
141 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
142 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); 141 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
143 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
144 return {}; 143 return {};
145 } 144 }
146 145
146 Kernel::CodeSet codeset;
147 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { 147 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
148 codeset->segments[i].addr = nro_header.segments[i].offset; 148 codeset.segments[i].addr = nro_header.segments[i].offset;
149 codeset->segments[i].offset = nro_header.segments[i].offset; 149 codeset.segments[i].offset = nro_header.segments[i].offset;
150 codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); 150 codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
151 }
152
153 if (!Settings::values.program_args.empty()) {
154 const auto arg_data = Settings::values.program_args;
155 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
156 NSOArgumentHeader args_header{
157 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
158 const auto end_offset = program_image.size();
159 program_image.resize(static_cast<u32>(program_image.size()) +
160 NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
161 std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
162 std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
163 arg_data.size());
151 } 164 }
152 165
153 // Read MOD header 166 // Read MOD header
@@ -161,16 +174,15 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
161 // Resize program image to include .bss section and page align each section 174 // Resize program image to include .bss section and page align each section
162 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 175 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
163 } 176 }
164 codeset->DataSegment().size += bss_size; 177 codeset.DataSegment().size += bss_size;
165 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 178 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
166 179
167 // Load codeset for current process 180 // Load codeset for current process
168 codeset->name = file->GetName(); 181 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
169 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
170 Core::CurrentProcess()->LoadModule(codeset, load_base);
171 183
172 // Register module with GDBStub 184 // Register module with GDBStub
173 GDBStub::RegisterModule(codeset->name, load_base, load_base); 185 GDBStub::RegisterModule(file->GetName(), load_base, load_base);
174 186
175 return true; 187 return true;
176} 188}
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 2186b02af..ba57db9bf 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -12,11 +12,11 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
18#include "core/loader/nso.h" 17#include "core/loader/nso.h"
19#include "core/memory.h" 18#include "core/memory.h"
19#include "core/settings.h"
20 20
21namespace Loader { 21namespace Loader {
22 22
@@ -94,6 +94,7 @@ static constexpr u32 PageAlignSize(u32 size) {
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
97 bool should_pass_arguments,
97 boost::optional<FileSys::PatchManager> pm) { 98 boost::optional<FileSys::PatchManager> pm) {
98 if (file == nullptr) 99 if (file == nullptr)
99 return {}; 100 return {};
@@ -109,8 +110,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
109 return {}; 110 return {};
110 111
111 // Build program image 112 // Build program image
112 auto& kernel = Core::System::GetInstance().Kernel(); 113 Kernel::CodeSet codeset;
113 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
114 std::vector<u8> program_image; 114 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 116 std::vector<u8> data =
@@ -120,9 +120,22 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
120 } 120 }
121 program_image.resize(nso_header.segments[i].location); 121 program_image.resize(nso_header.segments[i].location);
122 program_image.insert(program_image.end(), data.begin(), data.end()); 122 program_image.insert(program_image.end(), data.begin(), data.end());
123 codeset->segments[i].addr = nso_header.segments[i].location; 123 codeset.segments[i].addr = nso_header.segments[i].location;
124 codeset->segments[i].offset = nso_header.segments[i].location; 124 codeset.segments[i].offset = nso_header.segments[i].location;
125 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); 125 codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
126 }
127
128 if (should_pass_arguments && !Settings::values.program_args.empty()) {
129 const auto arg_data = Settings::values.program_args;
130 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
131 NSOArgumentHeader args_header{
132 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
133 const auto end_offset = program_image.size();
134 program_image.resize(static_cast<u32>(program_image.size()) +
135 NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
136 std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
137 std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
138 arg_data.size());
126 } 139 }
127 140
128 // MOD header pointer is at .text offset + 4 141 // MOD header pointer is at .text offset + 4
@@ -139,7 +152,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
139 // Resize program image to include .bss section and page align each section 152 // Resize program image to include .bss section and page align each section
140 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 153 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
141 } 154 }
142 codeset->DataSegment().size += bss_size; 155 codeset.DataSegment().size += bss_size;
143 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; 156 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
144 program_image.resize(image_size); 157 program_image.resize(image_size);
145 158
@@ -155,12 +168,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
155 } 168 }
156 169
157 // Load codeset for current process 170 // Load codeset for current process
158 codeset->name = file->GetName(); 171 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
159 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 172 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
160 Core::CurrentProcess()->LoadModule(codeset, load_base);
161 173
162 // Register module with GDBStub 174 // Register module with GDBStub
163 GDBStub::RegisterModule(codeset->name, load_base, load_base); 175 GDBStub::RegisterModule(file->GetName(), load_base, load_base);
164 176
165 return load_base + image_size; 177 return load_base + image_size;
166} 178}
@@ -172,7 +184,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
172 184
173 // Load module 185 // Load module
174 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 186 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
175 LoadModule(file, base_address); 187 LoadModule(file, base_address, true);
176 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 188 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
177 189
178 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 190 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 05353d4d9..70ab3b718 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -11,6 +11,15 @@
11 11
12namespace Loader { 12namespace Loader {
13 13
14constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
15
16struct NSOArgumentHeader {
17 u32_le allocated_size;
18 u32_le actual_size;
19 INSERT_PADDING_BYTES(0x18);
20};
21static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
22
14/// Loads an NSO file 23/// Loads an NSO file
15class AppLoader_NSO final : public AppLoader, Linker { 24class AppLoader_NSO final : public AppLoader, Linker {
16public: 25public:
@@ -27,7 +36,7 @@ public:
27 return IdentifyType(file); 36 return IdentifyType(file);
28 } 37 }
29 38
30 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, 39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments,
31 boost::optional<FileSys::PatchManager> pm = boost::none); 40 boost::optional<FileSys::PatchManager> pm = boost::none);
32 41
33 ResultStatus Load(Kernel::Process& process) override; 42 ResultStatus Load(Kernel::Process& process) override;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 5534ce01c..13e57848d 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -35,7 +35,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
35 return; 35 return;
36 36
37 std::tie(nacp_file, icon_file) = 37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); 38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39} 39}
40 40
41AppLoader_NSP::~AppLoader_NSP() = default; 41AppLoader_NSP::~AppLoader_NSP() = default;
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index b006594a6..db91cd01e 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -49,7 +49,7 @@ private:
49 std::unique_ptr<AppLoader> secondary_loader; 49 std::unique_ptr<AppLoader> secondary_loader;
50 50
51 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
52 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
53 u64 title_id; 53 u64 title_id;
54}; 54};
55 55
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index ee5452eb9..7a619acb4 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -30,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
30 return; 30 return;
31 31
32 std::tie(nacp_file, icon_file) = 32 std::tie(nacp_file, icon_file) =
33 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); 33 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
34} 34}
35 35
36AppLoader_XCI::~AppLoader_XCI() = default; 36AppLoader_XCI::~AppLoader_XCI() = default;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 770ed1437..46f8dfc9e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -49,7 +49,7 @@ private:
49 std::unique_ptr<AppLoader_NCA> nca_loader; 49 std::unique_ptr<AppLoader_NCA> nca_loader;
50 50
51 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
52 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
53}; 53};
54 54
55} // namespace Loader 55} // namespace Loader
diff --git a/src/core/settings.h b/src/core/settings.h
index 1808f5937..83b9a04c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -155,6 +155,7 @@ struct Values {
155 // Debugging 155 // Debugging
156 bool use_gdbstub; 156 bool use_gdbstub;
157 u16 gdbstub_port; 157 u16 gdbstub_port;
158 std::string program_args;
158 159
159 // WebService 160 // WebService
160 bool enable_telemetry; 161 bool enable_telemetry;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index f29fff1e7..7b04792b5 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -2,12 +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 <array>
6
7#include <mbedtls/ctr_drbg.h>
8#include <mbedtls/entropy.h>
9
5#include "common/assert.h" 10#include "common/assert.h"
6#include "common/common_types.h" 11#include "common/common_types.h"
7#include "common/file_util.h" 12#include "common/file_util.h"
13#include "common/logging/log.h"
8 14
9#include <mbedtls/ctr_drbg.h>
10#include <mbedtls/entropy.h>
11#include "core/core.h" 15#include "core/core.h"
12#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
13#include "core/file_sys/patch_manager.h" 17#include "core/file_sys/patch_manager.h"
@@ -28,11 +32,11 @@ static u64 GenerateTelemetryId() {
28 mbedtls_entropy_context entropy; 32 mbedtls_entropy_context entropy;
29 mbedtls_entropy_init(&entropy); 33 mbedtls_entropy_init(&entropy);
30 mbedtls_ctr_drbg_context ctr_drbg; 34 mbedtls_ctr_drbg_context ctr_drbg;
31 std::string personalization = "yuzu Telemetry ID"; 35 constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}};
32 36
33 mbedtls_ctr_drbg_init(&ctr_drbg); 37 mbedtls_ctr_drbg_init(&ctr_drbg);
34 ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, 38 ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
35 reinterpret_cast<const unsigned char*>(personalization.c_str()), 39 reinterpret_cast<const unsigned char*>(personalization.data()),
36 personalization.size()) == 0); 40 personalization.size()) == 0);
37 ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), 41 ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id),
38 sizeof(u64)) == 0); 42 sizeof(u64)) == 0);
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cec271df0..2a4845797 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8#include "common/telemetry.h" 9#include "common/telemetry.h"
9 10
10namespace Core { 11namespace Core {
@@ -30,8 +31,6 @@ public:
30 field_collection.AddField(type, name, std::move(value)); 31 field_collection.AddField(type, name, std::move(value));
31 } 32 }
32 33
33 static void FinalizeAsyncJob();
34
35private: 34private:
36 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 35 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
37 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields 36 std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
@@ -53,7 +52,6 @@ u64 RegenerateTelemetryId();
53 * Verifies the username and token. 52 * Verifies the username and token.
54 * @param username yuzu username to use for authentication. 53 * @param username yuzu username to use for authentication.
55 * @param token yuzu token to use for authentication. 54 * @param token yuzu token to use for authentication.
56 * @param func A function that gets exectued when the verification is finished
57 * @returns Future with bool indicating whether the verification succeeded 55 * @returns Future with bool indicating whether the verification succeeded
58 */ 56 */
59bool VerifyLogin(const std::string& username, const std::string& token); 57bool VerifyLogin(const std::string& username, const std::string& token);
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index c0a57e71f..37e15bad0 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -15,7 +15,8 @@ namespace ArmTests {
15TestEnvironment::TestEnvironment(bool mutable_memory_) 15TestEnvironment::TestEnvironment(bool mutable_memory_)
16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
17 17
18 Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); 18 auto process = Kernel::Process::Create(kernel, "");
19 kernel.MakeCurrentProcess(process.get());
19 page_table = &Core::CurrentProcess()->VMManager().page_table; 20 page_table = &Core::CurrentProcess()->VMManager().page_table;
20 21
21 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 81d15c62a..2a6e8bbbb 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -36,9 +36,9 @@ public:
36 RenderTargetFormat format; 36 RenderTargetFormat format;
37 BitField<0, 1, u32> linear; 37 BitField<0, 1, u32> linear;
38 union { 38 union {
39 BitField<0, 4, u32> block_depth; 39 BitField<0, 4, u32> block_width;
40 BitField<4, 4, u32> block_height; 40 BitField<4, 4, u32> block_height;
41 BitField<8, 4, u32> block_width; 41 BitField<8, 4, u32> block_depth;
42 }; 42 };
43 u32 depth; 43 u32 depth;
44 u32 layer; 44 u32 layer;
@@ -53,10 +53,20 @@ public:
53 address_low); 53 address_low);
54 } 54 }
55 55
56 u32 BlockWidth() const {
57 // The block width is stored in log2 format.
58 return 1 << block_width;
59 }
60
56 u32 BlockHeight() const { 61 u32 BlockHeight() const {
57 // The block height is stored in log2 format. 62 // The block height is stored in log2 format.
58 return 1 << block_height; 63 return 1 << block_height;
59 } 64 }
65
66 u32 BlockDepth() const {
67 // The block depth is stored in log2 format.
68 return 1 << block_depth;
69 }
60 }; 70 };
61 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 71 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
62 72
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4290da33f..c8d1b6478 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -347,6 +347,16 @@ public:
347 DecrWrap = 8, 347 DecrWrap = 8,
348 }; 348 };
349 349
350 enum class MemoryLayout : u32 {
351 Linear = 0,
352 BlockLinear = 1,
353 };
354
355 enum class InvMemoryLayout : u32 {
356 BlockLinear = 0,
357 Linear = 1,
358 };
359
350 struct Cull { 360 struct Cull {
351 enum class FrontFace : u32 { 361 enum class FrontFace : u32 {
352 ClockWise = 0x0900, 362 ClockWise = 0x0900,
@@ -432,7 +442,12 @@ public:
432 u32 width; 442 u32 width;
433 u32 height; 443 u32 height;
434 Tegra::RenderTargetFormat format; 444 Tegra::RenderTargetFormat format;
435 u32 block_dimensions; 445 union {
446 BitField<0, 3, u32> block_width;
447 BitField<4, 3, u32> block_height;
448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout;
436 u32 array_mode; 451 u32 array_mode;
437 u32 layer_stride; 452 u32 layer_stride;
438 u32 base_layer; 453 u32 base_layer;
@@ -532,7 +547,21 @@ public:
532 INSERT_PADDING_WORDS(0x3); 547 INSERT_PADDING_WORDS(0x3);
533 s32 clear_stencil; 548 s32 clear_stencil;
534 549
535 INSERT_PADDING_WORDS(0x6C); 550 INSERT_PADDING_WORDS(0x17);
551
552 struct {
553 u32 enable;
554 union {
555 BitField<0, 16, u32> min_x;
556 BitField<16, 16, u32> max_x;
557 };
558 union {
559 BitField<0, 16, u32> min_y;
560 BitField<16, 16, u32> max_y;
561 };
562 } scissor_test;
563
564 INSERT_PADDING_WORDS(0x52);
536 565
537 s32 stencil_back_func_ref; 566 s32 stencil_back_func_ref;
538 u32 stencil_back_mask; 567 u32 stencil_back_mask;
@@ -548,7 +577,12 @@ public:
548 u32 address_high; 577 u32 address_high;
549 u32 address_low; 578 u32 address_low;
550 Tegra::DepthFormat format; 579 Tegra::DepthFormat format;
551 u32 block_dimensions; 580 union {
581 BitField<0, 4, u32> block_width;
582 BitField<4, 4, u32> block_height;
583 BitField<8, 4, u32> block_depth;
584 BitField<20, 1, InvMemoryLayout> type;
585 } memory_layout;
552 u32 layer_stride; 586 u32 layer_stride;
553 587
554 GPUVAddr Address() const { 588 GPUVAddr Address() const {
@@ -1002,6 +1036,7 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1002ASSERT_REG_POSITION(clear_color[0], 0x360); 1036ASSERT_REG_POSITION(clear_color[0], 0x360);
1003ASSERT_REG_POSITION(clear_depth, 0x364); 1037ASSERT_REG_POSITION(clear_depth, 0x364);
1004ASSERT_REG_POSITION(clear_stencil, 0x368); 1038ASSERT_REG_POSITION(clear_stencil, 0x368);
1039ASSERT_REG_POSITION(scissor_test, 0x380);
1005ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1040ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1006ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1041ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1007ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1042ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b1f137b9c..9a59b65b3 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 214 XHi = 3,
215}; 215};
216 216
217enum class VmadType : u64 {
218 Size16_Low = 0,
219 Size16_High = 1,
220 Size32 = 2,
221 Invalid = 3,
222};
223
224enum class VmadShr : u64 {
225 Shr7 = 1,
226 Shr15 = 2,
227};
228
217enum class XmadMode : u64 { 229enum class XmadMode : u64 {
218 None = 0, 230 None = 0,
219 CLo = 1, 231 CLo = 1,
@@ -314,6 +326,15 @@ enum class TextureMiscMode : u64 {
314 PTP, 326 PTP,
315}; 327};
316 328
329enum class IsberdMode : u64 {
330 None = 0,
331 Patch = 1,
332 Prim = 2,
333 Attr = 3,
334};
335
336enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
337
317enum class IpaInterpMode : u64 { 338enum class IpaInterpMode : u64 {
318 Linear = 0, 339 Linear = 0,
319 Perspective = 1, 340 Perspective = 1,
@@ -340,6 +361,87 @@ struct IpaMode {
340 } 361 }
341}; 362};
342 363
364enum class SystemVariable : u64 {
365 LaneId = 0x00,
366 VirtCfg = 0x02,
367 VirtId = 0x03,
368 Pm0 = 0x04,
369 Pm1 = 0x05,
370 Pm2 = 0x06,
371 Pm3 = 0x07,
372 Pm4 = 0x08,
373 Pm5 = 0x09,
374 Pm6 = 0x0a,
375 Pm7 = 0x0b,
376 OrderingTicket = 0x0f,
377 PrimType = 0x10,
378 InvocationId = 0x11,
379 Ydirection = 0x12,
380 ThreadKill = 0x13,
381 ShaderType = 0x14,
382 DirectBeWriteAddressLow = 0x15,
383 DirectBeWriteAddressHigh = 0x16,
384 DirectBeWriteEnabled = 0x17,
385 MachineId0 = 0x18,
386 MachineId1 = 0x19,
387 MachineId2 = 0x1a,
388 MachineId3 = 0x1b,
389 Affinity = 0x1c,
390 InvocationInfo = 0x1d,
391 WscaleFactorXY = 0x1e,
392 WscaleFactorZ = 0x1f,
393 Tid = 0x20,
394 TidX = 0x21,
395 TidY = 0x22,
396 TidZ = 0x23,
397 CtaParam = 0x24,
398 CtaIdX = 0x25,
399 CtaIdY = 0x26,
400 CtaIdZ = 0x27,
401 NtId = 0x28,
402 CirQueueIncrMinusOne = 0x29,
403 Nlatc = 0x2a,
404 SmSpaVersion = 0x2c,
405 MultiPassShaderInfo = 0x2d,
406 LwinHi = 0x2e,
407 SwinHi = 0x2f,
408 SwinLo = 0x30,
409 SwinSz = 0x31,
410 SmemSz = 0x32,
411 SmemBanks = 0x33,
412 LwinLo = 0x34,
413 LwinSz = 0x35,
414 LmemLosz = 0x36,
415 LmemHioff = 0x37,
416 EqMask = 0x38,
417 LtMask = 0x39,
418 LeMask = 0x3a,
419 GtMask = 0x3b,
420 GeMask = 0x3c,
421 RegAlloc = 0x3d,
422 CtxAddr = 0x3e, // .fmask = F_SM50
423 BarrierAlloc = 0x3e, // .fmask = F_SM60
424 GlobalErrorStatus = 0x40,
425 WarpErrorStatus = 0x42,
426 WarpErrorStatusClear = 0x43,
427 PmHi0 = 0x48,
428 PmHi1 = 0x49,
429 PmHi2 = 0x4a,
430 PmHi3 = 0x4b,
431 PmHi4 = 0x4c,
432 PmHi5 = 0x4d,
433 PmHi6 = 0x4e,
434 PmHi7 = 0x4f,
435 ClockLo = 0x50,
436 ClockHi = 0x51,
437 GlobalTimerLo = 0x52,
438 GlobalTimerHi = 0x53,
439 HwTaskId = 0x60,
440 CircularQueueEntryIndex = 0x61,
441 CircularQueueEntryAddressLow = 0x62,
442 CircularQueueEntryAddressHigh = 0x63,
443};
444
343union Instruction { 445union Instruction {
344 Instruction& operator=(const Instruction& instr) { 446 Instruction& operator=(const Instruction& instr) {
345 value = instr.value; 447 value = instr.value;
@@ -362,6 +464,7 @@ union Instruction {
362 BitField<48, 16, u64> opcode; 464 BitField<48, 16, u64> opcode;
363 465
364 union { 466 union {
467 BitField<20, 16, u64> imm20_16;
365 BitField<20, 19, u64> imm20_19; 468 BitField<20, 19, u64> imm20_19;
366 BitField<20, 32, s64> imm20_32; 469 BitField<20, 32, s64> imm20_32;
367 BitField<45, 1, u64> negate_b; 470 BitField<45, 1, u64> negate_b;
@@ -403,6 +506,10 @@ union Instruction {
403 } 506 }
404 } lop3; 507 } lop3;
405 508
509 u16 GetImm20_16() const {
510 return static_cast<u16>(imm20_16);
511 }
512
406 u32 GetImm20_19() const { 513 u32 GetImm20_19() const {
407 u32 imm{static_cast<u32>(imm20_19)}; 514 u32 imm{static_cast<u32>(imm20_19)};
408 imm <<= 12; 515 imm <<= 12;
@@ -915,6 +1022,35 @@ union Instruction {
915 } bra; 1022 } bra;
916 1023
917 union { 1024 union {
1025 BitField<39, 1, u64> emit; // EmitVertex
1026 BitField<40, 1, u64> cut; // EndPrimitive
1027 } out;
1028
1029 union {
1030 BitField<31, 1, u64> skew;
1031 BitField<32, 1, u64> o;
1032 BitField<33, 2, IsberdMode> mode;
1033 BitField<47, 2, IsberdShift> shift;
1034 } isberd;
1035
1036 union {
1037 BitField<48, 1, u64> signed_a;
1038 BitField<38, 1, u64> is_byte_chunk_a;
1039 BitField<36, 2, VmadType> type_a;
1040 BitField<36, 2, u64> byte_height_a;
1041
1042 BitField<49, 1, u64> signed_b;
1043 BitField<50, 1, u64> use_register_b;
1044 BitField<30, 1, u64> is_byte_chunk_b;
1045 BitField<28, 2, VmadType> type_b;
1046 BitField<28, 2, u64> byte_height_b;
1047
1048 BitField<51, 2, VmadShr> shr;
1049 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1050 BitField<47, 1, u64> cc;
1051 } vmad;
1052
1053 union {
918 BitField<20, 16, u64> imm20_16; 1054 BitField<20, 16, u64> imm20_16;
919 BitField<36, 1, u64> product_shift_left; 1055 BitField<36, 1, u64> product_shift_left;
920 BitField<37, 1, u64> merge_37; 1056 BitField<37, 1, u64> merge_37;
@@ -936,6 +1072,10 @@ union Instruction {
936 BitField<36, 5, u64> index; 1072 BitField<36, 5, u64> index;
937 } cbuf36; 1073 } cbuf36;
938 1074
1075 // Unsure about the size of this one.
1076 // It's always used with a gpr0, so any size should be fine.
1077 BitField<20, 8, SystemVariable> sys20;
1078
939 BitField<47, 1, u64> generates_cc; 1079 BitField<47, 1, u64> generates_cc;
940 BitField<61, 1, u64> is_b_imm; 1080 BitField<61, 1, u64> is_b_imm;
941 BitField<60, 1, u64> is_b_gpr; 1081 BitField<60, 1, u64> is_b_gpr;
@@ -975,6 +1115,9 @@ public:
975 TMML, // Texture Mip Map Level 1115 TMML, // Texture Mip Map Level
976 EXIT, 1116 EXIT,
977 IPA, 1117 IPA,
1118 OUT_R, // Emit vertex/primitive
1119 ISBERD,
1120 VMAD,
978 FFMA_IMM, // Fused Multiply and Add 1121 FFMA_IMM, // Fused Multiply and Add
979 FFMA_CR, 1122 FFMA_CR,
980 FFMA_RC, 1123 FFMA_RC,
@@ -1034,6 +1177,7 @@ public:
1034 MOV_C, 1177 MOV_C,
1035 MOV_R, 1178 MOV_R,
1036 MOV_IMM, 1179 MOV_IMM,
1180 MOV_SYS,
1037 MOV32_IMM, 1181 MOV32_IMM,
1038 SHL_C, 1182 SHL_C,
1039 SHL_R, 1183 SHL_R,
@@ -1209,6 +1353,9 @@ private:
1209 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), 1353 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
1210 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 1354 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
1211 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1355 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
1356 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1357 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1358 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1212 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1359 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1213 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1360 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1214 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1361 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1255,6 +1402,7 @@ private:
1255 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), 1402 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
1256 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), 1403 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
1257 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), 1404 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
1405 INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
1258 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), 1406 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
1259 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), 1407 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
1260 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), 1408 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 209bdf181..84582c777 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -255,7 +255,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
255 return params; 255 return params;
256} 256}
257 257
258void RasterizerOpenGL::SetupShaders() { 258void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
259 MICROPROFILE_SCOPE(OpenGL_Shader); 259 MICROPROFILE_SCOPE(OpenGL_Shader);
260 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 260 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
261 261
@@ -270,6 +270,11 @@ void RasterizerOpenGL::SetupShaders() {
270 270
271 // Skip stages that are not enabled 271 // Skip stages that are not enabled
272 if (!gpu.regs.IsShaderConfigEnabled(index)) { 272 if (!gpu.regs.IsShaderConfigEnabled(index)) {
273 switch (program) {
274 case Maxwell::ShaderProgram::Geometry:
275 shader_program_manager->UseTrivialGeometryShader();
276 break;
277 }
273 continue; 278 continue;
274 } 279 }
275 280
@@ -288,11 +293,18 @@ void RasterizerOpenGL::SetupShaders() {
288 switch (program) { 293 switch (program) {
289 case Maxwell::ShaderProgram::VertexA: 294 case Maxwell::ShaderProgram::VertexA:
290 case Maxwell::ShaderProgram::VertexB: { 295 case Maxwell::ShaderProgram::VertexB: {
291 shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle()); 296 shader_program_manager->UseProgrammableVertexShader(
297 shader->GetProgramHandle(primitive_mode));
298 break;
299 }
300 case Maxwell::ShaderProgram::Geometry: {
301 shader_program_manager->UseProgrammableGeometryShader(
302 shader->GetProgramHandle(primitive_mode));
292 break; 303 break;
293 } 304 }
294 case Maxwell::ShaderProgram::Fragment: { 305 case Maxwell::ShaderProgram::Fragment: {
295 shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle()); 306 shader_program_manager->UseProgrammableFragmentShader(
307 shader->GetProgramHandle(primitive_mode));
296 break; 308 break;
297 } 309 }
298 default: 310 default:
@@ -302,12 +314,13 @@ void RasterizerOpenGL::SetupShaders() {
302 } 314 }
303 315
304 // Configure the const buffers for this shader stage. 316 // Configure the const buffers for this shader stage.
305 current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), 317 current_constbuffer_bindpoint =
306 shader, current_constbuffer_bindpoint); 318 SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
319 current_constbuffer_bindpoint);
307 320
308 // Configure the textures for this shader stage. 321 // Configure the textures for this shader stage.
309 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, 322 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
310 current_texture_bindpoint); 323 primitive_mode, current_texture_bindpoint);
311 324
312 // When VertexA is enabled, we have dual vertex shaders 325 // When VertexA is enabled, we have dual vertex shaders
313 if (program == Maxwell::ShaderProgram::VertexA) { 326 if (program == Maxwell::ShaderProgram::VertexA) {
@@ -317,8 +330,6 @@ void RasterizerOpenGL::SetupShaders() {
317 } 330 }
318 331
319 state.Apply(); 332 state.Apply();
320
321 shader_program_manager->UseTrivialGeometryShader();
322} 333}
323 334
324std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 335std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -541,6 +552,7 @@ void RasterizerOpenGL::DrawArrays() {
541 SyncLogicOpState(); 552 SyncLogicOpState();
542 SyncCullMode(); 553 SyncCullMode();
543 SyncAlphaTest(); 554 SyncAlphaTest();
555 SyncScissorTest();
544 SyncTransformFeedback(); 556 SyncTransformFeedback();
545 SyncPointState(); 557 SyncPointState();
546 558
@@ -580,7 +592,7 @@ void RasterizerOpenGL::DrawArrays() {
580 592
581 SetupVertexArrays(); 593 SetupVertexArrays();
582 DrawParameters params = SetupDraw(); 594 DrawParameters params = SetupDraw();
583 SetupShaders(); 595 SetupShaders(params.primitive_mode);
584 596
585 buffer_cache.Unmap(); 597 buffer_cache.Unmap();
586 598
@@ -719,7 +731,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
719} 731}
720 732
721u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 733u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
722 u32 current_bindpoint) { 734 GLenum primitive_mode, u32 current_bindpoint) {
723 MICROPROFILE_SCOPE(OpenGL_UBO); 735 MICROPROFILE_SCOPE(OpenGL_UBO);
724 const auto& gpu = Core::System::GetInstance().GPU(); 736 const auto& gpu = Core::System::GetInstance().GPU();
725 const auto& maxwell3d = gpu.Maxwell3D(); 737 const auto& maxwell3d = gpu.Maxwell3D();
@@ -771,7 +783,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
771 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); 783 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
772 784
773 // Now configure the bindpoint of the buffer inside the shader 785 // Now configure the bindpoint of the buffer inside the shader
774 glUniformBlockBinding(shader->GetProgramHandle(), 786 glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
775 shader->GetProgramResourceIndex(used_buffer), 787 shader->GetProgramResourceIndex(used_buffer),
776 current_bindpoint + bindpoint); 788 current_bindpoint + bindpoint);
777 789
@@ -787,7 +799,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
787 return current_bindpoint + static_cast<u32>(entries.size()); 799 return current_bindpoint + static_cast<u32>(entries.size());
788} 800}
789 801
790u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { 802u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
803 GLenum primitive_mode, u32 current_unit) {
791 MICROPROFILE_SCOPE(OpenGL_Texture); 804 MICROPROFILE_SCOPE(OpenGL_Texture);
792 const auto& gpu = Core::System::GetInstance().GPU(); 805 const auto& gpu = Core::System::GetInstance().GPU();
793 const auto& maxwell3d = gpu.Maxwell3D(); 806 const auto& maxwell3d = gpu.Maxwell3D();
@@ -802,8 +815,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
802 815
803 // Bind the uniform to the sampler. 816 // Bind the uniform to the sampler.
804 817
805 glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry), 818 glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
806 current_bindpoint); 819 shader->GetUniformLocation(entry), current_bindpoint);
807 820
808 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); 821 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
809 822
@@ -972,6 +985,22 @@ void RasterizerOpenGL::SyncAlphaTest() {
972 } 985 }
973} 986}
974 987
988void RasterizerOpenGL::SyncScissorTest() {
989 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
990
991 state.scissor.enabled = (regs.scissor_test.enable != 0);
992 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's
993 // implemented.
994 if (regs.scissor_test.enable != 0) {
995 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
996 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
997 state.scissor.x = regs.scissor_test.min_x;
998 state.scissor.y = regs.scissor_test.min_y;
999 state.scissor.width = width;
1000 state.scissor.height = height;
1001 }
1002}
1003
975void RasterizerOpenGL::SyncTransformFeedback() { 1004void RasterizerOpenGL::SyncTransformFeedback() {
976 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1005 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
977 1006
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 0dab2018b..b1f7ccc7e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -120,7 +120,7 @@ private:
120 * @returns The next available bindpoint for use in the next shader stage. 120 * @returns The next available bindpoint for use in the next shader stage.
121 */ 121 */
122 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 122 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
123 u32 current_bindpoint); 123 GLenum primitive_mode, u32 current_bindpoint);
124 124
125 /* 125 /*
126 * Configures the current textures to use for the draw command. 126 * Configures the current textures to use for the draw command.
@@ -130,7 +130,7 @@ private:
130 * @returns The next available bindpoint for use in the next shader stage. 130 * @returns The next available bindpoint for use in the next shader stage.
131 */ 131 */
132 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 132 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
133 u32 current_unit); 133 GLenum primitive_mode, u32 current_unit);
134 134
135 /// Syncs the viewport to match the guest state 135 /// Syncs the viewport to match the guest state
136 void SyncViewport(); 136 void SyncViewport();
@@ -165,6 +165,9 @@ private:
165 /// Syncs the alpha test state to match the guest state 165 /// Syncs the alpha test state to match the guest state
166 void SyncAlphaTest(); 166 void SyncAlphaTest();
167 167
168 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest();
170
168 /// Syncs the transform feedback state to match the guest state 171 /// Syncs the transform feedback state to match the guest state
169 void SyncTransformFeedback(); 172 void SyncTransformFeedback();
170 173
@@ -207,7 +210,7 @@ private:
207 210
208 DrawParameters SetupDraw(); 211 DrawParameters SetupDraw();
209 212
210 void SetupShaders(); 213 void SetupShaders(GLenum primitive_mode);
211 214
212 enum class AccelDraw { Disabled, Arrays, Indexed }; 215 enum class AccelDraw { Disabled, Arrays, Indexed };
213 AccelDraw accelerate_draw = AccelDraw::Disabled; 216 AccelDraw accelerate_draw = AccelDraw::Disabled;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 56ff83eff..65a220c41 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -45,7 +45,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
45 SurfaceParams params{}; 45 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address()); 46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 47 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
48 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
50 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
49 params.pixel_format = 51 params.pixel_format =
50 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 52 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
51 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 53 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
@@ -97,8 +99,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
97 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
98 SurfaceParams params{}; 100 SurfaceParams params{};
99 params.addr = TryGetCpuAddr(config.Address()); 101 params.addr = TryGetCpuAddr(config.Address());
100 params.is_tiled = true; 102 params.is_tiled =
101 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width;
105 params.block_height = 1 << config.memory_layout.block_height;
106 params.block_depth = 1 << config.memory_layout.block_depth;
102 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 107 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
103 params.component_type = ComponentTypeFromRenderTarget(config.format); 108 params.component_type = ComponentTypeFromRenderTarget(config.format);
104 params.type = GetFormatType(params.pixel_format); 109 params.type = GetFormatType(params.pixel_format);
@@ -120,13 +125,16 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
120 return params; 125 return params;
121} 126}
122 127
123/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 128/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
124 Tegra::GPUVAddr zeta_address, 129 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
125 Tegra::DepthFormat format) { 130 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
126 SurfaceParams params{}; 132 SurfaceParams params{};
127 params.addr = TryGetCpuAddr(zeta_address); 133 params.addr = TryGetCpuAddr(zeta_address);
128 params.is_tiled = true; 134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
129 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 135 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U);
137 params.block_depth = 1 << std::min(block_depth, 5U);
130 params.pixel_format = PixelFormatFromDepthFormat(format); 138 params.pixel_format = PixelFormatFromDepthFormat(format);
131 params.component_type = ComponentTypeFromDepthFormat(format); 139 params.component_type = ComponentTypeFromDepthFormat(format);
132 params.type = GetFormatType(params.pixel_format); 140 params.type = GetFormatType(params.pixel_format);
@@ -148,7 +156,9 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
148 SurfaceParams params{}; 156 SurfaceParams params{};
149 params.addr = TryGetCpuAddr(config.Address()); 157 params.addr = TryGetCpuAddr(config.Address());
150 params.is_tiled = !config.linear; 158 params.is_tiled = !config.linear;
151 params.block_height = params.is_tiled ? config.BlockHeight() : 0, 159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
161 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
152 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 162 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
153 params.component_type = ComponentTypeFromRenderTarget(config.format); 163 params.component_type = ComponentTypeFromRenderTarget(config.format);
154 params.type = GetFormatType(params.pixel_format); 164 params.type = GetFormatType(params.pixel_format);
@@ -818,6 +828,11 @@ void CachedSurface::LoadGLBuffer() {
818 if (params.is_tiled) { 828 if (params.is_tiled) {
819 gl_buffer.resize(total_size); 829 gl_buffer.resize(total_size);
820 830
831 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
832 params.block_width, static_cast<u32>(params.target));
833 ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}",
834 params.block_depth, static_cast<u32>(params.target));
835
821 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 836 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
822 // this for 3D textures, etc. 837 // this for 3D textures, etc.
823 switch (params.target) { 838 switch (params.target) {
@@ -989,7 +1004,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
989 } 1004 }
990 1005
991 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( 1006 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
992 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; 1007 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format,
1008 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
1009 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
993 1010
994 return GetSurface(depth_params, preserve_contents); 1011 return GetSurface(depth_params, preserve_contents);
995} 1012}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b4940b3c..66d98ad4e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -716,9 +716,10 @@ struct SurfaceParams {
716 static SurfaceParams CreateForFramebuffer(std::size_t index); 716 static SurfaceParams CreateForFramebuffer(std::size_t index);
717 717
718 /// Creates SurfaceParams for a depth buffer configuration 718 /// Creates SurfaceParams for a depth buffer configuration
719 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 719 static SurfaceParams CreateForDepthBuffer(
720 Tegra::GPUVAddr zeta_address, 720 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
721 Tegra::DepthFormat format); 721 u32 block_width, u32 block_height, u32 block_depth,
722 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
722 723
723 /// Creates SurfaceParams for a Fermi2D surface copy 724 /// Creates SurfaceParams for a Fermi2D surface copy
724 static SurfaceParams CreateForFermiCopySurface( 725 static SurfaceParams CreateForFermiCopySurface(
@@ -733,7 +734,9 @@ struct SurfaceParams {
733 734
734 VAddr addr; 735 VAddr addr;
735 bool is_tiled; 736 bool is_tiled;
737 u32 block_width;
736 u32 block_height; 738 u32 block_height;
739 u32 block_depth;
737 PixelFormat pixel_format; 740 PixelFormat pixel_format;
738 ComponentType component_type; 741 ComponentType component_type;
739 SurfaceType type; 742 SurfaceType type;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7cd8f91e4..1a03a677f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -68,6 +68,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
68 program_result = GLShader::GenerateVertexShader(setup); 68 program_result = GLShader::GenerateVertexShader(setup);
69 gl_type = GL_VERTEX_SHADER; 69 gl_type = GL_VERTEX_SHADER;
70 break; 70 break;
71 case Maxwell::ShaderProgram::Geometry:
72 program_result = GLShader::GenerateGeometryShader(setup);
73 gl_type = GL_GEOMETRY_SHADER;
74 break;
71 case Maxwell::ShaderProgram::Fragment: 75 case Maxwell::ShaderProgram::Fragment:
72 program_result = GLShader::GenerateFragmentShader(setup); 76 program_result = GLShader::GenerateFragmentShader(setup);
73 gl_type = GL_FRAGMENT_SHADER; 77 gl_type = GL_FRAGMENT_SHADER;
@@ -80,11 +84,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
80 84
81 entries = program_result.second; 85 entries = program_result.second;
82 86
83 OGLShader shader; 87 if (program_type != Maxwell::ShaderProgram::Geometry) {
84 shader.Create(program_result.first.c_str(), gl_type); 88 OGLShader shader;
85 program.Create(true, shader.handle); 89 shader.Create(program_result.first.c_str(), gl_type);
86 SetShaderUniformBlockBindings(program.handle); 90 program.Create(true, shader.handle);
87 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); 91 SetShaderUniformBlockBindings(program.handle);
92 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
93 } else {
94 // Store shader's code to lazily build it on draw
95 geometry_programs.code = program_result.first;
96 }
88} 97}
89 98
90GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { 99GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -110,6 +119,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
110 return search->second; 119 return search->second;
111} 120}
112 121
122GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
123 const std::string& glsl_topology,
124 const std::string& debug_name) {
125 if (target_program.handle != 0) {
126 return target_program.handle;
127 }
128 const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
129 OGLShader shader;
130 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
131 target_program.Create(true, shader.handle);
132 SetShaderUniformBlockBindings(target_program.handle);
133 VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
134 return target_program.handle;
135};
136
113Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 137Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
114 const VAddr program_addr{GetShaderAddress(program)}; 138 const VAddr program_addr{GetShaderAddress(program)};
115 139
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 9bafe43a9..7bb287f56 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -7,6 +7,7 @@
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9 9
10#include "common/assert.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "video_core/rasterizer_cache.h" 12#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -38,8 +39,31 @@ public:
38 } 39 }
39 40
40 /// Gets the GL program handle for the shader 41 /// Gets the GL program handle for the shader
41 GLuint GetProgramHandle() const { 42 GLuint GetProgramHandle(GLenum primitive_mode) {
42 return program.handle; 43 if (program_type != Maxwell::ShaderProgram::Geometry) {
44 return program.handle;
45 }
46 switch (primitive_mode) {
47 case GL_POINTS:
48 return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
49 case GL_LINES:
50 case GL_LINE_STRIP:
51 return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
52 case GL_LINES_ADJACENCY:
53 case GL_LINE_STRIP_ADJACENCY:
54 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
55 "ShaderLinesAdjacency");
56 case GL_TRIANGLES:
57 case GL_TRIANGLE_STRIP:
58 case GL_TRIANGLE_FAN:
59 return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
60 case GL_TRIANGLES_ADJACENCY:
61 case GL_TRIANGLE_STRIP_ADJACENCY:
62 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
63 "ShaderLines");
64 default:
65 UNREACHABLE_MSG("Unknown primitive mode.");
66 }
43 } 67 }
44 68
45 /// Gets the GL program resource location for the specified resource, caching as needed 69 /// Gets the GL program resource location for the specified resource, caching as needed
@@ -49,12 +73,30 @@ public:
49 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); 73 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
50 74
51private: 75private:
76 /// Generates a geometry shader or returns one that already exists.
77 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
78 const std::string& debug_name);
79
52 VAddr addr; 80 VAddr addr;
53 Maxwell::ShaderProgram program_type; 81 Maxwell::ShaderProgram program_type;
54 GLShader::ShaderSetup setup; 82 GLShader::ShaderSetup setup;
55 GLShader::ShaderEntries entries; 83 GLShader::ShaderEntries entries;
84
85 // Non-geometry program.
56 OGLProgram program; 86 OGLProgram program;
57 87
88 // Geometry programs. These are needed because GLSL needs an input topology but it's not
89 // declared by the hardware. Workaround this issue by generating a different shader per input
90 // topology class.
91 struct {
92 std::string code;
93 OGLProgram points;
94 OGLProgram lines;
95 OGLProgram lines_adjacency;
96 OGLProgram triangles;
97 OGLProgram triangles_adjacency;
98 } geometry_programs;
99
58 std::map<u32, GLuint> resource_cache; 100 std::map<u32, GLuint> resource_cache;
59 std::map<u32, GLint> uniform_cache; 101 std::map<u32, GLint> uniform_cache;
60}; 102};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 7e57de78a..8dfb49507 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -7,6 +7,7 @@
7#include <string> 7#include <string>
8#include <string_view> 8#include <string_view>
9 9
10#include <boost/optional.hpp>
10#include <fmt/format.h> 11#include <fmt/format.h>
11 12
12#include "common/assert.h" 13#include "common/assert.h"
@@ -29,11 +30,32 @@ using Tegra::Shader::SubOp;
29constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 30constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
30constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); 31constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
31 32
33enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
34
35constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
36constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
37
32class DecompileFail : public std::runtime_error { 38class DecompileFail : public std::runtime_error {
33public: 39public:
34 using std::runtime_error::runtime_error; 40 using std::runtime_error::runtime_error;
35}; 41};
36 42
43/// Translate topology
44static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
45 switch (topology) {
46 case Tegra::Shader::OutputTopology::PointList:
47 return "points";
48 case Tegra::Shader::OutputTopology::LineStrip:
49 return "line_strip";
50 case Tegra::Shader::OutputTopology::TriangleStrip:
51 return "triangle_strip";
52 default:
53 LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology));
54 UNREACHABLE();
55 return "points";
56 }
57}
58
37/// Describes the behaviour of code path of a given entry point and a return point. 59/// Describes the behaviour of code path of a given entry point and a return point.
38enum class ExitMethod { 60enum class ExitMethod {
39 Undetermined, ///< Internal value. Only occur when analyzing JMP loop. 61 Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
@@ -253,8 +275,9 @@ enum class InternalFlag : u64 {
253class GLSLRegisterManager { 275class GLSLRegisterManager {
254public: 276public:
255 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
256 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
257 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { 279 const Tegra::Shader::Header& header)
280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} {
258 BuildRegisterList(); 281 BuildRegisterList();
259 BuildInputList(); 282 BuildInputList();
260 } 283 }
@@ -358,11 +381,13 @@ public:
358 * @param reg The destination register to use. 381 * @param reg The destination register to use.
359 * @param elem The element to use for the operation. 382 * @param elem The element to use for the operation.
360 * @param attribute The input attribute to use as the source value. 383 * @param attribute The input attribute to use as the source value.
384 * @param vertex The register that decides which vertex to read from (used in GS).
361 */ 385 */
362 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 386 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
363 const Tegra::Shader::IpaMode& input_mode) { 387 const Tegra::Shader::IpaMode& input_mode,
388 boost::optional<Register> vertex = {}) {
364 const std::string dest = GetRegisterAsFloat(reg); 389 const std::string dest = GetRegisterAsFloat(reg);
365 const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); 390 const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
366 shader.AddLine(dest + " = " + src + ';'); 391 shader.AddLine(dest + " = " + src + ';');
367 } 392 }
368 393
@@ -391,16 +416,29 @@ public:
391 * are stored as floats, so this may require conversion. 416 * are stored as floats, so this may require conversion.
392 * @param attribute The destination output attribute. 417 * @param attribute The destination output attribute.
393 * @param elem The element to use for the operation. 418 * @param elem The element to use for the operation.
394 * @param reg The register to use as the source value. 419 * @param val_reg The register to use as the source value.
420 * @param buf_reg The register that tells which buffer to write to (used in geometry shaders).
395 */ 421 */
396 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { 422 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg,
423 const Register& buf_reg) {
397 const std::string dest = GetOutputAttribute(attribute); 424 const std::string dest = GetOutputAttribute(attribute);
398 const std::string src = GetRegisterAsFloat(reg); 425 const std::string src = GetRegisterAsFloat(val_reg);
399 426
400 if (!dest.empty()) { 427 if (!dest.empty()) {
401 // Can happen with unknown/unimplemented output attributes, in which case we ignore the 428 // Can happen with unknown/unimplemented output attributes, in which case we ignore the
402 // instruction for now. 429 // instruction for now.
403 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 430 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
431 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
432 // shader. These instructions use a dirty register as buffer index. To avoid some
433 // drivers from complaining for the out of boundary writes, guard them.
434 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
435 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
436 shader.AddLine("amem[" + buf_index + "][" +
437 std::to_string(static_cast<u32>(attribute)) + ']' +
438 GetSwizzle(elem) + " = " + src + ';');
439 } else {
440 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
441 }
404 } 442 }
405 } 443 }
406 444
@@ -441,41 +479,123 @@ public:
441 } 479 }
442 } 480 }
443 481
444 /// Add declarations for registers 482 /// Add declarations.
445 void GenerateDeclarations(const std::string& suffix) { 483 void GenerateDeclarations(const std::string& suffix) {
484 GenerateRegisters(suffix);
485 GenerateInternalFlags();
486 GenerateInputAttrs();
487 GenerateOutputAttrs();
488 GenerateConstBuffers();
489 GenerateSamplers();
490 GenerateGeometry();
491 }
492
493 /// Returns a list of constant buffer declarations.
494 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
495 std::vector<ConstBufferEntry> result;
496 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
497 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
498 return result;
499 }
500
501 /// Returns a list of samplers used in the shader.
502 const std::vector<SamplerEntry>& GetSamplers() const {
503 return used_samplers;
504 }
505
506 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
507 /// necessary.
508 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
509 bool is_array, bool is_shadow) {
510 const auto offset = static_cast<std::size_t>(sampler.index.Value());
511
512 // If this sampler has already been used, return the existing mapping.
513 const auto itr =
514 std::find_if(used_samplers.begin(), used_samplers.end(),
515 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
516
517 if (itr != used_samplers.end()) {
518 ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
519 itr->IsShadow() == is_shadow);
520 return itr->GetName();
521 }
522
523 // Otherwise create a new mapping for this sampler
524 const std::size_t next_index = used_samplers.size();
525 const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
526 used_samplers.emplace_back(entry);
527 return entry.GetName();
528 }
529
530private:
531 /// Generates declarations for registers.
532 void GenerateRegisters(const std::string& suffix) {
446 for (const auto& reg : regs) { 533 for (const auto& reg : regs) {
447 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + 534 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
448 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); 535 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
449 } 536 }
450 declarations.AddNewLine(); 537 declarations.AddNewLine();
538 }
451 539
540 /// Generates declarations for internal flags.
541 void GenerateInternalFlags() {
452 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 542 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
453 const InternalFlag code = static_cast<InternalFlag>(ii); 543 const InternalFlag code = static_cast<InternalFlag>(ii);
454 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 544 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
455 } 545 }
456 declarations.AddNewLine(); 546 declarations.AddNewLine();
547 }
548
549 /// Generates declarations for input attributes.
550 void GenerateInputAttrs() {
551 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
552 const std::string attr =
553 stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
554 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
555 ") in vec4 " + attr + ';');
556 }
457 557
458 for (const auto element : declr_input_attribute) { 558 for (const auto element : declr_input_attribute) {
459 // TODO(bunnei): Use proper number of elements for these 559 // TODO(bunnei): Use proper number of elements for these
460 u32 idx = 560 u32 idx =
461 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); 561 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
462 declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + 562 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
463 GetInputFlags(element.first) + "in vec4 " + 563 // If inputs are varyings, add an offset
464 GetInputAttribute(element.first, element.second) + ';'); 564 idx += GENERIC_VARYING_START_LOCATION;
565 }
566
567 std::string attr{GetInputAttribute(element.first, element.second)};
568 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
569 attr = "gs_" + attr + "[]";
570 }
571 declarations.AddLine("layout (location = " + std::to_string(idx) + ") " +
572 GetInputFlags(element.first) + "in vec4 " + attr + ';');
465 } 573 }
574
466 declarations.AddNewLine(); 575 declarations.AddNewLine();
576 }
467 577
578 /// Generates declarations for output attributes.
579 void GenerateOutputAttrs() {
580 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
581 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
582 ") out vec4 position;");
583 }
468 for (const auto& index : declr_output_attribute) { 584 for (const auto& index : declr_output_attribute) {
469 // TODO(bunnei): Use proper number of elements for these 585 // TODO(bunnei): Use proper number of elements for these
470 declarations.AddLine("layout(location = " + 586 const u32 idx = static_cast<u32>(index) -
471 std::to_string(static_cast<u32>(index) - 587 static_cast<u32>(Attribute::Index::Attribute_0) +
472 static_cast<u32>(Attribute::Index::Attribute_0)) + 588 GENERIC_VARYING_START_LOCATION;
473 ") out vec4 " + GetOutputAttribute(index) + ';'); 589 declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
590 GetOutputAttribute(index) + ';');
474 } 591 }
475 declarations.AddNewLine(); 592 declarations.AddNewLine();
593 }
476 594
595 /// Generates declarations for constant buffers.
596 void GenerateConstBuffers() {
477 for (const auto& entry : GetConstBuffersDeclarations()) { 597 for (const auto& entry : GetConstBuffersDeclarations()) {
478 declarations.AddLine("layout(std140) uniform " + entry.GetName()); 598 declarations.AddLine("layout (std140) uniform " + entry.GetName());
479 declarations.AddLine('{'); 599 declarations.AddLine('{');
480 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + 600 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
481 "[MAX_CONSTBUFFER_ELEMENTS];"); 601 "[MAX_CONSTBUFFER_ELEMENTS];");
@@ -483,7 +603,10 @@ public:
483 declarations.AddNewLine(); 603 declarations.AddNewLine();
484 } 604 }
485 declarations.AddNewLine(); 605 declarations.AddNewLine();
606 }
486 607
608 /// Generates declarations for samplers.
609 void GenerateSamplers() {
487 const auto& samplers = GetSamplers(); 610 const auto& samplers = GetSamplers();
488 for (const auto& sampler : samplers) { 611 for (const auto& sampler : samplers) {
489 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + 612 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
@@ -492,44 +615,42 @@ public:
492 declarations.AddNewLine(); 615 declarations.AddNewLine();
493 } 616 }
494 617
495 /// Returns a list of constant buffer declarations 618 /// Generates declarations used for geometry shaders.
496 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { 619 void GenerateGeometry() {
497 std::vector<ConstBufferEntry> result; 620 if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
498 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), 621 return;
499 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
500 return result;
501 }
502
503 /// Returns a list of samplers used in the shader
504 const std::vector<SamplerEntry>& GetSamplers() const {
505 return used_samplers;
506 }
507
508 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
509 /// necessary.
510 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
511 bool is_array, bool is_shadow) {
512 const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
513 622
514 // If this sampler has already been used, return the existing mapping. 623 declarations.AddLine(
515 const auto itr = 624 "layout (" + GetTopologyName(header.common3.output_topology) +
516 std::find_if(used_samplers.begin(), used_samplers.end(), 625 ", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
517 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); 626 declarations.AddNewLine();
518 627
519 if (itr != used_samplers.end()) { 628 declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
520 ASSERT(itr->GetType() == type && itr->IsArray() == is_array && 629 std::to_string(MAX_ATTRIBUTES) + "];");
521 itr->IsShadow() == is_shadow); 630 declarations.AddNewLine();
522 return itr->GetName();
523 }
524 631
525 // Otherwise create a new mapping for this sampler 632 constexpr char buffer[] = "amem[output_buffer]";
526 const std::size_t next_index = used_samplers.size(); 633 declarations.AddLine("void emit_vertex(uint output_buffer) {");
527 const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; 634 ++declarations.scope;
528 used_samplers.emplace_back(entry); 635 for (const auto element : declr_output_attribute) {
529 return entry.GetName(); 636 declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' +
637 std::to_string(static_cast<u32>(element)) + "];");
638 }
639
640 declarations.AddLine("position = " + std::string(buffer) + '[' +
641 std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];");
642
643 // If a geometry shader is attached, it will always flip (it's the last stage before
644 // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
645 declarations.AddLine("position.xy *= viewport_flip.xy;");
646 declarations.AddLine("gl_Position = position;");
647 declarations.AddLine("position.w = 1.0;");
648 declarations.AddLine("EmitVertex();");
649 --declarations.scope;
650 declarations.AddLine('}');
651 declarations.AddNewLine();
530 } 652 }
531 653
532private:
533 /// Generates code representing a temporary (GPR) register. 654 /// Generates code representing a temporary (GPR) register.
534 std::string GetRegister(const Register& reg, unsigned elem) { 655 std::string GetRegister(const Register& reg, unsigned elem) {
535 if (reg == Register::ZeroIndex) { 656 if (reg == Register::ZeroIndex) {
@@ -586,11 +707,19 @@ private:
586 707
587 /// Generates code representing an input attribute register. 708 /// Generates code representing an input attribute register.
588 std::string GetInputAttribute(Attribute::Index attribute, 709 std::string GetInputAttribute(Attribute::Index attribute,
589 const Tegra::Shader::IpaMode& input_mode) { 710 const Tegra::Shader::IpaMode& input_mode,
711 boost::optional<Register> vertex = {}) {
712 auto GeometryPass = [&](const std::string& name) {
713 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
714 return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']';
715 }
716 return name;
717 };
718
590 switch (attribute) { 719 switch (attribute) {
591 case Attribute::Index::Position: 720 case Attribute::Index::Position:
592 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { 721 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
593 return "position"; 722 return GeometryPass("position");
594 } else { 723 } else {
595 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; 724 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
596 } 725 }
@@ -619,7 +748,7 @@ private:
619 UNREACHABLE(); 748 UNREACHABLE();
620 } 749 }
621 } 750 }
622 return "input_attribute_" + std::to_string(index); 751 return GeometryPass("input_attribute_" + std::to_string(index));
623 } 752 }
624 753
625 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 754 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
@@ -672,7 +801,7 @@ private:
672 return out; 801 return out;
673 } 802 }
674 803
675 /// Generates code representing an output attribute register. 804 /// Generates code representing the declaration name of an output attribute register.
676 std::string GetOutputAttribute(Attribute::Index attribute) { 805 std::string GetOutputAttribute(Attribute::Index attribute) {
677 switch (attribute) { 806 switch (attribute) {
678 case Attribute::Index::Position: 807 case Attribute::Index::Position:
@@ -708,6 +837,7 @@ private:
708 std::vector<SamplerEntry> used_samplers; 837 std::vector<SamplerEntry> used_samplers;
709 const Maxwell3D::Regs::ShaderStage& stage; 838 const Maxwell3D::Regs::ShaderStage& stage;
710 const std::string& suffix; 839 const std::string& suffix;
840 const Tegra::Shader::Header& header;
711}; 841};
712 842
713class GLSLGenerator { 843class GLSLGenerator {
@@ -1103,8 +1233,8 @@ private:
1103 return offset + 1; 1233 return offset + 1;
1104 } 1234 }
1105 1235
1106 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" + 1236 shader.AddLine(
1107 std::to_string(instr.value) + ')'); 1237 fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value));
1108 1238
1109 using Tegra::Shader::Pred; 1239 using Tegra::Shader::Pred;
1110 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1240 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1826,7 +1956,7 @@ private:
1826 const auto LoadNextElement = [&](u32 reg_offset) { 1956 const auto LoadNextElement = [&](u32 reg_offset) {
1827 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, 1957 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
1828 static_cast<Attribute::Index>(next_index), 1958 static_cast<Attribute::Index>(next_index),
1829 input_mode); 1959 input_mode, instr.gpr39.Value());
1830 1960
1831 // Load the next attribute element into the following register. If the element 1961 // Load the next attribute element into the following register. If the element
1832 // to load goes beyond the vec4 size, load the first element of the next 1962 // to load goes beyond the vec4 size, load the first element of the next
@@ -1890,8 +2020,8 @@ private:
1890 2020
1891 const auto StoreNextElement = [&](u32 reg_offset) { 2021 const auto StoreNextElement = [&](u32 reg_offset) {
1892 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), 2022 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
1893 next_element, 2023 next_element, instr.gpr0.Value() + reg_offset,
1894 instr.gpr0.Value() + reg_offset); 2024 instr.gpr39.Value());
1895 2025
1896 // Load the next attribute element into the following register. If the element 2026 // Load the next attribute element into the following register. If the element
1897 // to load goes beyond the vec4 size, load the first element of the next 2027 // to load goes beyond the vec4 size, load the first element of the next
@@ -2299,8 +2429,7 @@ private:
2299 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2429 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2300 "NDV is not implemented"); 2430 "NDV is not implemented");
2301 2431
2302 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2432 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2303 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2304 const bool is_array = instr.tmml.array != 0; 2433 const bool is_array = instr.tmml.array != 0;
2305 auto texture_type = instr.tmml.texture_type.Value(); 2434 auto texture_type = instr.tmml.texture_type.Value();
2306 const std::string sampler = 2435 const std::string sampler =
@@ -2311,13 +2440,11 @@ private:
2311 std::string coord; 2440 std::string coord;
2312 switch (texture_type) { 2441 switch (texture_type) {
2313 case Tegra::Shader::TextureType::Texture1D: { 2442 case Tegra::Shader::TextureType::Texture1D: {
2314 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2315 coord = "float coords = " + x + ';'; 2443 coord = "float coords = " + x + ';';
2316 break; 2444 break;
2317 } 2445 }
2318 case Tegra::Shader::TextureType::Texture2D: { 2446 case Tegra::Shader::TextureType::Texture2D: {
2319 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2447 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2320 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2321 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2448 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2322 break; 2449 break;
2323 } 2450 }
@@ -2327,8 +2454,7 @@ private:
2327 UNREACHABLE(); 2454 UNREACHABLE();
2328 2455
2329 // Fallback to interpreting as a 2D texture for now 2456 // Fallback to interpreting as a 2D texture for now
2330 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2457 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2331 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2332 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2458 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2333 texture_type = Tegra::Shader::TextureType::Texture2D; 2459 texture_type = Tegra::Shader::TextureType::Texture2D;
2334 } 2460 }
@@ -2738,6 +2864,52 @@ private:
2738 2864
2739 break; 2865 break;
2740 } 2866 }
2867 case OpCode::Id::OUT_R: {
2868 ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
2869 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
2870 "OUT is expected to be used in a geometry shader.");
2871
2872 if (instr.out.emit) {
2873 // gpr0 is used to store the next address. Hardware returns a pointer but
2874 // we just return the next index with a cyclic cap.
2875 const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)};
2876 const std::string next = "((" + current + " + 1" + ") % " +
2877 std::to_string(MAX_GEOMETRY_BUFFERS) + ')';
2878 shader.AddLine("emit_vertex(" + current + ");");
2879 regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1);
2880 }
2881 if (instr.out.cut) {
2882 shader.AddLine("EndPrimitive();");
2883 }
2884
2885 break;
2886 }
2887 case OpCode::Id::MOV_SYS: {
2888 switch (instr.sys20) {
2889 case Tegra::Shader::SystemVariable::InvocationInfo: {
2890 LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
2891 regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
2892 break;
2893 }
2894 default: {
2895 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}",
2896 static_cast<u32>(instr.sys20.Value()));
2897 UNREACHABLE();
2898 }
2899 }
2900 break;
2901 }
2902 case OpCode::Id::ISBERD: {
2903 ASSERT(instr.isberd.o == 0);
2904 ASSERT(instr.isberd.skew == 0);
2905 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None);
2906 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None);
2907 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
2908 "ISBERD is expected to be used in a geometry shader.");
2909 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
2910 regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1);
2911 break;
2912 }
2741 case OpCode::Id::BRA: { 2913 case OpCode::Id::BRA: {
2742 ASSERT_MSG(instr.bra.constant_buffer == 0, 2914 ASSERT_MSG(instr.bra.constant_buffer == 0,
2743 "BRA with constant buffers are not implemented"); 2915 "BRA with constant buffers are not implemented");
@@ -2781,6 +2953,88 @@ private:
2781 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); 2953 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
2782 break; 2954 break;
2783 } 2955 }
2956 case OpCode::Id::VMAD: {
2957 const bool signed_a = instr.vmad.signed_a == 1;
2958 const bool signed_b = instr.vmad.signed_b == 1;
2959 const bool result_signed = signed_a || signed_b;
2960 boost::optional<std::string> forced_result;
2961
2962 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
2963 Tegra::Shader::VmadType type, u64 byte_height) {
2964 const std::string value = [&]() {
2965 if (!is_chunk) {
2966 const auto offset = static_cast<u32>(byte_height * 8);
2967 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
2968 }
2969 const std::string zero = "0";
2970
2971 switch (type) {
2972 case Tegra::Shader::VmadType::Size16_Low:
2973 return '(' + op + " & 0xffff)";
2974 case Tegra::Shader::VmadType::Size16_High:
2975 return '(' + op + " >> 16)";
2976 case Tegra::Shader::VmadType::Size32:
2977 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
2978 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
2979 // explanation is found: assert.
2980 UNREACHABLE_MSG("Unimplemented");
2981 return zero;
2982 case Tegra::Shader::VmadType::Invalid:
2983 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
2984 // testing (even though it's invalid) this makes the whole instruction
2985 // assign zero to target register.
2986 forced_result = boost::make_optional(zero);
2987 return zero;
2988 default:
2989 UNREACHABLE();
2990 return zero;
2991 }
2992 }();
2993
2994 if (is_signed) {
2995 return "int(" + value + ')';
2996 }
2997 return value;
2998 };
2999
3000 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
3001 instr.vmad.is_byte_chunk_a != 0, signed_a,
3002 instr.vmad.type_a, instr.vmad.byte_height_a);
3003
3004 std::string op_b;
3005 if (instr.vmad.use_register_b) {
3006 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3007 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3008 instr.vmad.byte_height_b);
3009 } else {
3010 op_b = '(' +
3011 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3012 : instr.alu.GetImm20_16()) +
3013 ')';
3014 }
3015
3016 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3017
3018 std::string result;
3019 if (forced_result) {
3020 result = *forced_result;
3021 } else {
3022 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3023
3024 switch (instr.vmad.shr) {
3025 case Tegra::Shader::VmadShr::Shr7:
3026 result = '(' + result + " >> 7)";
3027 break;
3028 case Tegra::Shader::VmadShr::Shr15:
3029 result = '(' + result + " >> 15)";
3030 break;
3031 }
3032 }
3033 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3034 instr.vmad.saturate == 1, 0, Register::Size::Word,
3035 instr.vmad.cc);
3036 break;
3037 }
2784 default: { 3038 default: {
2785 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3039 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
2786 UNREACHABLE(); 3040 UNREACHABLE();
@@ -2911,7 +3165,7 @@ private:
2911 3165
2912 ShaderWriter shader; 3166 ShaderWriter shader;
2913 ShaderWriter declarations; 3167 ShaderWriter declarations;
2914 GLSLRegisterManager regs{shader, declarations, stage, suffix}; 3168 GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
2915 3169
2916 // Declarations 3170 // Declarations
2917 std::set<std::string> declr_predicates; 3171 std::set<std::string> declr_predicates;
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index b0466c18f..1e5eb32df 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
17 std::string out = "#version 430 core\n"; 17 std::string out = "#version 430 core\n";
18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 out += "bool exec_vertex();\n"; 20
21 out += R"(
22out gl_PerVertex {
23 vec4 gl_Position;
24};
25
26layout(std140) uniform vs_config {
27 vec4 viewport_flip;
28 uvec4 instance_id;
29 uvec4 flip_stage;
30};
31)";
21 32
22 if (setup.IsDualProgram()) { 33 if (setup.IsDualProgram()) {
23 out += "bool exec_vertex_b();\n"; 34 out += "bool exec_vertex_b();\n";
@@ -28,18 +39,17 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
28 Maxwell3D::Regs::ShaderStage::Vertex, "vertex") 39 Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
29 .get_value_or({}); 40 .get_value_or({});
30 41
31 out += R"( 42 out += program.first;
32
33out gl_PerVertex {
34 vec4 gl_Position;
35};
36 43
37out vec4 position; 44 if (setup.IsDualProgram()) {
45 ProgramResult program_b =
46 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
47 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
48 .get_value_or({});
49 out += program_b.first;
50 }
38 51
39layout (std140) uniform vs_config { 52 out += R"(
40 vec4 viewport_flip;
41 uvec4 instance_id;
42};
43 53
44void main() { 54void main() {
45 position = vec4(0.0, 0.0, 0.0, 0.0); 55 position = vec4(0.0, 0.0, 0.0, 0.0);
@@ -52,27 +62,52 @@ void main() {
52 62
53 out += R"( 63 out += R"(
54 64
55 // Viewport can be flipped, which is unsupported by glViewport 65 // Check if the flip stage is VertexB
56 position.xy *= viewport_flip.xy; 66 if (flip_stage[0] == 1) {
67 // Viewport can be flipped, which is unsupported by glViewport
68 position.xy *= viewport_flip.xy;
69 }
57 gl_Position = position; 70 gl_Position = position;
58 71
59 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 72 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
60 // For now, this is here to bring order in lieu of proper emulation 73 // For now, this is here to bring order in lieu of proper emulation
61 position.w = 1.0; 74 if (flip_stage[0] == 1) {
75 position.w = 1.0;
76 }
62} 77}
63 78
64)"; 79)";
65 80
66 out += program.first; 81 return {out, program.second};
82}
67 83
68 if (setup.IsDualProgram()) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
69 ProgramResult program_b = 85 std::string out = "#version 430 core\n";
70 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, 86 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
71 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") 87 out += Decompiler::GetCommonDeclarations();
72 .get_value_or({}); 88 out += "bool exec_geometry();\n";
73 out += program_b.first; 89
74 } 90 ProgramResult program =
91 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
92 Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
93 .get_value_or({});
94 out += R"(
95out gl_PerVertex {
96 vec4 gl_Position;
97};
75 98
99layout (std140) uniform gs_config {
100 vec4 viewport_flip;
101 uvec4 instance_id;
102 uvec4 flip_stage;
103};
104
105void main() {
106 exec_geometry();
107}
108
109)";
110 out += program.first;
76 return {out, program.second}; 111 return {out, program.second};
77} 112}
78 113
@@ -87,7 +122,6 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
87 Maxwell3D::Regs::ShaderStage::Fragment, "fragment") 122 Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
88 .get_value_or({}); 123 .get_value_or({});
89 out += R"( 124 out += R"(
90in vec4 position;
91layout(location = 0) out vec4 FragColor0; 125layout(location = 0) out vec4 FragColor0;
92layout(location = 1) out vec4 FragColor1; 126layout(location = 1) out vec4 FragColor1;
93layout(location = 2) out vec4 FragColor2; 127layout(location = 2) out vec4 FragColor2;
@@ -100,6 +134,7 @@ layout(location = 7) out vec4 FragColor7;
100layout (std140) uniform fs_config { 134layout (std140) uniform fs_config {
101 vec4 viewport_flip; 135 vec4 viewport_flip;
102 uvec4 instance_id; 136 uvec4 instance_id;
137 uvec4 flip_stage;
103}; 138};
104 139
105void main() { 140void main() {
@@ -110,5 +145,4 @@ void main() {
110 out += program.first; 145 out += program.first;
111 return {out, program.second}; 146 return {out, program.second};
112} 147}
113 148} // namespace OpenGL::GLShader \ No newline at end of file
114} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index e56f39e78..79596087a 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -196,6 +196,12 @@ private:
196ProgramResult GenerateVertexShader(const ShaderSetup& setup); 196ProgramResult GenerateVertexShader(const ShaderSetup& setup);
197 197
198/** 198/**
199 * Generates the GLSL geometry shader program source code for the given GS program
200 * @returns String of the shader source code
201 */
202ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
203
204/**
199 * Generates the GLSL fragment shader program source code for the given FS program 205 * Generates the GLSL fragment shader program source code for the given FS program
200 * @returns String of the shader source code 206 * @returns String of the shader source code
201 */ 207 */
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 022d32a86..010857ec6 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -18,6 +18,14 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
18 18
19 // We only assign the instance to the first component of the vector, the rest is just padding. 19 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 20 instance_id[0] = state.current_instance;
21
22 // Assign in which stage the position has to be flipped
23 // (the last stage before the fragment shader).
24 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
25 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
26 } else {
27 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
28 }
21} 29}
22 30
23} // namespace OpenGL::GLShader 31} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 3de15ba9b..b3a191cf2 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,9 @@ struct MaxwellUniformData {
21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage;
24}; 25};
25static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect"); 26static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
26static_assert(sizeof(MaxwellUniformData) < 16384, 27static_assert(sizeof(MaxwellUniformData) < 16384,
27 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 28 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
28 29
@@ -36,6 +37,10 @@ public:
36 vs = program; 37 vs = program;
37 } 38 }
38 39
40 void UseProgrammableGeometryShader(GLuint program) {
41 gs = program;
42 }
43
39 void UseProgrammableFragmentShader(GLuint program) { 44 void UseProgrammableFragmentShader(GLuint program) {
40 fs = program; 45 fs = program;
41 } 46 }
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..0d2456b56 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cmath> 5#include <cmath>
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/gpu.h" 10#include "video_core/gpu.h"
@@ -199,4 +200,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
199 return rgba_data; 200 return rgba_data;
200} 201}
201 202
203std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
204 u32 block_height, u32 block_depth) {
205 if (tiled) {
206 const u32 gobs_in_x = 64 / bytes_per_pixel;
207 const u32 gobs_in_y = 8;
208 const u32 gobs_in_z = 1;
209 const u32 aligned_width = Common::AlignUp(width, gobs_in_x);
210 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
211 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
212 return aligned_width * aligned_height * aligned_depth * bytes_per_pixel;
213 } else {
214 return width * height * depth * bytes_per_pixel;
215 }
216}
217
202} // namespace Tegra::Texture 218} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..234d250af 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -32,4 +32,10 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
32std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 32std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
33 u32 height); 33 u32 height);
34 34
35/**
36 * This function calculates the correct size of a texture depending if it's tiled or not.
37 */
38std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
39 u32 block_height, u32 block_depth);
40
35} // namespace Tegra::Texture 41} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 8f31d825a..58d17abcb 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -161,7 +161,9 @@ struct TICEntry {
161 BitField<21, 3, TICHeaderVersion> header_version; 161 BitField<21, 3, TICHeaderVersion> header_version;
162 }; 162 };
163 union { 163 union {
164 BitField<0, 3, u32> block_width;
164 BitField<3, 3, u32> block_height; 165 BitField<3, 3, u32> block_height;
166 BitField<6, 3, u32> block_depth;
165 167
166 // High 16 bits of the pitch value 168 // High 16 bits of the pitch value
167 BitField<0, 16, u32> pitch_high; 169 BitField<0, 16, u32> pitch_high;
@@ -202,13 +204,24 @@ struct TICEntry {
202 return depth_minus_1 + 1; 204 return depth_minus_1 + 1;
203 } 205 }
204 206
207 u32 BlockWidth() const {
208 ASSERT(IsTiled());
209 // The block height is stored in log2 format.
210 return 1 << block_width;
211 }
212
205 u32 BlockHeight() const { 213 u32 BlockHeight() const {
206 ASSERT(header_version == TICHeaderVersion::BlockLinear || 214 ASSERT(IsTiled());
207 header_version == TICHeaderVersion::BlockLinearColorKey);
208 // The block height is stored in log2 format. 215 // The block height is stored in log2 format.
209 return 1 << block_height; 216 return 1 << block_height;
210 } 217 }
211 218
219 u32 BlockDepth() const {
220 ASSERT(IsTiled());
221 // The block height is stored in log2 format.
222 return 1 << block_depth;
223 }
224
212 bool IsTiled() const { 225 bool IsTiled() const {
213 return header_version == TICHeaderVersion::BlockLinear || 226 return header_version == TICHeaderVersion::BlockLinear ||
214 header_version == TICHeaderVersion::BlockLinearColorKey; 227 header_version == TICHeaderVersion::BlockLinearColorKey;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 681919ae3..237cc1307 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -169,16 +169,20 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
169 const std::string nice_addr = fmt::format("0x{:016x}", addr); 169 const std::string nice_addr = fmt::format("0x{:016x}", addr);
170 std::string object_label; 170 std::string object_label;
171 171
172 switch (identifier) { 172 if (extra_info.empty()) {
173 case GL_TEXTURE: 173 switch (identifier) {
174 object_label = extra_info + "@" + nice_addr; 174 case GL_TEXTURE:
175 break; 175 object_label = "Texture@" + nice_addr;
176 case GL_PROGRAM: 176 break;
177 object_label = "ShaderProgram@" + nice_addr; 177 case GL_PROGRAM:
178 break; 178 object_label = "Shader@" + nice_addr;
179 default: 179 break;
180 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); 180 default:
181 break; 181 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
182 break;
183 }
184 } else {
185 object_label = extra_info + '@' + nice_addr;
182 } 186 }
183 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); 187 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
184} 188}
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4e4c108ab..e8ab23326 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -110,6 +110,7 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
110 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, 110 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name,
111 Common::g_scm_branch, Common::g_scm_desc); 111 Common::g_scm_branch, Common::g_scm_desc);
112 setWindowTitle(QString::fromStdString(window_title)); 112 setWindowTitle(QString::fromStdString(window_title));
113 setAttribute(Qt::WA_AcceptTouchEvents);
113 114
114 InputCommon::Init(); 115 InputCommon::Init();
115 InputCommon::StartJoystickEventHandler(); 116 InputCommon::StartJoystickEventHandler();
@@ -190,11 +191,17 @@ QByteArray GRenderWindow::saveGeometry() {
190 return geometry; 191 return geometry;
191} 192}
192 193
193qreal GRenderWindow::windowPixelRatio() { 194qreal GRenderWindow::windowPixelRatio() const {
194 // windowHandle() might not be accessible until the window is displayed to screen. 195 // windowHandle() might not be accessible until the window is displayed to screen.
195 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; 196 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
196} 197}
197 198
199std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
200 const qreal pixel_ratio = windowPixelRatio();
201 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
202 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
203}
204
198void GRenderWindow::closeEvent(QCloseEvent* event) { 205void GRenderWindow::closeEvent(QCloseEvent* event) {
199 emit Closed(); 206 emit Closed();
200 QWidget::closeEvent(event); 207 QWidget::closeEvent(event);
@@ -209,31 +216,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
209} 216}
210 217
211void GRenderWindow::mousePressEvent(QMouseEvent* event) { 218void GRenderWindow::mousePressEvent(QMouseEvent* event) {
219 if (event->source() == Qt::MouseEventSynthesizedBySystem)
220 return; // touch input is handled in TouchBeginEvent
221
212 auto pos = event->pos(); 222 auto pos = event->pos();
213 if (event->button() == Qt::LeftButton) { 223 if (event->button() == Qt::LeftButton) {
214 qreal pixelRatio = windowPixelRatio(); 224 const auto [x, y] = ScaleTouch(pos);
215 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), 225 this->TouchPressed(x, y);
216 static_cast<unsigned>(pos.y() * pixelRatio));
217 } else if (event->button() == Qt::RightButton) { 226 } else if (event->button() == Qt::RightButton) {
218 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); 227 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
219 } 228 }
220} 229}
221 230
222void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { 231void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
232 if (event->source() == Qt::MouseEventSynthesizedBySystem)
233 return; // touch input is handled in TouchUpdateEvent
234
223 auto pos = event->pos(); 235 auto pos = event->pos();
224 qreal pixelRatio = windowPixelRatio(); 236 const auto [x, y] = ScaleTouch(pos);
225 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), 237 this->TouchMoved(x, y);
226 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
227 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 238 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
228} 239}
229 240
230void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 241void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
242 if (event->source() == Qt::MouseEventSynthesizedBySystem)
243 return; // touch input is handled in TouchEndEvent
244
231 if (event->button() == Qt::LeftButton) 245 if (event->button() == Qt::LeftButton)
232 this->TouchReleased(); 246 this->TouchReleased();
233 else if (event->button() == Qt::RightButton) 247 else if (event->button() == Qt::RightButton)
234 InputCommon::GetMotionEmu()->EndTilt(); 248 InputCommon::GetMotionEmu()->EndTilt();
235} 249}
236 250
251void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
252 // TouchBegin always has exactly one touch point, so take the .first()
253 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
254 this->TouchPressed(x, y);
255}
256
257void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
258 QPointF pos;
259 int active_points = 0;
260
261 // average all active touch points
262 for (const auto tp : event->touchPoints()) {
263 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
264 active_points++;
265 pos += tp.pos();
266 }
267 }
268
269 pos /= active_points;
270
271 const auto [x, y] = ScaleTouch(pos);
272 this->TouchMoved(x, y);
273}
274
275void GRenderWindow::TouchEndEvent() {
276 this->TouchReleased();
277}
278
279bool GRenderWindow::event(QEvent* event) {
280 if (event->type() == QEvent::TouchBegin) {
281 TouchBeginEvent(static_cast<QTouchEvent*>(event));
282 return true;
283 } else if (event->type() == QEvent::TouchUpdate) {
284 TouchUpdateEvent(static_cast<QTouchEvent*>(event));
285 return true;
286 } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
287 TouchEndEvent();
288 return true;
289 }
290
291 return QWidget::event(event);
292}
293
237void GRenderWindow::focusOutEvent(QFocusEvent* event) { 294void GRenderWindow::focusOutEvent(QFocusEvent* event) {
238 QWidget::focusOutEvent(event); 295 QWidget::focusOutEvent(event);
239 InputCommon::GetKeyboard()->ReleaseAllKeys(); 296 InputCommon::GetKeyboard()->ReleaseAllKeys();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index f133bfadf..873985564 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -15,6 +15,7 @@
15 15
16class QKeyEvent; 16class QKeyEvent;
17class QScreen; 17class QScreen;
18class QTouchEvent;
18 19
19class GGLWidgetInternal; 20class GGLWidgetInternal;
20class GMainWindow; 21class GMainWindow;
@@ -119,7 +120,7 @@ public:
119 void restoreGeometry(const QByteArray& geometry); // overridden 120 void restoreGeometry(const QByteArray& geometry); // overridden
120 QByteArray saveGeometry(); // overridden 121 QByteArray saveGeometry(); // overridden
121 122
122 qreal windowPixelRatio(); 123 qreal windowPixelRatio() const;
123 124
124 void closeEvent(QCloseEvent* event) override; 125 void closeEvent(QCloseEvent* event) override;
125 126
@@ -130,6 +131,8 @@ public:
130 void mouseMoveEvent(QMouseEvent* event) override; 131 void mouseMoveEvent(QMouseEvent* event) override;
131 void mouseReleaseEvent(QMouseEvent* event) override; 132 void mouseReleaseEvent(QMouseEvent* event) override;
132 133
134 bool event(QEvent* event) override;
135
133 void focusOutEvent(QFocusEvent* event) override; 136 void focusOutEvent(QFocusEvent* event) override;
134 137
135 void OnClientAreaResized(unsigned width, unsigned height); 138 void OnClientAreaResized(unsigned width, unsigned height);
@@ -148,6 +151,11 @@ signals:
148 void Closed(); 151 void Closed();
149 152
150private: 153private:
154 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
155 void TouchBeginEvent(const QTouchEvent* event);
156 void TouchUpdateEvent(const QTouchEvent* event);
157 void TouchEndEvent();
158
151 void OnMinimalClientAreaChangeRequest( 159 void OnMinimalClientAreaChangeRequest(
152 const std::pair<unsigned, unsigned>& minimal_size) override; 160 const std::pair<unsigned, unsigned>& minimal_size) override;
153 161
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 650dd03c0..7fec15991 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -134,6 +134,7 @@ void Config::ReadValues() {
134 qt_config->beginGroup("Debugging"); 134 qt_config->beginGroup("Debugging");
135 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 135 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
136 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 136 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
137 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
137 qt_config->endGroup(); 138 qt_config->endGroup();
138 139
139 qt_config->beginGroup("WebService"); 140 qt_config->beginGroup("WebService");
@@ -269,6 +270,7 @@ void Config::SaveValues() {
269 qt_config->beginGroup("Debugging"); 270 qt_config->beginGroup("Debugging");
270 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 271 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
271 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 272 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
273 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
272 qt_config->endGroup(); 274 qt_config->endGroup();
273 275
274 qt_config->beginGroup("WebService"); 276 qt_config->beginGroup("WebService");
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 45d84f19a..9e765fc93 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -33,6 +33,7 @@ void ConfigureDebug::setConfiguration() {
33 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 33 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
36} 37}
37 38
38void ConfigureDebug::applyConfiguration() { 39void ConfigureDebug::applyConfiguration() {
@@ -40,6 +41,7 @@ void ConfigureDebug::applyConfiguration() {
40 Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); 41 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
41 UISettings::values.show_console = ui->toggle_console->isChecked(); 42 UISettings::values.show_console = ui->toggle_console->isChecked();
42 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 43 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
44 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
43 Debugger::ToggleConsole(); 45 Debugger::ToggleConsole();
44 Log::Filter filter; 46 Log::Filter filter;
45 filter.ParseFilterString(Settings::values.log_filter); 47 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 5ae7276bd..ff4987604 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -107,6 +107,29 @@
107 </widget> 107 </widget>
108 </item> 108 </item>
109 <item> 109 <item>
110 <widget class="QGroupBox" name="groupBox_3">
111 <property name="title">
112 <string>Homebrew</string>
113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout">
115 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout">
117 <item>
118 <widget class="QLabel" name="label">
119 <property name="text">
120 <string>Arguments String</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLineEdit" name="homebrew_args_edit"/>
126 </item>
127 </layout>
128 </item>
129 </layout>
130 </widget>
131 </item>
132 <item>
110 <spacer name="verticalSpacer"> 133 <spacer name="verticalSpacer">
111 <property name="orientation"> 134 <property name="orientation">
112 <enum>Qt::Vertical</enum> 135 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index d2b3de683..8f99a1c78 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -27,9 +27,8 @@
27#include "yuzu/ui_settings.h" 27#include "yuzu/ui_settings.h"
28 28
29namespace { 29namespace {
30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, 30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
31 const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, 31 std::vector<u8>& icon, std::string& name) {
32 std::string& name) {
33 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); 32 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
34 if (icon_file != nullptr) 33 if (icon_file != nullptr)
35 icon = icon_file->ReadAllBytes(); 34 icon = icon_file->ReadAllBytes();
@@ -110,7 +109,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
110 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
111 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
112 if (control != nullptr) 111 if (control != nullptr)
113 GetMetadataFromControlNCA(patch, control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
114 113
115 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 114 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
116 115
@@ -197,8 +196,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
197 res2 == Loader::ResultStatus::Success) { 196 res2 == Loader::ResultStatus::Success) {
198 // Use from metadata pool. 197 // Use from metadata pool.
199 if (nca_control_map.find(program_id) != nca_control_map.end()) { 198 if (nca_control_map.find(program_id) != nca_control_map.end()) {
200 const auto nca = nca_control_map[program_id]; 199 const auto& nca = nca_control_map[program_id];
201 GetMetadataFromControlNCA(patch, nca, icon, name); 200 GetMetadataFromControlNCA(patch, *nca, icon, name);
202 } 201 }
203 } 202 }
204 203
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 9d934e220..2470f4640 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -138,6 +138,7 @@ void Config::ReadValues() {
138 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 138 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
139 Settings::values.gdbstub_port = 139 Settings::values.gdbstub_port =
140 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 140 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
141 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
141 142
142 // Web Service 143 // Web Service
143 Settings::values.enable_telemetry = 144 Settings::values.enable_telemetry =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 155095095..a9ad92a80 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
40 } 40 }
41} 41}
42 42
43std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
44 int w, h;
45 SDL_GetWindowSize(render_window, &w, &h);
46
47 touch_x *= w;
48 touch_y *= h;
49
50 return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
51 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
52}
53
54void EmuWindow_SDL2::OnFingerDown(float x, float y) {
55 // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
56 // This isn't critical because the best we can do when we have that is to average them, like the
57 // 3DS does
58
59 const auto [px, py] = TouchToPixelPos(x, y);
60 TouchPressed(px, py);
61}
62
63void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
64 const auto [px, py] = TouchToPixelPos(x, y);
65 TouchMoved(px, py);
66}
67
68void EmuWindow_SDL2::OnFingerUp() {
69 TouchReleased();
70}
71
43void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 72void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
44 if (state == SDL_PRESSED) { 73 if (state == SDL_PRESSED) {
45 InputCommon::GetKeyboard()->PressKey(key); 74 InputCommon::GetKeyboard()->PressKey(key);
@@ -219,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() {
219 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); 248 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
220 break; 249 break;
221 case SDL_MOUSEMOTION: 250 case SDL_MOUSEMOTION:
222 OnMouseMotion(event.motion.x, event.motion.y); 251 // ignore if it came from touch
252 if (event.button.which != SDL_TOUCH_MOUSEID)
253 OnMouseMotion(event.motion.x, event.motion.y);
223 break; 254 break;
224 case SDL_MOUSEBUTTONDOWN: 255 case SDL_MOUSEBUTTONDOWN:
225 case SDL_MOUSEBUTTONUP: 256 case SDL_MOUSEBUTTONUP:
226 OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); 257 // ignore if it came from touch
258 if (event.button.which != SDL_TOUCH_MOUSEID) {
259 OnMouseButton(event.button.button, event.button.state, event.button.x,
260 event.button.y);
261 }
262 break;
263 case SDL_FINGERDOWN:
264 OnFingerDown(event.tfinger.x, event.tfinger.y);
265 break;
266 case SDL_FINGERMOTION:
267 OnFingerMotion(event.tfinger.x, event.tfinger.y);
268 break;
269 case SDL_FINGERUP:
270 OnFingerUp();
227 break; 271 break;
228 case SDL_QUIT: 272 case SDL_QUIT:
229 is_open = false; 273 is_open = false;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index d34902109..b0d4116cc 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -40,6 +40,18 @@ private:
40 /// Called by PollEvents when a mouse button is pressed or released 40 /// Called by PollEvents when a mouse button is pressed or released
41 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 41 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
42 42
43 /// Translates pixel position (0..1) to pixel positions
44 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
45
46 /// Called by PollEvents when a finger starts touching the touchscreen
47 void OnFingerDown(float x, float y);
48
49 /// Called by PollEvents when a finger moves while touching the touchscreen
50 void OnFingerMotion(float x, float y);
51
52 /// Called by PollEvents when a finger stops touching the touchscreen
53 void OnFingerUp();
54
43 /// Called by PollEvents when any event that may cause the window to be resized occurs 55 /// Called by PollEvents when any event that may cause the window to be resized occurs
44 void OnResize(); 56 void OnResize();
45 57
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 1d951ca3f..27aba95f6 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -56,9 +56,10 @@ static void PrintHelp(const char* argv0) {
56 std::cout << "Usage: " << argv0 56 std::cout << "Usage: " << argv0
57 << " [options] <filename>\n" 57 << " [options] <filename>\n"
58 "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" 58 "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
59 "-f, --fullscreen Start in fullscreen mode\n" 59 "-f, --fullscreen Start in fullscreen mode\n"
60 "-h, --help Display this help and exit\n" 60 "-h, --help Display this help and exit\n"
61 "-v, --version Output version information and exit\n"; 61 "-v, --version Output version information and exit\n"
62 "-p, --program Pass following string as arguments to executable\n";
62} 63}
63 64
64static void PrintVersion() { 65static void PrintVersion() {
@@ -103,15 +104,13 @@ int main(int argc, char** argv) {
103 bool fullscreen = false; 104 bool fullscreen = false;
104 105
105 static struct option long_options[] = { 106 static struct option long_options[] = {
106 {"gdbport", required_argument, 0, 'g'}, 107 {"gdbport", required_argument, 0, 'g'}, {"fullscreen", no_argument, 0, 'f'},
107 {"fullscreen", no_argument, 0, 'f'}, 108 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'},
108 {"help", no_argument, 0, 'h'}, 109 {"program", optional_argument, 0, 'p'}, {0, 0, 0, 0},
109 {"version", no_argument, 0, 'v'},
110 {0, 0, 0, 0},
111 }; 110 };
112 111
113 while (optind < argc) { 112 while (optind < argc) {
114 char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index); 113 char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
115 if (arg != -1) { 114 if (arg != -1) {
116 switch (arg) { 115 switch (arg) {
117 case 'g': 116 case 'g':
@@ -135,6 +134,10 @@ int main(int argc, char** argv) {
135 case 'v': 134 case 'v':
136 PrintVersion(); 135 PrintVersion();
137 return 0; 136 return 0;
137 case 'p':
138 Settings::values.program_args = argv[optind];
139 ++optind;
140 break;
138 } 141 }
139 } else { 142 } else {
140#ifdef _WIN32 143#ifdef _WIN32