summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/audio_renderer.h25
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt7
-rw-r--r--src/core/core.cpp25
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/file_sys/cheat_engine.cpp492
-rw-r--r--src/core/file_sys/cheat_engine.h234
-rw-r--r--src/core/file_sys/patch_manager.cpp32
-rw-r--r--src/core/file_sys/patch_manager.h6
-rw-r--r--src/core/hle/service/acc/acc.cpp14
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp19
-rw-r--r--src/core/hle/service/aoc/aoc_u.h5
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp8
-rw-r--r--src/core/hle/service/btdrv/btdrv.h6
-rw-r--r--src/core/hle/service/btm/btm.cpp14
-rw-r--r--src/core/hle/service/btm/btm.h6
-rw-r--r--src/core/hle/service/fatal/fatal.cpp29
-rw-r--r--src/core/hle/service/fatal/fatal.h9
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp4
-rw-r--r--src/core/hle/service/fatal/fatal_p.h2
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp3
-rw-r--r--src/core/hle/service/fatal/fatal_u.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp31
-rw-r--r--src/core/hle/service/friend/friend.h9
-rw-r--r--src/core/hle/service/friend/interface.cpp4
-rw-r--r--src/core/hle/service/friend/interface.h2
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h8
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h3
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp26
-rw-r--r--src/core/hle/service/hid/controllers/npad.h8
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h3
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp25
-rw-r--r--src/core/hle/service/hid/hid.h10
-rw-r--r--src/core/hle/service/hid/irs.cpp6
-rw-r--r--src/core/hle/service/hid/irs.h3
-rw-r--r--src/core/hle/service/ldr/ldr.cpp11
-rw-r--r--src/core/hle/service/ldr/ldr.h2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp19
-rw-r--r--src/core/hle/service/nfp/nfp.h5
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp4
-rw-r--r--src/core/hle/service/nfp/nfp_user.h2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp30
-rw-r--r--src/core/hle/service/nifm/nifm.h6
-rw-r--r--src/core/hle/service/nim/nim.cpp13
-rw-r--r--src/core/hle/service/nim/nim.h6
-rw-r--r--src/core/hle/service/ns/ns.cpp5
-rw-r--r--src/core/hle/service/ns/ns.h3
-rw-r--r--src/core/hle/service/ns/pl_u.cpp10
-rw-r--r--src/core/hle/service/ns/pl_u.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h5
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp75
-rw-r--r--src/core/hle/service/nvdrv/interface.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h6
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp7
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h5
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp38
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h5
-rw-r--r--src/core/hle/service/prepo/prepo.cpp14
-rw-r--r--src/core/hle/service/prepo/prepo.h6
-rw-r--r--src/core/hle/service/service.cpp26
-rw-r--r--src/core/hle/service/time/interface.cpp4
-rw-r--r--src/core/hle/service/time/interface.h2
-rw-r--r--src/core/hle/service/time/time.cpp25
-rw-r--r--src/core/hle/service/time/time.h4
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp4
-rw-r--r--src/core/hle/service/vi/display/vi_display.h2
-rw-r--r--src/core/loader/nso.cpp3
-rw-r--r--src/core/memory/cheat_engine.cpp234
-rw-r--r--src/core/memory/cheat_engine.h86
-rw-r--r--src/core/memory/dmnt_cheat_types.h58
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp1212
-rw-r--r--src/core/memory/dmnt_cheat_vm.h321
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp102
-rw-r--r--src/video_core/engines/maxwell_3d.h29
-rw-r--r--src/video_core/gpu.cpp1
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/macro_interpreter.cpp2
-rw-r--r--src/video_core/morton.cpp2
-rw-r--r--src/video_core/rasterizer_interface.h9
-rw-r--r--src/video_core/renderer_opengl/gl_framebuffer_cache.cpp27
-rw-r--r--src/video_core/renderer_opengl/gl_framebuffer_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp269
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h47
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp1
-rw-r--r--src/video_core/shader/shader_ir.cpp12
-rw-r--r--src/video_core/shader/shader_ir.h10
-rw-r--r--src/video_core/surface.cpp3
-rw-r--r--src/video_core/surface.h50
-rw-r--r--src/yuzu/configuration/configure_general.cpp1
126 files changed, 2801 insertions, 1310 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index da50a0bbc..e6f38d600 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -107,6 +107,11 @@ Stream::State AudioRenderer::GetStreamState() const {
107 return stream->GetState(); 107 return stream->GetState();
108} 108}
109 109
110static constexpr u32 VersionFromRevision(u32_le rev) {
111 // "REV7" -> 7
112 return ((rev >> 24) & 0xff) - 0x30;
113}
114
110std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 115std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
111 // Copy UpdateDataHeader struct 116 // Copy UpdateDataHeader struct
112 UpdateDataHeader config{}; 117 UpdateDataHeader config{};
@@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
166 // Copy output header 171 // Copy output header
167 UpdateDataHeader response_data{worker_params}; 172 UpdateDataHeader response_data{worker_params};
168 std::vector<u8> output_params(response_data.total_size); 173 std::vector<u8> output_params(response_data.total_size);
174 const auto audren_revision = VersionFromRevision(config.revision);
175 if (audren_revision >= 5) {
176 response_data.frame_count = 0x10;
177 response_data.total_size += 0x10;
178 }
169 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); 179 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
170 180
171 // Copy output memory pool entries 181 // Copy output memory pool entries
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 45afbe759..4f14b91cd 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -194,21 +194,24 @@ struct UpdateDataHeader {
194 mixes_size = 0x0; 194 mixes_size = 0x0;
195 sinks_size = config.sink_count * 0x20; 195 sinks_size = config.sink_count * 0x20;
196 performance_manager_size = 0x10; 196 performance_manager_size = 0x10;
197 frame_count = 0;
197 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + 198 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
198 effects_size + sinks_size + performance_manager_size; 199 effects_size + sinks_size + performance_manager_size;
199 } 200 }
200 201
201 u32_le revision; 202 u32_le revision{};
202 u32_le behavior_size; 203 u32_le behavior_size{};
203 u32_le memory_pools_size; 204 u32_le memory_pools_size{};
204 u32_le voices_size; 205 u32_le voices_size{};
205 u32_le voice_resource_size; 206 u32_le voice_resource_size{};
206 u32_le effects_size; 207 u32_le effects_size{};
207 u32_le mixes_size; 208 u32_le mixes_size{};
208 u32_le sinks_size; 209 u32_le sinks_size{};
209 u32_le performance_manager_size; 210 u32_le performance_manager_size{};
210 INSERT_PADDING_WORDS(6); 211 INSERT_PADDING_WORDS(1);
211 u32_le total_size; 212 u32_le frame_count{};
213 INSERT_PADDING_WORDS(4);
214 u32_le total_size{};
212}; 215};
213static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); 216static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
214 217
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index a03179520..1111cfbad 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -255,6 +255,7 @@ void DebuggerBackend::Write(const Entry& entry) {
255 CLS(Input) \ 255 CLS(Input) \
256 CLS(Network) \ 256 CLS(Network) \
257 CLS(Loader) \ 257 CLS(Loader) \
258 CLS(CheatEngine) \
258 CLS(Crypto) \ 259 CLS(Crypto) \
259 CLS(WebService) 260 CLS(WebService)
260 261
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 8ed6d5050..259708116 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -117,6 +117,7 @@ enum class Class : ClassType {
117 Audio_DSP, ///< The HLE implementation of the DSP 117 Audio_DSP, ///< The HLE implementation of the DSP
118 Audio_Sink, ///< Emulator audio output backend 118 Audio_Sink, ///< Emulator audio output backend
119 Loader, ///< ROM loader 119 Loader, ///< ROM loader
120 CheatEngine, ///< Memory manipulation and engine VM functions
120 Crypto, ///< Cryptographic engine/functions 121 Crypto, ///< Cryptographic engine/functions
121 Input, ///< Input emulation 122 Input, ///< Input emulation
122 Network, ///< Network emulation 123 Network, ///< Network emulation
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 877a9e353..a6b56c9c6 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,8 +33,6 @@ add_library(core STATIC
33 file_sys/bis_factory.h 33 file_sys/bis_factory.h
34 file_sys/card_image.cpp 34 file_sys/card_image.cpp
35 file_sys/card_image.h 35 file_sys/card_image.h
36 file_sys/cheat_engine.cpp
37 file_sys/cheat_engine.h
38 file_sys/content_archive.cpp 36 file_sys/content_archive.cpp
39 file_sys/content_archive.h 37 file_sys/content_archive.h
40 file_sys/control_metadata.cpp 38 file_sys/control_metadata.cpp
@@ -477,6 +475,11 @@ add_library(core STATIC
477 loader/nsp.h 475 loader/nsp.h
478 loader/xci.cpp 476 loader/xci.cpp
479 loader/xci.h 477 loader/xci.h
478 memory/cheat_engine.cpp
479 memory/cheat_engine.h
480 memory/dmnt_cheat_types.h
481 memory/dmnt_cheat_vm.cpp
482 memory/dmnt_cheat_vm.h
480 memory.cpp 483 memory.cpp
481 memory.h 484 memory.h
482 memory_setup.h 485 memory_setup.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c1bc92782..92ba42fb9 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
17#include "core/file_sys/bis_factory.h" 17#include "core/file_sys/bis_factory.h"
18#include "core/file_sys/card_image.h" 18#include "core/file_sys/card_image.h"
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/registered_cache.h" 21#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/romfs_factory.h" 22#include "core/file_sys/romfs_factory.h"
22#include "core/file_sys/savedata_factory.h" 23#include "core/file_sys/savedata_factory.h"
@@ -37,13 +38,12 @@
37#include "core/hle/service/service.h" 38#include "core/hle/service/service.h"
38#include "core/hle/service/sm/sm.h" 39#include "core/hle/service/sm/sm.h"
39#include "core/loader/loader.h" 40#include "core/loader/loader.h"
41#include "core/memory/cheat_engine.h"
40#include "core/perf_stats.h" 42#include "core/perf_stats.h"
41#include "core/reporter.h" 43#include "core/reporter.h"
42#include "core/settings.h" 44#include "core/settings.h"
43#include "core/telemetry_session.h" 45#include "core/telemetry_session.h"
44#include "core/tools/freezer.h" 46#include "core/tools/freezer.h"
45#include "file_sys/cheat_engine.h"
46#include "file_sys/patch_manager.h"
47#include "video_core/debug_utils/debug_utils.h" 47#include "video_core/debug_utils/debug_utils.h"
48#include "video_core/renderer_base.h" 48#include "video_core/renderer_base.h"
49#include "video_core/video_core.h" 49#include "video_core/video_core.h"
@@ -205,6 +205,11 @@ struct System::Impl {
205 gpu_core->Start(); 205 gpu_core->Start();
206 cpu_core_manager.StartThreads(); 206 cpu_core_manager.StartThreads();
207 207
208 // Initialize cheat engine
209 if (cheat_engine) {
210 cheat_engine->Initialize();
211 }
212
208 // All threads are started, begin main process execution, now that we're in the clear. 213 // All threads are started, begin main process execution, now that we're in the clear.
209 main_process->Run(load_parameters->main_thread_priority, 214 main_process->Run(load_parameters->main_thread_priority,
210 load_parameters->main_thread_stack_size); 215 load_parameters->main_thread_stack_size);
@@ -332,7 +337,7 @@ struct System::Impl {
332 bool is_powered_on = false; 337 bool is_powered_on = false;
333 bool exit_lock = false; 338 bool exit_lock = false;
334 339
335 std::unique_ptr<FileSys::CheatEngine> cheat_engine; 340 std::unique_ptr<Memory::CheatEngine> cheat_engine;
336 std::unique_ptr<Tools::Freezer> memory_freezer; 341 std::unique_ptr<Tools::Freezer> memory_freezer;
337 342
338 /// Frontend applets 343 /// Frontend applets
@@ -547,13 +552,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
547 return impl->debug_context.get(); 552 return impl->debug_context.get();
548} 553}
549 554
550void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
551 const std::string& build_id, VAddr code_region_start,
552 VAddr code_region_end) {
553 impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
554 code_region_start, code_region_end);
555}
556
557void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 555void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
558 impl->virtual_filesystem = std::move(vfs); 556 impl->virtual_filesystem = std::move(vfs);
559} 557}
@@ -562,6 +560,13 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
562 return impl->virtual_filesystem; 560 return impl->virtual_filesystem;
563} 561}
564 562
563void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
564 const std::array<u8, 32>& build_id, VAddr main_region_begin,
565 u64 main_region_size) {
566 impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
567 impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
568}
569
565void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { 570void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
566 impl->applet_manager.SetAppletFrontendSet(std::move(set)); 571 impl->applet_manager.SetAppletFrontendSet(std::move(set));
567} 572}
diff --git a/src/core/core.h b/src/core/core.h
index 9874ee487..ff10ebe12 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -18,7 +18,6 @@ class EmuWindow;
18} // namespace Core::Frontend 18} // namespace Core::Frontend
19 19
20namespace FileSys { 20namespace FileSys {
21class CheatList;
22class ContentProvider; 21class ContentProvider;
23class ContentProviderUnion; 22class ContentProviderUnion;
24enum class ContentProviderUnionSlot; 23enum class ContentProviderUnionSlot;
@@ -36,6 +35,10 @@ class AppLoader;
36enum class ResultStatus : u16; 35enum class ResultStatus : u16;
37} // namespace Loader 36} // namespace Loader
38 37
38namespace Memory {
39struct CheatEntry;
40} // namespace Memory
41
39namespace Service { 42namespace Service {
40 43
41namespace AM::Applets { 44namespace AM::Applets {
@@ -286,8 +289,9 @@ public:
286 289
287 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 290 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
288 291
289 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, 292 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
290 VAddr code_region_start, VAddr code_region_end); 293 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
294 u64 main_region_size);
291 295
292 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 296 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
293 297
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp
deleted file mode 100644
index b06c2f20a..000000000
--- a/src/core/file_sys/cheat_engine.cpp
+++ /dev/null
@@ -1,492 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <locale>
6#include "common/hex_util.h"
7#include "common/microprofile.h"
8#include "common/swap.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
12#include "core/file_sys/cheat_engine.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/service/hid/controllers/npad.h"
15#include "core/hle/service/hid/hid.h"
16#include "core/hle/service/sm/sm.h"
17
18namespace FileSys {
19
20constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
21constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
22
23u64 Cheat::Address() const {
24 u64 out;
25 std::memcpy(&out, raw.data(), sizeof(u64));
26 return Common::swap64(out) & 0xFFFFFFFFFF;
27}
28
29u64 Cheat::ValueWidth(u64 offset) const {
30 return Value(offset, width);
31}
32
33u64 Cheat::Value(u64 offset, u64 width) const {
34 u64 out;
35 std::memcpy(&out, raw.data() + offset, sizeof(u64));
36 out = Common::swap64(out);
37 if (width == 8)
38 return out;
39 return out & ((1ull << (width * CHAR_BIT)) - 1);
40}
41
42u32 Cheat::KeypadValue() const {
43 u32 out;
44 std::memcpy(&out, raw.data(), sizeof(u32));
45 return Common::swap32(out) & 0x0FFFFFFF;
46}
47
48void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
49 VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
50 this->main_region_begin = main_begin;
51 this->main_region_end = main_end;
52 this->heap_region_begin = heap_begin;
53 this->heap_region_end = heap_end;
54 this->writer = writer;
55 this->reader = reader;
56}
57
58MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
59
60void CheatList::Execute() {
61 MICROPROFILE_SCOPE(Cheat_Engine);
62
63 std::fill(scratch.begin(), scratch.end(), 0);
64 in_standard = false;
65 for (std::size_t i = 0; i < master_list.size(); ++i) {
66 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
67 current_block = i;
68 ExecuteBlock(master_list[i].second);
69 }
70
71 in_standard = true;
72 for (std::size_t i = 0; i < standard_list.size(); ++i) {
73 LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
74 current_block = i;
75 ExecuteBlock(standard_list[i].second);
76 }
77}
78
79CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
80 : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
81
82bool CheatList::EvaluateConditional(const Cheat& cheat) const {
83 using ComparisonFunction = bool (*)(u64, u64);
84 constexpr std::array<ComparisonFunction, 6> comparison_functions{
85 [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
86 [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
87 [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
88 };
89
90 if (cheat.type == CodeType::ConditionalInput) {
91 const auto applet_resource =
92 system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
93 if (applet_resource == nullptr) {
94 LOG_WARNING(
95 Common_Filesystem,
96 "Attempted to evaluate input conditional, but applet resource is not initialized!");
97 return false;
98 }
99
100 const auto press_state =
101 applet_resource
102 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
103 .GetAndResetPressState();
104 return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
105 }
106
107 ASSERT(cheat.type == CodeType::Conditional);
108
109 const auto offset =
110 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
111 ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
112 auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
113 const auto addr = cheat.Address() + offset;
114
115 return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
116}
117
118void CheatList::ProcessBlockPairs(const Block& block) {
119 block_pairs.clear();
120
121 u64 scope = 0;
122 std::map<u64, u64> pairs;
123
124 for (std::size_t i = 0; i < block.size(); ++i) {
125 const auto& cheat = block[i];
126
127 switch (cheat.type) {
128 case CodeType::Conditional:
129 case CodeType::ConditionalInput:
130 pairs.insert_or_assign(scope, i);
131 ++scope;
132 break;
133 case CodeType::EndConditional: {
134 --scope;
135 const auto idx = pairs.at(scope);
136 block_pairs.insert_or_assign(idx, i);
137 break;
138 }
139 case CodeType::Loop: {
140 if (cheat.end_of_loop) {
141 --scope;
142 const auto idx = pairs.at(scope);
143 block_pairs.insert_or_assign(idx, i);
144 } else {
145 pairs.insert_or_assign(scope, i);
146 ++scope;
147 }
148 break;
149 }
150 }
151 }
152}
153
154void CheatList::WriteImmediate(const Cheat& cheat) {
155 const auto offset =
156 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
157 const auto& register_3 = scratch.at(cheat.register_3);
158
159 const auto addr = cheat.Address() + offset + register_3;
160 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
161 cheat.Value(8, cheat.width));
162 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
163}
164
165void CheatList::BeginConditional(const Cheat& cheat) {
166 if (EvaluateConditional(cheat)) {
167 return;
168 }
169
170 const auto iter = block_pairs.find(current_index);
171 ASSERT(iter != block_pairs.end());
172 current_index = iter->second - 1;
173}
174
175void CheatList::EndConditional(const Cheat& cheat) {
176 LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
177}
178
179void CheatList::Loop(const Cheat& cheat) {
180 if (cheat.end_of_loop.Value())
181 ASSERT(!cheat.end_of_loop.Value());
182
183 auto& register_3 = scratch.at(cheat.register_3);
184 const auto iter = block_pairs.find(current_index);
185 ASSERT(iter != block_pairs.end());
186 ASSERT(iter->first < iter->second);
187
188 const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
189 for (s32 i = initial_value; i >= 0; --i) {
190 register_3 = static_cast<u64>(i);
191 for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
192 current_index = c;
193 ExecuteSingleCheat(
194 (in_standard ? standard_list : master_list)[current_block].second[c]);
195 }
196 }
197
198 current_index = iter->second;
199}
200
201void CheatList::LoadImmediate(const Cheat& cheat) {
202 auto& register_3 = scratch.at(cheat.register_3);
203
204 LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
205 cheat.Value(4, 8));
206 register_3 = cheat.Value(4, 8);
207}
208
209void CheatList::LoadIndexed(const Cheat& cheat) {
210 const auto offset =
211 cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
212 auto& register_3 = scratch.at(cheat.register_3);
213
214 const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
215 LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
216 cheat.register_3, addr);
217 register_3 = reader(cheat.width, SanitizeAddress(addr));
218}
219
220void CheatList::StoreIndexed(const Cheat& cheat) {
221 const auto& register_3 = scratch.at(cheat.register_3);
222
223 const auto addr =
224 register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
225 LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
226 cheat.Value(4, cheat.width), addr);
227 writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
228}
229
230void CheatList::RegisterArithmetic(const Cheat& cheat) {
231 using ArithmeticFunction = u64 (*)(u64, u64);
232 constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
233 [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
234 [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
235 [](u64 a, u64 b) { return a >> b; },
236 };
237
238 using ArithmeticOverflowCheck = bool (*)(u64, u64);
239 constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
240 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
241 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
242 [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
243 [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
244 [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
245 };
246
247 static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
248 "Missing or have extra arithmetic overflow checks compared to functions!");
249
250 auto& register_3 = scratch.at(cheat.register_3);
251
252 ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
253 auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
254 auto* overflow_function =
255 arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
256 LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
257 cheat.register_3, cheat.ValueWidth(4));
258
259 if (overflow_function(register_3, cheat.ValueWidth(4))) {
260 LOG_WARNING(Common_Filesystem,
261 "overflow will occur when performing arithmetic operation={:02X} with operands "
262 "a={:016X}, b={:016X}!",
263 static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
264 }
265
266 register_3 = function(register_3, cheat.ValueWidth(4));
267}
268
269void CheatList::BeginConditionalInput(const Cheat& cheat) {
270 if (EvaluateConditional(cheat))
271 return;
272
273 const auto iter = block_pairs.find(current_index);
274 ASSERT(iter != block_pairs.end());
275 current_index = iter->second - 1;
276}
277
278VAddr CheatList::SanitizeAddress(VAddr in) const {
279 if ((in < main_region_begin || in >= main_region_end) &&
280 (in < heap_region_begin || in >= heap_region_end)) {
281 LOG_ERROR(Common_Filesystem,
282 "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
283 "the cheat may be incorrect. However, this may be normal early in execution if "
284 "the game has not properly set up yet.",
285 in);
286 return 0; ///< Invalid addresses will hard crash
287 }
288
289 return in;
290}
291
292void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
293 using CheatOperationFunction = void (CheatList::*)(const Cheat&);
294 constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
295 &CheatList::WriteImmediate, &CheatList::BeginConditional,
296 &CheatList::EndConditional, &CheatList::Loop,
297 &CheatList::LoadImmediate, &CheatList::LoadIndexed,
298 &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
299 &CheatList::BeginConditionalInput,
300 };
301
302 const auto index = static_cast<u8>(cheat.type.Value());
303 ASSERT(index < sizeof(cheat_operation_functions));
304 const auto op = cheat_operation_functions[index];
305 (this->*op)(cheat);
306}
307
308void CheatList::ExecuteBlock(const Block& block) {
309 encountered_loops.clear();
310
311 ProcessBlockPairs(block);
312 for (std::size_t i = 0; i < block.size(); ++i) {
313 current_index = i;
314 ExecuteSingleCheat(block[i]);
315 i = current_index;
316 }
317}
318
319CheatParser::~CheatParser() = default;
320
321CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
322 CheatList::ProgramSegment standard) const {
323 return {system, std::move(master), std::move(standard)};
324}
325
326TextCheatParser::~TextCheatParser() = default;
327
328CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
329 std::stringstream ss;
330 ss.write(reinterpret_cast<const char*>(data.data()), data.size());
331
332 std::vector<std::string> lines;
333 std::string stream_line;
334 while (std::getline(ss, stream_line)) {
335 // Remove a trailing \r
336 if (!stream_line.empty() && stream_line.back() == '\r')
337 stream_line.pop_back();
338 lines.push_back(std::move(stream_line));
339 }
340
341 CheatList::ProgramSegment master_list;
342 CheatList::ProgramSegment standard_list;
343
344 for (std::size_t i = 0; i < lines.size(); ++i) {
345 auto line = lines[i];
346
347 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
348 const auto master = line[0] == '{';
349 const auto begin = master ? line.find('{') : line.find('[');
350 const auto end = master ? line.rfind('}') : line.rfind(']');
351
352 ASSERT(begin != std::string::npos && end != std::string::npos);
353
354 const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
355 CheatList::Block block{};
356
357 while (i < lines.size() - 1) {
358 line = lines[++i];
359 if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
360 --i;
361 break;
362 }
363
364 if (line.size() < 8)
365 continue;
366
367 Cheat out{};
368 out.raw = ParseSingleLineCheat(line);
369 block.push_back(out);
370 }
371
372 (master ? master_list : standard_list).emplace_back(patch_name, block);
373 }
374 }
375
376 return MakeCheatList(system, master_list, standard_list);
377}
378
379std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
380 std::array<u8, 16> out{};
381
382 if (line.size() < 8)
383 return out;
384
385 const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
386 std::memcpy(out.data(), word1.data(), sizeof(u32));
387
388 if (line.size() < 17 || line[8] != ' ')
389 return out;
390
391 const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
392 std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
393
394 if (line.size() < 26 || line[17] != ' ') {
395 // Perform shifting in case value is truncated early.
396 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
397 if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
398 type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
399 std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
400 std::memset(out.data() + 4, 0, sizeof(u32));
401 }
402
403 return out;
404 }
405
406 const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
407 std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
408
409 if (line.size() < 35 || line[26] != ' ') {
410 // Perform shifting in case value is truncated early.
411 const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
412 if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
413 std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
414 std::memset(out.data() + 8, 0, sizeof(u32));
415 }
416
417 return out;
418 }
419
420 const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
421 std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
422
423 return out;
424}
425
426namespace {
427u64 MemoryReadImpl(u32 width, VAddr addr) {
428 switch (width) {
429 case 1:
430 return Memory::Read8(addr);
431 case 2:
432 return Memory::Read16(addr);
433 case 4:
434 return Memory::Read32(addr);
435 case 8:
436 return Memory::Read64(addr);
437 default:
438 UNREACHABLE();
439 return 0;
440 }
441}
442
443void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
444 switch (width) {
445 case 1:
446 Memory::Write8(addr, static_cast<u8>(value));
447 break;
448 case 2:
449 Memory::Write16(addr, static_cast<u16>(value));
450 break;
451 case 4:
452 Memory::Write32(addr, static_cast<u32>(value));
453 break;
454 case 8:
455 Memory::Write64(addr, value);
456 break;
457 default:
458 UNREACHABLE();
459 }
460}
461} // Anonymous namespace
462
463CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
464 const std::string& build_id, VAddr code_region_start,
465 VAddr code_region_end)
466 : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
467 event = core_timing.RegisterEvent(
468 "CheatEngine::FrameCallback::" + build_id,
469 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
470 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
471
472 const auto& vm_manager = system.CurrentProcess()->VMManager();
473 for (auto& list : this->cheats) {
474 list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
475 code_region_end, vm_manager.GetHeapRegionEndAddress(),
476 &MemoryWriteImpl, &MemoryReadImpl);
477 }
478}
479
480CheatEngine::~CheatEngine() {
481 core_timing.UnscheduleEvent(event, 0);
482}
483
484void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
485 for (auto& list : cheats) {
486 list.Execute();
487 }
488
489 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
490}
491
492} // namespace FileSys
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h
deleted file mode 100644
index ac22a82cb..000000000
--- a/src/core/file_sys/cheat_engine.h
+++ /dev/null
@@ -1,234 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <set>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18class CoreTiming;
19struct EventType;
20} // namespace Core::Timing
21
22namespace FileSys {
23
24enum class CodeType : u32 {
25 // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
26 // Writes a T sized value Y to the address A added to the value of register R in memory domain M
27 WriteImmediate = 0,
28
29 // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
30 // Compares the T sized value Y to the value at address A in memory domain M using the
31 // conditional function C. If success, continues execution. If failure, jumps to the matching
32 // EndConditional statement.
33 Conditional = 1,
34
35 // 20000000
36 // Terminates a Conditional or ConditionalInput block.
37 EndConditional = 2,
38
39 // 300R0000 VVVVVVVV
40 // Starts looping V times, storing the current count in register R.
41 // Loop block is terminated with a matching 310R0000.
42 Loop = 3,
43
44 // 400R0000 VVVVVVVV VVVVVVVV
45 // Sets the value of register R to the value V.
46 LoadImmediate = 4,
47
48 // 5TMRI0AA AAAAAAAA
49 // Sets the value of register R to the value of width T at address A in memory domain M, with
50 // the current value of R added to the address if I == 1.
51 LoadIndexed = 5,
52
53 // 6T0RIFG0 VVVVVVVV VVVVVVVV
54 // Writes the value V of width T to the memory address stored in register R. Adds the value of
55 // register G to the final calculation if F is nonzero. Increments the value of register R by T
56 // after operation if I is nonzero.
57 StoreIndexed = 6,
58
59 // 7T0RA000 VVVVVVVV
60 // Performs the arithmetic operation A on the value in register R and the value V of width T,
61 // storing the result in register R.
62 RegisterArithmetic = 7,
63
64 // 8KKKKKKK
65 // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
66 // execution continues. If none are, execution skips to the next EndConditional command.
67 ConditionalInput = 8,
68};
69
70enum class MemoryType : u32 {
71 // Addressed relative to start of main NSO
72 MainNSO = 0,
73
74 // Addressed relative to start of heap
75 Heap = 1,
76};
77
78enum class ArithmeticOp : u32 {
79 Add = 0,
80 Sub = 1,
81 Mult = 2,
82 LShift = 3,
83 RShift = 4,
84};
85
86enum class ComparisonOp : u32 {
87 GreaterThan = 1,
88 GreaterThanEqual = 2,
89 LessThan = 3,
90 LessThanEqual = 4,
91 Equal = 5,
92 Inequal = 6,
93};
94
95union Cheat {
96 std::array<u8, 16> raw;
97
98 BitField<4, 4, CodeType> type;
99 BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
100 BitField<0, 4, u32> end_of_loop;
101 BitField<12, 4, MemoryType> memory_type;
102 BitField<8, 4, u32> register_3;
103 BitField<8, 4, ComparisonOp> comparison_op;
104 BitField<20, 4, u32> load_from_register;
105 BitField<20, 4, u32> increment_register;
106 BitField<20, 4, ArithmeticOp> arithmetic_op;
107 BitField<16, 4, u32> add_additional_register;
108 BitField<28, 4, u32> register_6;
109
110 u64 Address() const;
111 u64 ValueWidth(u64 offset) const;
112 u64 Value(u64 offset, u64 width) const;
113 u32 KeypadValue() const;
114};
115
116class CheatParser;
117
118// Represents a full collection of cheats for a game. The Execute function should be called every
119// interval that all cheats should be executed. Clients should not directly instantiate this class
120// (hence private constructor), they should instead receive an instance from CheatParser, which
121// guarantees the list is always in an acceptable state.
122class CheatList {
123public:
124 friend class CheatParser;
125
126 using Block = std::vector<Cheat>;
127 using ProgramSegment = std::vector<std::pair<std::string, Block>>;
128
129 // (width in bytes, address, value)
130 using MemoryWriter = void (*)(u32, VAddr, u64);
131 // (width in bytes, address) -> value
132 using MemoryReader = u64 (*)(u32, VAddr);
133
134 void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
135 MemoryWriter writer, MemoryReader reader);
136
137 void Execute();
138
139private:
140 CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
141
142 void ProcessBlockPairs(const Block& block);
143 void ExecuteSingleCheat(const Cheat& cheat);
144
145 void ExecuteBlock(const Block& block);
146
147 bool EvaluateConditional(const Cheat& cheat) const;
148
149 // Individual cheat operations
150 void WriteImmediate(const Cheat& cheat);
151 void BeginConditional(const Cheat& cheat);
152 void EndConditional(const Cheat& cheat);
153 void Loop(const Cheat& cheat);
154 void LoadImmediate(const Cheat& cheat);
155 void LoadIndexed(const Cheat& cheat);
156 void StoreIndexed(const Cheat& cheat);
157 void RegisterArithmetic(const Cheat& cheat);
158 void BeginConditionalInput(const Cheat& cheat);
159
160 VAddr SanitizeAddress(VAddr in) const;
161
162 // Master Codes are defined as codes that cannot be disabled and are run prior to all
163 // others.
164 ProgramSegment master_list;
165 // All other codes
166 ProgramSegment standard_list;
167
168 bool in_standard = false;
169
170 // 16 (0x0-0xF) scratch registers that can be used by cheats
171 std::array<u64, 16> scratch{};
172
173 MemoryWriter writer = nullptr;
174 MemoryReader reader = nullptr;
175
176 u64 main_region_begin{};
177 u64 heap_region_begin{};
178 u64 main_region_end{};
179 u64 heap_region_end{};
180
181 u64 current_block{};
182 // The current index of the cheat within the current Block
183 u64 current_index{};
184
185 // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
186 // pushed onto this queue. When a end block is encountered, the condition is checked.
187 std::map<u64, u64> block_pairs;
188
189 std::set<u64> encountered_loops;
190
191 const Core::System* system;
192};
193
194// Intermediary class that parses a text file or other disk format for storing cheats into a
195// CheatList object, that can be used for execution.
196class CheatParser {
197public:
198 virtual ~CheatParser();
199
200 virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
201
202protected:
203 CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
204 CheatList::ProgramSegment standard) const;
205};
206
207// CheatParser implementation that parses text files
208class TextCheatParser final : public CheatParser {
209public:
210 ~TextCheatParser() override;
211
212 CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
213
214private:
215 std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
216};
217
218// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
219class CheatEngine final {
220public:
221 CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
222 VAddr code_region_start, VAddr code_region_end);
223 ~CheatEngine();
224
225private:
226 void FrameCallback(u64 userdata, s64 cycles_late);
227
228 std::vector<CheatList> cheats;
229
230 Core::Timing::EventType* event;
231 Core::Timing::CoreTiming& core_timing;
232};
233
234} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index c1dd0c6d7..df0ecb15c 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -22,6 +22,7 @@
22#include "core/hle/service/filesystem/filesystem.h" 22#include "core/hle/service/filesystem/filesystem.h"
23#include "core/loader/loader.h" 23#include "core/loader/loader.h"
24#include "core/loader/nso.h" 24#include "core/loader/nso.h"
25#include "core/memory/cheat_engine.h"
25#include "core/settings.h" 26#include "core/settings.h"
26 27
27namespace FileSys { 28namespace FileSys {
@@ -247,9 +248,10 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
247 return !CollectPatches(patch_dirs, build_id).empty(); 248 return !CollectPatches(patch_dirs, build_id).empty();
248} 249}
249 250
250static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id, 251namespace {
251 const std::array<u8, 0x20>& build_id_, 252std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
252 const VirtualDir& base_path, bool upper) { 253 const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
254 const VirtualDir& base_path, bool upper) {
253 const auto build_id_raw = Common::HexToString(build_id_, upper); 255 const auto build_id_raw = Common::HexToString(build_id_, upper);
254 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); 256 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
255 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); 257 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -267,14 +269,16 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
267 return std::nullopt; 269 return std::nullopt;
268 } 270 }
269 271
270 TextCheatParser parser; 272 Memory::TextCheatParser parser;
271 return parser.Parse(system, data); 273 return parser.Parse(
274 system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
272} 275}
273 276
274std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, 277} // Anonymous namespace
275 const std::array<u8, 32>& build_id_) const { 278
276 const auto load_dir = 279std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
277 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); 280 const Core::System& system, const std::array<u8, 32>& build_id_) const {
281 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
278 if (load_dir == nullptr) { 282 if (load_dir == nullptr) {
279 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 283 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
280 return {}; 284 return {};
@@ -284,20 +288,20 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
284 std::sort(patch_dirs.begin(), patch_dirs.end(), 288 std::sort(patch_dirs.begin(), patch_dirs.end(),
285 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 289 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
286 290
287 std::vector<CheatList> out; 291 std::vector<Memory::CheatEntry> out;
288 out.reserve(patch_dirs.size());
289 for (const auto& subdir : patch_dirs) { 292 for (const auto& subdir : patch_dirs) {
290 auto cheats_dir = subdir->GetSubdirectory("cheats"); 293 auto cheats_dir = subdir->GetSubdirectory("cheats");
291 if (cheats_dir != nullptr) { 294 if (cheats_dir != nullptr) {
292 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); 295 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
293 if (res.has_value()) { 296 if (res.has_value()) {
294 out.push_back(std::move(*res)); 297 std::copy(res->begin(), res->end(), std::back_inserter(out));
295 continue; 298 continue;
296 } 299 }
297 300
298 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); 301 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
299 if (res.has_value()) 302 if (res.has_value()) {
300 out.push_back(std::move(*res)); 303 std::copy(res->begin(), res->end(), std::back_inserter(out));
304 }
301 } 305 }
302 } 306 }
303 307
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index a363c6577..e857e6e82 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -8,9 +8,9 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/file_sys/cheat_engine.h"
12#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
13#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
13#include "core/memory/dmnt_cheat_types.h"
14 14
15namespace Core { 15namespace Core {
16class System; 16class System;
@@ -51,8 +51,8 @@ public:
51 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 51 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
52 52
53 // Creates a CheatList object with all 53 // Creates a CheatList object with all
54 std::vector<CheatList> CreateCheatList(const Core::System& system, 54 std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system,
55 const std::array<u8, 0x20>& build_id) const; 55 const std::array<u8, 0x20>& build_id) const;
56 56
57 // Currently tracked RomFS patches: 57 // Currently tracked RomFS patches:
58 // - Game Updates 58 // - Game Updates
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index a7c55e116..0c0f7ed6e 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -70,7 +70,7 @@ public:
70 70
71protected: 71protected:
72 void Get(Kernel::HLERequestContext& ctx) { 72 void Get(Kernel::HLERequestContext& ctx) {
73 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 73 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
74 ProfileBase profile_base{}; 74 ProfileBase profile_base{};
75 ProfileData data{}; 75 ProfileData data{};
76 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 76 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -89,7 +89,7 @@ protected:
89 } 89 }
90 90
91 void GetBase(Kernel::HLERequestContext& ctx) { 91 void GetBase(Kernel::HLERequestContext& ctx) {
92 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 92 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
93 ProfileBase profile_base{}; 93 ProfileBase profile_base{};
94 if (profile_manager.GetProfileBase(user_id, profile_base)) { 94 if (profile_manager.GetProfileBase(user_id, profile_base)) {
95 IPC::ResponseBuilder rb{ctx, 16}; 95 IPC::ResponseBuilder rb{ctx, 16};
@@ -263,7 +263,7 @@ private:
263}; 263};
264 264
265void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { 265void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
266 LOG_INFO(Service_ACC, "called"); 266 LOG_DEBUG(Service_ACC, "called");
267 IPC::ResponseBuilder rb{ctx, 3}; 267 IPC::ResponseBuilder rb{ctx, 3};
268 rb.Push(RESULT_SUCCESS); 268 rb.Push(RESULT_SUCCESS);
269 rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); 269 rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
@@ -272,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
272void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 272void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
273 IPC::RequestParser rp{ctx}; 273 IPC::RequestParser rp{ctx};
274 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 274 Common::UUID user_id = rp.PopRaw<Common::UUID>();
275 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 275 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
276 276
277 IPC::ResponseBuilder rb{ctx, 3}; 277 IPC::ResponseBuilder rb{ctx, 3};
278 rb.Push(RESULT_SUCCESS); 278 rb.Push(RESULT_SUCCESS);
@@ -280,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
280} 280}
281 281
282void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { 282void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
283 LOG_INFO(Service_ACC, "called"); 283 LOG_DEBUG(Service_ACC, "called");
284 ctx.WriteBuffer(profile_manager->GetAllUsers()); 284 ctx.WriteBuffer(profile_manager->GetAllUsers());
285 IPC::ResponseBuilder rb{ctx, 2}; 285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(RESULT_SUCCESS); 286 rb.Push(RESULT_SUCCESS);
287} 287}
288 288
289void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { 289void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
290 LOG_INFO(Service_ACC, "called"); 290 LOG_DEBUG(Service_ACC, "called");
291 ctx.WriteBuffer(profile_manager->GetOpenUsers()); 291 ctx.WriteBuffer(profile_manager->GetOpenUsers());
292 IPC::ResponseBuilder rb{ctx, 2}; 292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(RESULT_SUCCESS); 293 rb.Push(RESULT_SUCCESS);
294} 294}
295 295
296void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { 296void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
297 LOG_INFO(Service_ACC, "called"); 297 LOG_DEBUG(Service_ACC, "called");
298 IPC::ResponseBuilder rb{ctx, 6}; 298 IPC::ResponseBuilder rb{ctx, 6};
299 rb.Push(RESULT_SUCCESS); 299 rb.Push(RESULT_SUCCESS);
300 rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); 300 rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index c4ddc7c69..797c9a06f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1083,7 +1083,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1083 1083
1084 RegisterHandlers(functions); 1084 RegisterHandlers(functions);
1085 1085
1086 auto& kernel = Core::System::GetInstance().Kernel(); 1086 auto& kernel = system.Kernel();
1087 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( 1087 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
1088 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); 1088 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
1089} 1089}
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index d3e97776b..e9cf1e840 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -29,9 +29,9 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
29 return (title_id & DLC_BASE_TITLE_ID_MASK) == base; 29 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
30} 30}
31 31
32static std::vector<u64> AccumulateAOCTitleIDs() { 32static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
33 std::vector<u64> add_on_content; 33 std::vector<u64> add_on_content;
34 const auto& rcu = Core::System::GetInstance().GetContentProvider(); 34 const auto& rcu = system.GetContentProvider();
35 const auto list = 35 const auto list =
36 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 36 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
37 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), 37 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
@@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
47 return add_on_content; 47 return add_on_content;
48} 48}
49 49
50AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { 50AOC_U::AOC_U(Core::System& system)
51 : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) {
51 // clang-format off 52 // clang-format off
52 static const FunctionInfo functions[] = { 53 static const FunctionInfo functions[] = {
53 {0, nullptr, "CountAddOnContentByApplicationId"}, 54 {0, nullptr, "CountAddOnContentByApplicationId"},
@@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
65 66
66 RegisterHandlers(functions); 67 RegisterHandlers(functions);
67 68
68 auto& kernel = Core::System::GetInstance().Kernel(); 69 auto& kernel = system.Kernel();
69 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 70 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
70 "GetAddOnContentListChanged:Event"); 71 "GetAddOnContentListChanged:Event");
71} 72}
@@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
86 IPC::ResponseBuilder rb{ctx, 3}; 87 IPC::ResponseBuilder rb{ctx, 3};
87 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
88 89
89 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 90 const auto current = system.CurrentProcess()->GetTitleID();
90 91
91 const auto& disabled = Settings::values.disabled_addons[current]; 92 const auto& disabled = Settings::values.disabled_addons[current];
92 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { 93 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
@@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
113 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, 114 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
114 process_id); 115 process_id);
115 116
116 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 117 const auto current = system.CurrentProcess()->GetTitleID();
117 118
118 std::vector<u32> out; 119 std::vector<u32> out;
119 const auto& disabled = Settings::values.disabled_addons[current]; 120 const auto& disabled = Settings::values.disabled_addons[current];
@@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
159 IPC::ResponseBuilder rb{ctx, 4}; 160 IPC::ResponseBuilder rb{ctx, 4};
160 rb.Push(RESULT_SUCCESS); 161 rb.Push(RESULT_SUCCESS);
161 162
162 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 163 const auto title_id = system.CurrentProcess()->GetTitleID();
163 FileSys::PatchManager pm{title_id}; 164 FileSys::PatchManager pm{title_id};
164 165
165 const auto res = pm.GetControlMetadata(); 166 const auto res = pm.GetControlMetadata();
@@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
196 rb.PushCopyObjects(aoc_change_event.readable); 197 rb.PushCopyObjects(aoc_change_event.readable);
197} 198}
198 199
199void InstallInterfaces(SM::ServiceManager& service_manager) { 200void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
200 std::make_shared<AOC_U>()->InstallAsService(service_manager); 201 std::make_shared<AOC_U>(system)->InstallAsService(service_manager);
201} 202}
202 203
203} // namespace Service::AOC 204} // namespace Service::AOC
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 5effea730..848b2f416 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -14,7 +14,7 @@ namespace Service::AOC {
14 14
15class AOC_U final : public ServiceFramework<AOC_U> { 15class AOC_U final : public ServiceFramework<AOC_U> {
16public: 16public:
17 AOC_U(); 17 explicit AOC_U(Core::System& system);
18 ~AOC_U() override; 18 ~AOC_U() override;
19 19
20private: 20private:
@@ -26,9 +26,10 @@ private:
26 26
27 std::vector<u64> add_on_content; 27 std::vector<u64> add_on_content;
28 Kernel::EventPair aoc_change_event; 28 Kernel::EventPair aoc_change_event;
29 Core::System& system;
29}; 30};
30 31
31/// Registers all AOC services with the specified service manager. 32/// Registers all AOC services with the specified service manager.
32void InstallInterfaces(SM::ServiceManager& service_manager); 33void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
33 34
34} // namespace Service::AOC 35} // namespace Service::AOC
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 3c7ca2c44..afce581e5 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -16,7 +16,7 @@ namespace Service::BtDrv {
16 16
17class Bt final : public ServiceFramework<Bt> { 17class Bt final : public ServiceFramework<Bt> {
18public: 18public:
19 explicit Bt() : ServiceFramework{"bt"} { 19 explicit Bt(Core::System& system) : ServiceFramework{"bt"} {
20 // clang-format off 20 // clang-format off
21 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
22 {0, nullptr, "LeClientReadCharacteristic"}, 22 {0, nullptr, "LeClientReadCharacteristic"},
@@ -33,7 +33,7 @@ public:
33 // clang-format on 33 // clang-format on
34 RegisterHandlers(functions); 34 RegisterHandlers(functions);
35 35
36 auto& kernel = Core::System::GetInstance().Kernel(); 36 auto& kernel = system.Kernel();
37 register_event = Kernel::WritableEvent::CreateEventPair( 37 register_event = Kernel::WritableEvent::CreateEventPair(
38 kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); 38 kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
39 } 39 }
@@ -163,9 +163,9 @@ public:
163 } 163 }
164}; 164};
165 165
166void InstallInterfaces(SM::ServiceManager& sm) { 166void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
167 std::make_shared<BtDrv>()->InstallAsService(sm); 167 std::make_shared<BtDrv>()->InstallAsService(sm);
168 std::make_shared<Bt>()->InstallAsService(sm); 168 std::make_shared<Bt>(system)->InstallAsService(sm);
169} 169}
170 170
171} // namespace Service::BtDrv 171} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h
index 164e56f43..191410dbc 100644
--- a/src/core/hle/service/btdrv/btdrv.h
+++ b/src/core/hle/service/btdrv/btdrv.h
@@ -8,9 +8,13 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::BtDrv { 15namespace Service::BtDrv {
12 16
13/// Registers all BtDrv services with the specified service manager. 17/// Registers all BtDrv services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm); 18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
15 19
16} // namespace Service::BtDrv 20} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b439ee7ec..920fc6ff7 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -17,7 +17,7 @@ namespace Service::BTM {
17 17
18class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { 18class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
19public: 19public:
20 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { 20 explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
23 {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, 23 {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
@@ -56,7 +56,7 @@ public:
56 // clang-format on 56 // clang-format on
57 RegisterHandlers(functions); 57 RegisterHandlers(functions);
58 58
59 auto& kernel = Core::System::GetInstance().Kernel(); 59 auto& kernel = system.Kernel();
60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
61 "IBtmUserCore:ScanEvent"); 61 "IBtmUserCore:ScanEvent");
62 connection_event = Kernel::WritableEvent::CreateEventPair( 62 connection_event = Kernel::WritableEvent::CreateEventPair(
@@ -108,7 +108,7 @@ private:
108 108
109class BTM_USR final : public ServiceFramework<BTM_USR> { 109class BTM_USR final : public ServiceFramework<BTM_USR> {
110public: 110public:
111 explicit BTM_USR() : ServiceFramework{"btm:u"} { 111 explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) {
112 // clang-format off 112 // clang-format off
113 static const FunctionInfo functions[] = { 113 static const FunctionInfo functions[] = {
114 {0, &BTM_USR::GetCore, "GetCore"}, 114 {0, &BTM_USR::GetCore, "GetCore"},
@@ -123,8 +123,10 @@ private:
123 123
124 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 124 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
125 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
126 rb.PushIpcInterface<IBtmUserCore>(); 126 rb.PushIpcInterface<IBtmUserCore>(system);
127 } 127 }
128
129 Core::System& system;
128}; 130};
129 131
130class BTM final : public ServiceFramework<BTM> { 132class BTM final : public ServiceFramework<BTM> {
@@ -268,11 +270,11 @@ private:
268 } 270 }
269}; 271};
270 272
271void InstallInterfaces(SM::ServiceManager& sm) { 273void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
272 std::make_shared<BTM>()->InstallAsService(sm); 274 std::make_shared<BTM>()->InstallAsService(sm);
273 std::make_shared<BTM_DBG>()->InstallAsService(sm); 275 std::make_shared<BTM_DBG>()->InstallAsService(sm);
274 std::make_shared<BTM_SYS>()->InstallAsService(sm); 276 std::make_shared<BTM_SYS>()->InstallAsService(sm);
275 std::make_shared<BTM_USR>()->InstallAsService(sm); 277 std::make_shared<BTM_USR>(system)->InstallAsService(sm);
276} 278}
277 279
278} // namespace Service::BTM 280} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
index e6425a7e3..c6b878043 100644
--- a/src/core/hle/service/btm/btm.h
+++ b/src/core/hle/service/btm/btm.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13};
14
11namespace Service::BTM { 15namespace Service::BTM {
12 16
13void InstallInterfaces(SM::ServiceManager& sm); 17void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
14 18
15} // namespace Service::BTM 19} // namespace Service::BTM
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 01fa06ad3..b2ebf6240 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -20,8 +20,8 @@
20 20
21namespace Service::Fatal { 21namespace Service::Fatal {
22 22
23Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 23Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
24 : ServiceFramework(name), module(std::move(module)) {} 24 : ServiceFramework(name), module(std::move(module)), system(system) {}
25 25
26Module::Interface::~Interface() = default; 26Module::Interface::~Interface() = default;
27 27
@@ -64,7 +64,8 @@ enum class FatalType : u32 {
64 ErrorScreen = 2, 64 ErrorScreen = 2,
65}; 65};
66 66
67static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { 67static void GenerateErrorReport(Core::System& system, ResultCode error_code,
68 const FatalInfo& info) {
68 const auto title_id = Core::CurrentProcess()->GetTitleID(); 69 const auto title_id = Core::CurrentProcess()->GetTitleID();
69 std::string crash_report = fmt::format( 70 std::string crash_report = fmt::format(
70 "Yuzu {}-{} crash report\n" 71 "Yuzu {}-{} crash report\n"
@@ -101,18 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
101 102
102 LOG_ERROR(Service_Fatal, "{}", crash_report); 103 LOG_ERROR(Service_Fatal, "{}", crash_report);
103 104
104 Core::System::GetInstance().GetReporter().SaveCrashReport( 105 system.GetReporter().SaveCrashReport(
105 title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, 106 title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
106 info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, 107 info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
107 info.backtrace_size, info.ArchAsString(), info.unk10); 108 info.backtrace_size, info.ArchAsString(), info.unk10);
108} 109}
109 110
110static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { 111static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
112 const FatalInfo& info) {
111 LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", 113 LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
112 static_cast<u32>(fatal_type), error_code.raw); 114 static_cast<u32>(fatal_type), error_code.raw);
113 switch (fatal_type) { 115 switch (fatal_type) {
114 case FatalType::ErrorReportAndScreen: 116 case FatalType::ErrorReportAndScreen:
115 GenerateErrorReport(error_code, info); 117 GenerateErrorReport(system, error_code, info);
116 [[fallthrough]]; 118 [[fallthrough]];
117 case FatalType::ErrorScreen: 119 case FatalType::ErrorScreen:
118 // Since we have no fatal:u error screen. We should just kill execution instead 120 // Since we have no fatal:u error screen. We should just kill execution instead
@@ -120,7 +122,7 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
120 break; 122 break;
121 // Should not throw a fatal screen but should generate an error report 123 // Should not throw a fatal screen but should generate an error report
122 case FatalType::ErrorReport: 124 case FatalType::ErrorReport:
123 GenerateErrorReport(error_code, info); 125 GenerateErrorReport(system, error_code, info);
124 break; 126 break;
125 } 127 }
126} 128}
@@ -130,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
130 IPC::RequestParser rp{ctx}; 132 IPC::RequestParser rp{ctx};
131 const auto error_code = rp.Pop<ResultCode>(); 133 const auto error_code = rp.Pop<ResultCode>();
132 134
133 ThrowFatalError(error_code, FatalType::ErrorScreen, {}); 135 ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
134 IPC::ResponseBuilder rb{ctx, 2}; 136 IPC::ResponseBuilder rb{ctx, 2};
135 rb.Push(RESULT_SUCCESS); 137 rb.Push(RESULT_SUCCESS);
136} 138}
@@ -141,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
141 const auto error_code = rp.Pop<ResultCode>(); 143 const auto error_code = rp.Pop<ResultCode>();
142 const auto fatal_type = rp.PopEnum<FatalType>(); 144 const auto fatal_type = rp.PopEnum<FatalType>();
143 145
144 ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy 146 ThrowFatalError(system, error_code, fatal_type,
147 {}); // No info is passed with ThrowFatalWithPolicy
145 IPC::ResponseBuilder rb{ctx, 2}; 148 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(RESULT_SUCCESS); 149 rb.Push(RESULT_SUCCESS);
147} 150}
@@ -157,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx)
157 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); 160 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
158 std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); 161 std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
159 162
160 ThrowFatalError(error_code, fatal_type, info); 163 ThrowFatalError(system, error_code, fatal_type, info);
161 IPC::ResponseBuilder rb{ctx, 2}; 164 IPC::ResponseBuilder rb{ctx, 2};
162 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
163} 166}
164 167
165void InstallInterfaces(SM::ServiceManager& service_manager) { 168void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
166 auto module = std::make_shared<Module>(); 169 auto module = std::make_shared<Module>();
167 std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); 170 std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager);
168 std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); 171 std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager);
169} 172}
170 173
171} // namespace Service::Fatal 174} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index 09371ff7f..bd9339dfc 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -6,13 +6,17 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service::Fatal { 13namespace Service::Fatal {
10 14
11class Module final { 15class Module final {
12public: 16public:
13 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
14 public: 18 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
16 ~Interface() override; 20 ~Interface() override;
17 21
18 void ThrowFatal(Kernel::HLERequestContext& ctx); 22 void ThrowFatal(Kernel::HLERequestContext& ctx);
@@ -21,9 +25,10 @@ public:
21 25
22 protected: 26 protected:
23 std::shared_ptr<Module> module; 27 std::shared_ptr<Module> module;
28 Core::System& system;
24 }; 29 };
25}; 30};
26 31
27void InstallInterfaces(SM::ServiceManager& service_manager); 32void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
28 33
29} // namespace Service::Fatal 34} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 9e5f872ff..066ccf6b0 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::Fatal { 7namespace Service::Fatal {
8 8
9Fatal_P::Fatal_P(std::shared_ptr<Module> module) 9Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system)
10 : Module::Interface(std::move(module), "fatal:p") {} 10 : Module::Interface(std::move(module), system, "fatal:p") {}
11 11
12Fatal_P::~Fatal_P() = default; 12Fatal_P::~Fatal_P() = default;
13 13
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h
index 6e9c5979f..c6d953cb5 100644
--- a/src/core/hle/service/fatal/fatal_p.h
+++ b/src/core/hle/service/fatal/fatal_p.h
@@ -10,7 +10,7 @@ namespace Service::Fatal {
10 10
11class Fatal_P final : public Module::Interface { 11class Fatal_P final : public Module::Interface {
12public: 12public:
13 explicit Fatal_P(std::shared_ptr<Module> module); 13 explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system);
14 ~Fatal_P() override; 14 ~Fatal_P() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index 1572a2051..8d72ed485 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Fatal { 7namespace Service::Fatal {
8 8
9Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { 9Fatal_U::Fatal_U(std::shared_ptr<Module> module, Core::System& system)
10 : Module::Interface(std::move(module), system, "fatal:u") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, 12 {0, &Fatal_U::ThrowFatal, "ThrowFatal"},
12 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, 13 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h
index 72cb6d076..34c5c7f95 100644
--- a/src/core/hle/service/fatal/fatal_u.h
+++ b/src/core/hle/service/fatal/fatal_u.h
@@ -10,7 +10,7 @@ namespace Service::Fatal {
10 10
11class Fatal_U final : public Module::Interface { 11class Fatal_U final : public Module::Interface {
12public: 12public:
13 explicit Fatal_U(std::shared_ptr<Module> module); 13 explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system);
14 ~Fatal_U() override; 14 ~Fatal_U() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index d1ec12ef9..42b4ee861 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -149,7 +149,8 @@ private:
149 149
150class INotificationService final : public ServiceFramework<INotificationService> { 150class INotificationService final : public ServiceFramework<INotificationService> {
151public: 151public:
152 INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) { 152 INotificationService(Common::UUID uuid, Core::System& system)
153 : ServiceFramework("INotificationService"), uuid(uuid) {
153 // clang-format off 154 // clang-format off
154 static const FunctionInfo functions[] = { 155 static const FunctionInfo functions[] = {
155 {0, &INotificationService::GetEvent, "GetEvent"}, 156 {0, &INotificationService::GetEvent, "GetEvent"},
@@ -159,6 +160,9 @@ public:
159 // clang-format on 160 // clang-format on
160 161
161 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163
164 notification_event = Kernel::WritableEvent::CreateEventPair(
165 system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
162 } 166 }
163 167
164private: 168private:
@@ -167,13 +171,6 @@ private:
167 171
168 IPC::ResponseBuilder rb{ctx, 2, 1}; 172 IPC::ResponseBuilder rb{ctx, 2, 1};
169 rb.Push(RESULT_SUCCESS); 173 rb.Push(RESULT_SUCCESS);
170
171 if (!is_event_created) {
172 auto& kernel = Core::System::GetInstance().Kernel();
173 notification_event = Kernel::WritableEvent::CreateEventPair(
174 kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
175 is_event_created = true;
176 }
177 rb.PushCopyObjects(notification_event.readable); 174 rb.PushCopyObjects(notification_event.readable);
178 } 175 }
179 176
@@ -261,21 +258,21 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
261 258
262 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 259 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
263 rb.Push(RESULT_SUCCESS); 260 rb.Push(RESULT_SUCCESS);
264 rb.PushIpcInterface<INotificationService>(uuid); 261 rb.PushIpcInterface<INotificationService>(uuid, system);
265} 262}
266 263
267Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 264Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
268 : ServiceFramework(name), module(std::move(module)) {} 265 : ServiceFramework(name), module(std::move(module)), system(system) {}
269 266
270Module::Interface::~Interface() = default; 267Module::Interface::~Interface() = default;
271 268
272void InstallInterfaces(SM::ServiceManager& service_manager) { 269void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
273 auto module = std::make_shared<Module>(); 270 auto module = std::make_shared<Module>();
274 std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); 271 std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager);
275 std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager); 272 std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager);
276 std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager); 273 std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager);
277 std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager); 274 std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager);
278 std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager); 275 std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager);
279} 276}
280 277
281} // namespace Service::Friend 278} // namespace Service::Friend
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index 38d05fa8e..24f3fc969 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -6,13 +6,17 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service::Friend { 13namespace Service::Friend {
10 14
11class Module final { 15class Module final {
12public: 16public:
13 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
14 public: 18 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
16 ~Interface() override; 20 ~Interface() override;
17 21
18 void CreateFriendService(Kernel::HLERequestContext& ctx); 22 void CreateFriendService(Kernel::HLERequestContext& ctx);
@@ -20,10 +24,11 @@ public:
20 24
21 protected: 25 protected:
22 std::shared_ptr<Module> module; 26 std::shared_ptr<Module> module;
27 Core::System& system;
23 }; 28 };
24}; 29};
25 30
26/// Registers all Friend services with the specified service manager. 31/// Registers all Friend services with the specified service manager.
27void InstallInterfaces(SM::ServiceManager& service_manager); 32void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
28 33
29} // namespace Service::Friend 34} // namespace Service::Friend
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 5b384f733..58155f652 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::Friend { 7namespace Service::Friend {
8 8
9Friend::Friend(std::shared_ptr<Module> module, const char* name) 9Friend::Friend(std::shared_ptr<Module> module, Core::System& system, const char* name)
10 : Interface(std::move(module), name) { 10 : Interface(std::move(module), system, name) {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &Friend::CreateFriendService, "CreateFriendService"}, 12 {0, &Friend::CreateFriendService, "CreateFriendService"},
13 {1, &Friend::CreateNotificationService, "CreateNotificationService"}, 13 {1, &Friend::CreateNotificationService, "CreateNotificationService"},
diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h
index 1963def39..465a35770 100644
--- a/src/core/hle/service/friend/interface.h
+++ b/src/core/hle/service/friend/interface.h
@@ -10,7 +10,7 @@ namespace Service::Friend {
10 10
11class Friend final : public Module::Interface { 11class Friend final : public Module::Interface {
12public: 12public:
13 explicit Friend(std::shared_ptr<Module> module, const char* name); 13 explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name);
14 ~Friend() override; 14 ~Friend() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 0993a7815..8091db9d7 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,7 +6,7 @@
6 6
7namespace Service::HID { 7namespace Service::HID {
8 8
9ControllerBase::ControllerBase() = default; 9ControllerBase::ControllerBase(Core::System& system) : system(system) {}
10ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
11 11
12void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 5e5097a03..8bc69c372 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,10 +11,14 @@ namespace Core::Timing {
11class CoreTiming; 11class CoreTiming;
12} 12}
13 13
14namespace Core {
15class System;
16}
17
14namespace Service::HID { 18namespace Service::HID {
15class ControllerBase { 19class ControllerBase {
16public: 20public:
17 ControllerBase(); 21 explicit ControllerBase(Core::System& system);
18 virtual ~ControllerBase(); 22 virtual ~ControllerBase();
19 23
20 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -46,5 +50,7 @@ protected:
46 s64_le entry_count; 50 s64_le entry_count;
47 }; 51 };
48 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); 52 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
53
54 Core::System& system;
49}; 55};
50} // namespace Service::HID 56} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index c5c2e032a..8e8263f5b 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -14,7 +14,8 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16 16
17Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad(Core::System& system)
18 : ControllerBase(system), system(system) {}
18Controller_DebugPad::~Controller_DebugPad() = default; 19Controller_DebugPad::~Controller_DebugPad() = default;
19 20
20void Controller_DebugPad::OnInit() {} 21void Controller_DebugPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index e584b92ec..6c4de817e 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -16,7 +16,7 @@
16namespace Service::HID { 16namespace Service::HID {
17class Controller_DebugPad final : public ControllerBase { 17class Controller_DebugPad final : public ControllerBase {
18public: 18public:
19 Controller_DebugPad(); 19 explicit Controller_DebugPad(Core::System& system);
20 ~Controller_DebugPad() override; 20 ~Controller_DebugPad() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
@@ -89,5 +89,6 @@ private:
89 buttons; 89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> 90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs; 91 analogs;
92 Core::System& system;
92}; 93};
93} // namespace Service::HID 94} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index a179252e3..80da0a0d3 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -10,7 +10,8 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12 12
13Controller_Gesture::Controller_Gesture() = default; 13Controller_Gesture::Controller_Gesture(Core::System& system)
14 : ControllerBase(system), system(system) {}
14Controller_Gesture::~Controller_Gesture() = default; 15Controller_Gesture::~Controller_Gesture() = default;
15 16
16void Controller_Gesture::OnInit() {} 17void Controller_Gesture::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index f305fe90f..396897527 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase { 13class Controller_Gesture final : public ControllerBase {
14public: 14public:
15 Controller_Gesture(); 15 explicit Controller_Gesture(Core::System& system);
16 ~Controller_Gesture() override; 16 ~Controller_Gesture() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -59,5 +59,6 @@ private:
59 std::array<GestureState, 17> gesture_states; 59 std::array<GestureState, 17> gesture_states;
60 }; 60 };
61 SharedMemory shared_memory{}; 61 SharedMemory shared_memory{};
62 Core::System& system;
62}; 63};
63} // namespace Service::HID 64} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 92d7bfb52..e587b2e15 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,7 +12,8 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8; 13constexpr u8 KEYS_PER_BYTE = 8;
14 14
15Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard(Core::System& system)
16 : ControllerBase(system), system(system) {}
16Controller_Keyboard::~Controller_Keyboard() = default; 17Controller_Keyboard::~Controller_Keyboard() = default;
17 18
18void Controller_Keyboard::OnInit() {} 19void Controller_Keyboard::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 73cd2c7bb..ef586f7eb 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -15,7 +15,7 @@
15namespace Service::HID { 15namespace Service::HID {
16class Controller_Keyboard final : public ControllerBase { 16class Controller_Keyboard final : public ControllerBase {
17public: 17public:
18 Controller_Keyboard(); 18 explicit Controller_Keyboard(Core::System& system);
19 ~Controller_Keyboard() override; 19 ~Controller_Keyboard() override;
20 20
21 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
53 keyboard_keys; 53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods; 55 keyboard_mods;
56 Core::System& system;
56}; 57};
57} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 11ab096d9..88f2ca4c1 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -11,7 +11,7 @@
11namespace Service::HID { 11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 13
14Controller_Mouse::Controller_Mouse() = default; 14Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
15Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
16 16
17void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 9d46eecbe..df2da6ae3 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -14,7 +14,7 @@
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Mouse final : public ControllerBase { 15class Controller_Mouse final : public ControllerBase {
16public: 16public:
17 Controller_Mouse(); 17 explicit Controller_Mouse(Core::System& system);
18 ~Controller_Mouse() override; 18 ~Controller_Mouse() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
53 std::unique_ptr<Input::MouseDevice> mouse_device; 53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices; 55 mouse_button_devices;
56 Core::System& system;
56}; 57};
57} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e47fe8188..44b668fbf 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -93,7 +93,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
93 }; 93 };
94} 94}
95 95
96Controller_NPad::Controller_NPad() = default; 96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 97Controller_NPad::~Controller_NPad() = default;
98 98
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
@@ -168,9 +168,11 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
168} 168}
169 169
170void Controller_NPad::OnInit() { 170void Controller_NPad::OnInit() {
171 auto& kernel = Core::System::GetInstance().Kernel(); 171 auto& kernel = system.Kernel();
172 styleset_changed_event = Kernel::WritableEvent::CreateEventPair( 172 for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
173 kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged"); 173 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
174 kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
175 }
174 176
175 if (!IsControllerActivated()) { 177 if (!IsControllerActivated()) {
176 return; 178 return;
@@ -453,7 +455,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
453 had_controller_update = true; 455 had_controller_update = true;
454 } 456 }
455 if (had_controller_update) { 457 if (had_controller_update) {
456 styleset_changed_event.writable->Signal(); 458 styleset_changed_events[i].writable->Signal();
457 } 459 }
458 } 460 }
459} 461}
@@ -468,7 +470,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
468} 470}
469 471
470void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 472void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
471 styleset_changed_event.writable->Signal();
472 hold_type = joy_hold_type; 473 hold_type = joy_hold_type;
473} 474}
474 475
@@ -479,7 +480,10 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
479void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { 480void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
480 const std::size_t npad_index = NPadIdToIndex(npad_id); 481 const std::size_t npad_index = NPadIdToIndex(npad_id);
481 ASSERT(npad_index < shared_memory_entries.size()); 482 ASSERT(npad_index < shared_memory_entries.size());
482 shared_memory_entries[npad_index].pad_assignment = assignment_mode; 483 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
484 styleset_changed_events[npad_index].writable->Signal();
485 shared_memory_entries[npad_index].pad_assignment = assignment_mode;
486 }
483} 487}
484 488
485void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, 489void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
@@ -498,11 +502,13 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
498 last_processed_vibration = vibrations.back(); 502 last_processed_vibration = vibrations.back();
499} 503}
500 504
501Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { 505Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
506 u32 npad_id) const {
502 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should 507 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
503 // be signalled at least once, and signaled after a new controller is connected? 508 // be signalled at least once, and signaled after a new controller is connected?
504 styleset_changed_event.writable->Signal(); 509 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
505 return styleset_changed_event.readable; 510 styleset_event.writable->Signal();
511 return styleset_event.readable;
506} 512}
507 513
508Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 514Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index f28b36806..1bc3d55d6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -20,7 +20,7 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
20 20
21class Controller_NPad final : public ControllerBase { 21class Controller_NPad final : public ControllerBase {
22public: 22public:
23 Controller_NPad(); 23 explicit Controller_NPad(Core::System& system);
24 ~Controller_NPad() override; 24 ~Controller_NPad() override;
25 25
26 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -109,7 +109,7 @@ public:
109 void VibrateController(const std::vector<u32>& controller_ids, 109 void VibrateController(const std::vector<u32>& controller_ids,
110 const std::vector<Vibration>& vibrations); 110 const std::vector<Vibration>& vibrations);
111 111
112 Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; 112 Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
113 Vibration GetLastVibration() const; 113 Vibration GetLastVibration() const;
114 114
115 void AddNewController(NPadControllerType controller); 115 void AddNewController(NPadControllerType controller);
@@ -315,7 +315,8 @@ private:
315 sticks; 315 sticks;
316 std::vector<u32> supported_npad_id_types{}; 316 std::vector<u32> supported_npad_id_types{};
317 NpadHoldType hold_type{NpadHoldType::Vertical}; 317 NpadHoldType hold_type{NpadHoldType::Vertical};
318 Kernel::EventPair styleset_changed_event; 318 // Each controller should have their own styleset changed event
319 std::array<Kernel::EventPair, 10> styleset_changed_events;
319 Vibration last_processed_vibration{}; 320 Vibration last_processed_vibration{};
320 std::array<ControllerHolder, 10> connected_controllers{}; 321 std::array<ControllerHolder, 10> connected_controllers{};
321 bool can_controllers_vibrate{true}; 322 bool can_controllers_vibrate{true};
@@ -327,5 +328,6 @@ private:
327 std::array<ControllerPad, 10> npad_pad_states{}; 328 std::array<ControllerPad, 10> npad_pad_states{};
328 bool IsControllerSupported(NPadControllerType controller); 329 bool IsControllerSupported(NPadControllerType controller);
329 bool is_in_lr_assignment_mode{false}; 330 bool is_in_lr_assignment_mode{false};
331 Core::System& system;
330}; 332};
331} // namespace Service::HID 333} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 946948f5e..9b829341e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -9,7 +9,8 @@
9 9
10namespace Service::HID { 10namespace Service::HID {
11 11
12Controller_Stubbed::Controller_Stubbed() = default; 12Controller_Stubbed::Controller_Stubbed(Core::System& system)
13 : ControllerBase(system), system(system) {}
13Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
14 15
15void Controller_Stubbed::OnInit() {} 16void Controller_Stubbed::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 24469f03e..37d7d8538 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 Controller_Stubbed(); 13 explicit Controller_Stubbed(Core::System& system);
14 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -30,5 +30,6 @@ public:
30private: 30private:
31 bool smart_update{}; 31 bool smart_update{};
32 std::size_t common_offset{}; 32 std::size_t common_offset{};
33 Core::System& system;
33}; 34};
34} // namespace Service::HID 35} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 1a8445a43..25912fd69 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -13,7 +13,8 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15 15
16Controller_Touchscreen::Controller_Touchscreen() = default; 16Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
17 : ControllerBase(system), system(system) {}
17Controller_Touchscreen::~Controller_Touchscreen() = default; 18Controller_Touchscreen::~Controller_Touchscreen() = default;
18 19
19void Controller_Touchscreen::OnInit() {} 20void Controller_Touchscreen::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 76fc340e9..3429c84db 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,7 +14,7 @@
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Touchscreen final : public ControllerBase { 15class Controller_Touchscreen final : public ControllerBase {
16public: 16public:
17 Controller_Touchscreen(); 17 explicit Controller_Touchscreen(Core::System& system);
18 ~Controller_Touchscreen() override; 18 ~Controller_Touchscreen() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -69,5 +69,6 @@ private:
69 TouchScreenSharedMemory shared_memory{}; 69 TouchScreenSharedMemory shared_memory{};
70 std::unique_ptr<Input::TouchDevice> touch_device; 70 std::unique_ptr<Input::TouchDevice> touch_device;
71 s64_le last_touch{}; 71 s64_le last_touch{};
72 Core::System& system;
72}; 73};
73} // namespace Service::HID 74} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 1a9da9576..1bce044b4 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad() = default; 13Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
14Controller_XPad::~Controller_XPad() = default; 14Controller_XPad::~Controller_XPad() = default;
15 15
16void Controller_XPad::OnInit() {} 16void Controller_XPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index 2864e6617..c445ebec0 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13class Controller_XPad final : public ControllerBase { 13class Controller_XPad final : public ControllerBase {
14public: 14public:
15 Controller_XPad(); 15 explicit Controller_XPad(Core::System& system);
16 ~Controller_XPad() override; 16 ~Controller_XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -56,5 +56,6 @@ private:
56 }; 56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); 57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{}; 58 SharedMemory shared_memory{};
59 Core::System& system;
59}; 60};
60} // namespace Service::HID 61} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index f8b1ca816..8d76ba746 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -42,13 +42,14 @@ constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_C
42constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); 42constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
43constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 43constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
44 44
45IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { 45IAppletResource::IAppletResource(Core::System& system)
46 : ServiceFramework("IAppletResource"), system(system) {
46 static const FunctionInfo functions[] = { 47 static const FunctionInfo functions[] = {
47 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, 48 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
48 }; 49 };
49 RegisterHandlers(functions); 50 RegisterHandlers(functions);
50 51
51 auto& kernel = Core::System::GetInstance().Kernel(); 52 auto& kernel = system.Kernel();
52 shared_mem = Kernel::SharedMemory::Create( 53 shared_mem = Kernel::SharedMemory::Create(
53 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, 54 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
54 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 55 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
@@ -74,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
74 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); 75 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
75 76
76 // Register update callbacks 77 // Register update callbacks
77 auto& core_timing = Core::System::GetInstance().CoreTiming(); 78 auto& core_timing = system.CoreTiming();
78 pad_update_event = 79 pad_update_event =
79 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { 80 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
80 UpdateControllers(userdata, cycles_late); 81 UpdateControllers(userdata, cycles_late);
@@ -96,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) {
96} 97}
97 98
98IAppletResource ::~IAppletResource() { 99IAppletResource ::~IAppletResource() {
99 Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); 100 system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
100} 101}
101 102
102void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { 103void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -108,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
108} 109}
109 110
110void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { 111void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
111 auto& core_timing = Core::System::GetInstance().CoreTiming(); 112 auto& core_timing = system.CoreTiming();
112 113
113 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 114 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
114 for (const auto& controller : controllers) { 115 for (const auto& controller : controllers) {
@@ -141,13 +142,13 @@ private:
141 142
142std::shared_ptr<IAppletResource> Hid::GetAppletResource() { 143std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
143 if (applet_resource == nullptr) { 144 if (applet_resource == nullptr) {
144 applet_resource = std::make_shared<IAppletResource>(); 145 applet_resource = std::make_shared<IAppletResource>(system);
145 } 146 }
146 147
147 return applet_resource; 148 return applet_resource;
148} 149}
149 150
150Hid::Hid() : ServiceFramework("hid") { 151Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
151 // clang-format off 152 // clang-format off
152 static const FunctionInfo functions[] = { 153 static const FunctionInfo functions[] = {
153 {0, &Hid::CreateAppletResource, "CreateAppletResource"}, 154 {0, &Hid::CreateAppletResource, "CreateAppletResource"},
@@ -286,7 +287,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
286 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 287 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
287 288
288 if (applet_resource == nullptr) { 289 if (applet_resource == nullptr) {
289 applet_resource = std::make_shared<IAppletResource>(); 290 applet_resource = std::make_shared<IAppletResource>(system);
290 } 291 }
291 292
292 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 293 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -479,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
479 IPC::ResponseBuilder rb{ctx, 2, 1}; 480 IPC::ResponseBuilder rb{ctx, 2, 1};
480 rb.Push(RESULT_SUCCESS); 481 rb.Push(RESULT_SUCCESS);
481 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 482 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
482 .GetStyleSetChangedEvent()); 483 .GetStyleSetChangedEvent(npad_id));
483} 484}
484 485
485void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 486void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
@@ -1053,14 +1054,14 @@ void ReloadInputDevices() {
1053 Settings::values.is_device_reload_pending.store(true); 1054 Settings::values.is_device_reload_pending.store(true);
1054} 1055}
1055 1056
1056void InstallInterfaces(SM::ServiceManager& service_manager) { 1057void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
1057 std::make_shared<Hid>()->InstallAsService(service_manager); 1058 std::make_shared<Hid>(system)->InstallAsService(service_manager);
1058 std::make_shared<HidBus>()->InstallAsService(service_manager); 1059 std::make_shared<HidBus>()->InstallAsService(service_manager);
1059 std::make_shared<HidDbg>()->InstallAsService(service_manager); 1060 std::make_shared<HidDbg>()->InstallAsService(service_manager);
1060 std::make_shared<HidSys>()->InstallAsService(service_manager); 1061 std::make_shared<HidSys>()->InstallAsService(service_manager);
1061 std::make_shared<HidTmp>()->InstallAsService(service_manager); 1062 std::make_shared<HidTmp>()->InstallAsService(service_manager);
1062 1063
1063 std::make_shared<IRS>()->InstallAsService(service_manager); 1064 std::make_shared<IRS>(system)->InstallAsService(service_manager);
1064 std::make_shared<IRS_SYS>()->InstallAsService(service_manager); 1065 std::make_shared<IRS_SYS>()->InstallAsService(service_manager);
1065 1066
1066 std::make_shared<XCD_SYS>()->InstallAsService(service_manager); 1067 std::make_shared<XCD_SYS>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 2fd6d9fc7..35b663679 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -42,7 +42,7 @@ enum class HidController : std::size_t {
42 42
43class IAppletResource final : public ServiceFramework<IAppletResource> { 43class IAppletResource final : public ServiceFramework<IAppletResource> {
44public: 44public:
45 IAppletResource(); 45 explicit IAppletResource(Core::System& system);
46 ~IAppletResource() override; 46 ~IAppletResource() override;
47 47
48 void ActivateController(HidController controller); 48 void ActivateController(HidController controller);
@@ -61,7 +61,7 @@ public:
61private: 61private:
62 template <typename T> 62 template <typename T>
63 void MakeController(HidController controller) { 63 void MakeController(HidController controller) {
64 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(); 64 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
65 } 65 }
66 66
67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -70,6 +70,7 @@ private:
70 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 70 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
71 71
72 Core::Timing::EventType* pad_update_event; 72 Core::Timing::EventType* pad_update_event;
73 Core::System& system;
73 74
74 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 75 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
75 controllers{}; 76 controllers{};
@@ -77,7 +78,7 @@ private:
77 78
78class Hid final : public ServiceFramework<Hid> { 79class Hid final : public ServiceFramework<Hid> {
79public: 80public:
80 Hid(); 81 explicit Hid(Core::System& system);
81 ~Hid() override; 82 ~Hid() override;
82 83
83 std::shared_ptr<IAppletResource> GetAppletResource(); 84 std::shared_ptr<IAppletResource> GetAppletResource();
@@ -126,12 +127,13 @@ private:
126 void SwapNpadAssignment(Kernel::HLERequestContext& ctx); 127 void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
127 128
128 std::shared_ptr<IAppletResource> applet_resource; 129 std::shared_ptr<IAppletResource> applet_resource;
130 Core::System& system;
129}; 131};
130 132
131/// Reload input devices. Used when input configuration changed 133/// Reload input devices. Used when input configuration changed
132void ReloadInputDevices(); 134void ReloadInputDevices();
133 135
134/// Registers all HID services with the specified service manager. 136/// Registers all HID services with the specified service manager.
135void InstallInterfaces(SM::ServiceManager& service_manager); 137void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
136 138
137} // namespace Service::HID 139} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 2c4625c99..5e79e2c1a 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -11,7 +11,7 @@
11 11
12namespace Service::HID { 12namespace Service::HID {
13 13
14IRS::IRS() : ServiceFramework{"irs"} { 14IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, 17 {302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
@@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} {
37 37
38 RegisterHandlers(functions); 38 RegisterHandlers(functions);
39 39
40 auto& kernel = Core::System::GetInstance().Kernel(); 40 auto& kernel = system.Kernel();
41 shared_mem = Kernel::SharedMemory::Create( 41 shared_mem = Kernel::SharedMemory::Create(
42 kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, 42 kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
43 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); 43 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
98 98
99 IPC::ResponseBuilder rb{ctx, 5}; 99 IPC::ResponseBuilder rb{ctx, 5};
100 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
101 rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks()); 101 rb.PushRaw<u64>(system.CoreTiming().GetTicks());
102 rb.PushRaw<u32>(0); 102 rb.PushRaw<u32>(0);
103} 103}
104 104
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 12de6bfb3..eb4e898dd 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -15,7 +15,7 @@ namespace Service::HID {
15 15
16class IRS final : public ServiceFramework<IRS> { 16class IRS final : public ServiceFramework<IRS> {
17public: 17public:
18 explicit IRS(); 18 explicit IRS(Core::System& system);
19 ~IRS() override; 19 ~IRS() override;
20 20
21private: 21private:
@@ -39,6 +39,7 @@ private:
39 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); 39 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
40 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 40 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
41 const u32 device_handle{0xABCD}; 41 const u32 device_handle{0xABCD};
42 Core::System& system;
42}; 43};
43 44
44class IRS_SYS final : public ServiceFramework<IRS_SYS> { 45class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 8ddad8682..3164ca26e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -78,7 +78,7 @@ public:
78 78
79class RelocatableObject final : public ServiceFramework<RelocatableObject> { 79class RelocatableObject final : public ServiceFramework<RelocatableObject> {
80public: 80public:
81 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { 81 explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) {
82 // clang-format off 82 // clang-format off
83 static const FunctionInfo functions[] = { 83 static const FunctionInfo functions[] = {
84 {0, &RelocatableObject::LoadNro, "LoadNro"}, 84 {0, &RelocatableObject::LoadNro, "LoadNro"},
@@ -364,7 +364,7 @@ public:
364 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, 364 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
365 Kernel::VMAPermission::ReadWrite); 365 Kernel::VMAPermission::ReadWrite);
366 366
367 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 367 system.InvalidateCpuInstructionCaches();
368 368
369 nro.insert_or_assign(*map_address, 369 nro.insert_or_assign(*map_address,
370 NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); 370 NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
@@ -430,7 +430,7 @@ public:
430 .IsSuccess()); 430 .IsSuccess());
431 } 431 }
432 432
433 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 433 system.InvalidateCpuInstructionCaches();
434 434
435 nro.erase(iter); 435 nro.erase(iter);
436 IPC::ResponseBuilder rb{ctx, 2}; 436 IPC::ResponseBuilder rb{ctx, 2};
@@ -516,13 +516,14 @@ private:
516 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && 516 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
517 Common::Is4KBAligned(header.rw_size); 517 Common::Is4KBAligned(header.rw_size);
518 } 518 }
519 Core::System& system;
519}; 520};
520 521
521void InstallInterfaces(SM::ServiceManager& sm) { 522void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
522 std::make_shared<DebugMonitor>()->InstallAsService(sm); 523 std::make_shared<DebugMonitor>()->InstallAsService(sm);
523 std::make_shared<ProcessManager>()->InstallAsService(sm); 524 std::make_shared<ProcessManager>()->InstallAsService(sm);
524 std::make_shared<Shell>()->InstallAsService(sm); 525 std::make_shared<Shell>()->InstallAsService(sm);
525 std::make_shared<RelocatableObject>()->InstallAsService(sm); 526 std::make_shared<RelocatableObject>(system)->InstallAsService(sm);
526} 527}
527 528
528} // namespace Service::LDR 529} // namespace Service::LDR
diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h
index 412410c4f..7ac8c0b65 100644
--- a/src/core/hle/service/ldr/ldr.h
+++ b/src/core/hle/service/ldr/ldr.h
@@ -11,6 +11,6 @@ class ServiceManager;
11namespace Service::LDR { 11namespace Service::LDR {
12 12
13/// Registers all LDR services with the specified service manager. 13/// Registers all LDR services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm); 14void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
15 15
16} // namespace Service::LDR 16} // namespace Service::LDR
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index a5cb06f8a..a42c22d44 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -23,9 +23,9 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); 23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
24} // namespace ErrCodes 24} // namespace ErrCodes
25 25
26Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 26Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
27 : ServiceFramework(name), module(std::move(module)) { 27 : ServiceFramework(name), module(std::move(module)), system(system) {
28 auto& kernel = Core::System::GetInstance().Kernel(); 28 auto& kernel = system.Kernel();
29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
30 "IUser:NFCTagDetected"); 30 "IUser:NFCTagDetected");
31} 31}
@@ -34,8 +34,8 @@ Module::Interface::~Interface() = default;
34 34
35class IUser final : public ServiceFramework<IUser> { 35class IUser final : public ServiceFramework<IUser> {
36public: 36public:
37 IUser(Module::Interface& nfp_interface) 37 IUser(Module::Interface& nfp_interface, Core::System& system)
38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { 38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
39 static const FunctionInfo functions[] = { 39 static const FunctionInfo functions[] = {
40 {0, &IUser::Initialize, "Initialize"}, 40 {0, &IUser::Initialize, "Initialize"},
41 {1, &IUser::Finalize, "Finalize"}, 41 {1, &IUser::Finalize, "Finalize"},
@@ -65,7 +65,7 @@ public:
65 }; 65 };
66 RegisterHandlers(functions); 66 RegisterHandlers(functions);
67 67
68 auto& kernel = Core::System::GetInstance().Kernel(); 68 auto& kernel = system.Kernel();
69 deactivate_event = Kernel::WritableEvent::CreateEventPair( 69 deactivate_event = Kernel::WritableEvent::CreateEventPair(
70 kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); 70 kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
71 availability_change_event = Kernel::WritableEvent::CreateEventPair( 71 availability_change_event = Kernel::WritableEvent::CreateEventPair(
@@ -324,6 +324,7 @@ private:
324 Kernel::EventPair deactivate_event; 324 Kernel::EventPair deactivate_event;
325 Kernel::EventPair availability_change_event; 325 Kernel::EventPair availability_change_event;
326 const Module::Interface& nfp_interface; 326 const Module::Interface& nfp_interface;
327 Core::System& system;
327}; 328};
328 329
329void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 330void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
@@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
331 332
332 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 333 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
333 rb.Push(RESULT_SUCCESS); 334 rb.Push(RESULT_SUCCESS);
334 rb.PushIpcInterface<IUser>(*this); 335 rb.PushIpcInterface<IUser>(*this, system);
335} 336}
336 337
337bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 338bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
@@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const
353 return amiibo; 354 return amiibo;
354} 355}
355 356
356void InstallInterfaces(SM::ServiceManager& service_manager) { 357void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
357 auto module = std::make_shared<Module>(); 358 auto module = std::make_shared<Module>();
358 std::make_shared<NFP_User>(module)->InstallAsService(service_manager); 359 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
359} 360}
360 361
361} // namespace Service::NFP 362} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index a1817e991..9718ef745 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -16,7 +16,7 @@ class Module final {
16public: 16public:
17 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
18 public: 18 public:
19 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
20 ~Interface() override; 20 ~Interface() override;
21 21
22 struct ModelInfo { 22 struct ModelInfo {
@@ -43,9 +43,10 @@ public:
43 43
44 protected: 44 protected:
45 std::shared_ptr<Module> module; 45 std::shared_ptr<Module> module;
46 Core::System& system;
46 }; 47 };
47}; 48};
48 49
49void InstallInterfaces(SM::ServiceManager& service_manager); 50void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
50 51
51} // namespace Service::NFP 52} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 784a87c1b..298184f17 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::NFP { 7namespace Service::NFP {
8 8
9NFP_User::NFP_User(std::shared_ptr<Module> module) 9NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system)
10 : Module::Interface(std::move(module), "nfp:user") { 10 : Module::Interface(std::move(module), system, "nfp:user") {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, 12 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"},
13 }; 13 };
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 65d9aaf48..1686ebf20 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -10,7 +10,7 @@ namespace Service::NFP {
10 10
11class NFP_User final : public Module::Interface { 11class NFP_User final : public Module::Interface {
12public: 12public:
13 explicit NFP_User(std::shared_ptr<Module> module); 13 explicit NFP_User(std::shared_ptr<Module> module, Core::System& system);
14 ~NFP_User() override; 14 ~NFP_User() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 76b12b482..24d1813a7 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -31,7 +31,7 @@ public:
31 31
32class IRequest final : public ServiceFramework<IRequest> { 32class IRequest final : public ServiceFramework<IRequest> {
33public: 33public:
34 explicit IRequest() : ServiceFramework("IRequest") { 34 explicit IRequest(Core::System& system) : ServiceFramework("IRequest") {
35 static const FunctionInfo functions[] = { 35 static const FunctionInfo functions[] = {
36 {0, &IRequest::GetRequestState, "GetRequestState"}, 36 {0, &IRequest::GetRequestState, "GetRequestState"},
37 {1, &IRequest::GetResult, "GetResult"}, 37 {1, &IRequest::GetResult, "GetResult"},
@@ -61,7 +61,7 @@ public:
61 }; 61 };
62 RegisterHandlers(functions); 62 RegisterHandlers(functions);
63 63
64 auto& kernel = Core::System::GetInstance().Kernel(); 64 auto& kernel = system.Kernel();
65 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 65 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
66 "IRequest:Event1"); 66 "IRequest:Event1");
67 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 67 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
@@ -130,7 +130,7 @@ public:
130 130
131class IGeneralService final : public ServiceFramework<IGeneralService> { 131class IGeneralService final : public ServiceFramework<IGeneralService> {
132public: 132public:
133 IGeneralService(); 133 IGeneralService(Core::System& system);
134 134
135private: 135private:
136 void GetClientId(Kernel::HLERequestContext& ctx) { 136 void GetClientId(Kernel::HLERequestContext& ctx) {
@@ -155,7 +155,7 @@ private:
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 156
157 rb.Push(RESULT_SUCCESS); 157 rb.Push(RESULT_SUCCESS);
158 rb.PushIpcInterface<IRequest>(); 158 rb.PushIpcInterface<IRequest>(system);
159 } 159 }
160 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 160 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
161 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 161 LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -198,9 +198,11 @@ private:
198 rb.Push(RESULT_SUCCESS); 198 rb.Push(RESULT_SUCCESS);
199 rb.Push<u8>(0); 199 rb.Push<u8>(0);
200 } 200 }
201 Core::System& system;
201}; 202};
202 203
203IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { 204IGeneralService::IGeneralService(Core::System& system)
205 : ServiceFramework("IGeneralService"), system(system) {
204 static const FunctionInfo functions[] = { 206 static const FunctionInfo functions[] = {
205 {1, &IGeneralService::GetClientId, "GetClientId"}, 207 {1, &IGeneralService::GetClientId, "GetClientId"},
206 {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, 208 {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
@@ -245,7 +247,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
245 247
246class NetworkInterface final : public ServiceFramework<NetworkInterface> { 248class NetworkInterface final : public ServiceFramework<NetworkInterface> {
247public: 249public:
248 explicit NetworkInterface(const char* name) : ServiceFramework{name} { 250 explicit NetworkInterface(const char* name, Core::System& system)
251 : ServiceFramework{name}, system(system) {
249 static const FunctionInfo functions[] = { 252 static const FunctionInfo functions[] = {
250 {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, 253 {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
251 {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"}, 254 {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"},
@@ -258,7 +261,7 @@ public:
258 261
259 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 262 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
260 rb.Push(RESULT_SUCCESS); 263 rb.Push(RESULT_SUCCESS);
261 rb.PushIpcInterface<IGeneralService>(); 264 rb.PushIpcInterface<IGeneralService>(system);
262 } 265 }
263 266
264 void CreateGeneralService(Kernel::HLERequestContext& ctx) { 267 void CreateGeneralService(Kernel::HLERequestContext& ctx) {
@@ -266,14 +269,17 @@ public:
266 269
267 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 270 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
268 rb.Push(RESULT_SUCCESS); 271 rb.Push(RESULT_SUCCESS);
269 rb.PushIpcInterface<IGeneralService>(); 272 rb.PushIpcInterface<IGeneralService>(system);
270 } 273 }
274
275private:
276 Core::System& system;
271}; 277};
272 278
273void InstallInterfaces(SM::ServiceManager& service_manager) { 279void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
274 std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager); 280 std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager);
275 std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager); 281 std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager);
276 std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager); 282 std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager);
277} 283}
278 284
279} // namespace Service::NIFM 285} // namespace Service::NIFM
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 4616b3b48..6857e18f9 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -8,9 +8,13 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::NIFM { 15namespace Service::NIFM {
12 16
13/// Registers all NIFM services with the specified service manager. 17/// Registers all NIFM services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& service_manager); 18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
15 19
16} // namespace Service::NIFM 20} // namespace Service::NIFM
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index f319a3ca1..75d414952 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -126,7 +126,7 @@ public:
126class IEnsureNetworkClockAvailabilityService final 126class IEnsureNetworkClockAvailabilityService final
127 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { 127 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
128public: 128public:
129 IEnsureNetworkClockAvailabilityService() 129 explicit IEnsureNetworkClockAvailabilityService(Core::System& system)
130 : ServiceFramework("IEnsureNetworkClockAvailabilityService") { 130 : ServiceFramework("IEnsureNetworkClockAvailabilityService") {
131 static const FunctionInfo functions[] = { 131 static const FunctionInfo functions[] = {
132 {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, 132 {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
@@ -139,7 +139,7 @@ public:
139 }; 139 };
140 RegisterHandlers(functions); 140 RegisterHandlers(functions);
141 141
142 auto& kernel = Core::System::GetInstance().Kernel(); 142 auto& kernel = system.Kernel();
143 finished_event = Kernel::WritableEvent::CreateEventPair( 143 finished_event = Kernel::WritableEvent::CreateEventPair(
144 kernel, Kernel::ResetType::Automatic, 144 kernel, Kernel::ResetType::Automatic,
145 "IEnsureNetworkClockAvailabilityService:FinishEvent"); 145 "IEnsureNetworkClockAvailabilityService:FinishEvent");
@@ -200,7 +200,7 @@ private:
200 200
201class NTC final : public ServiceFramework<NTC> { 201class NTC final : public ServiceFramework<NTC> {
202public: 202public:
203 explicit NTC() : ServiceFramework{"ntc"} { 203 explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) {
204 // clang-format off 204 // clang-format off
205 static const FunctionInfo functions[] = { 205 static const FunctionInfo functions[] = {
206 {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, 206 {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
@@ -218,7 +218,7 @@ private:
218 218
219 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 219 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
220 rb.Push(RESULT_SUCCESS); 220 rb.Push(RESULT_SUCCESS);
221 rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); 221 rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
222 } 222 }
223 223
224 // TODO(ogniK): Do we need these? 224 // TODO(ogniK): Do we need these?
@@ -235,13 +235,14 @@ private:
235 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
236 rb.Push(RESULT_SUCCESS); 236 rb.Push(RESULT_SUCCESS);
237 } 237 }
238 Core::System& system;
238}; 239};
239 240
240void InstallInterfaces(SM::ServiceManager& sm) { 241void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
241 std::make_shared<NIM>()->InstallAsService(sm); 242 std::make_shared<NIM>()->InstallAsService(sm);
242 std::make_shared<NIM_ECA>()->InstallAsService(sm); 243 std::make_shared<NIM_ECA>()->InstallAsService(sm);
243 std::make_shared<NIM_SHP>()->InstallAsService(sm); 244 std::make_shared<NIM_SHP>()->InstallAsService(sm);
244 std::make_shared<NTC>()->InstallAsService(sm); 245 std::make_shared<NTC>(system)->InstallAsService(sm);
245} 246}
246 247
247} // namespace Service::NIM 248} // namespace Service::NIM
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index 2a2a92df0..dbe25dc01 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::NIM { 15namespace Service::NIM {
12 16
13void InstallInterfaces(SM::ServiceManager& sm); 17void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
14 18
15} // namespace Service::NIM 19} // namespace Service::NIM
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 13121c4f1..15c156ce1 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -617,7 +617,8 @@ public:
617 } 617 }
618}; 618};
619 619
620void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) { 620void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
621
621 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); 622 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
622 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); 623 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
623 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); 624 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
@@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSyst
628 std::make_shared<NS_SU>()->InstallAsService(service_manager); 629 std::make_shared<NS_SU>()->InstallAsService(service_manager);
629 std::make_shared<NS_VM>()->InstallAsService(service_manager); 630 std::make_shared<NS_VM>()->InstallAsService(service_manager);
630 631
631 std::make_shared<PL_U>(fsc)->InstallAsService(service_manager); 632 std::make_shared<PL_U>(system)->InstallAsService(service_manager);
632} 633}
633 634
634} // namespace Service::NS 635} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index d067e7a9a..13a64ad88 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -97,8 +97,7 @@ private:
97}; 97};
98 98
99/// Registers all NS services with the specified service manager. 99/// Registers all NS services with the specified service manager.
100void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc); 100void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
101 101
102} // namespace NS 102} // namespace NS
103
104} // namespace Service 103} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 9d49f36e8..7dcdb4a07 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -150,8 +150,9 @@ struct PL_U::Impl {
150 std::vector<FontRegion> shared_font_regions; 150 std::vector<FontRegion> shared_font_regions;
151}; 151};
152 152
153PL_U::PL_U(FileSystem::FileSystemController& fsc) 153PL_U::PL_U(Core::System& system)
154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { 154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) {
155
155 static const FunctionInfo functions[] = { 156 static const FunctionInfo functions[] = {
156 {0, &PL_U::RequestLoad, "RequestLoad"}, 157 {0, &PL_U::RequestLoad, "RequestLoad"},
157 {1, &PL_U::GetLoadState, "GetLoadState"}, 158 {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -161,6 +162,9 @@ PL_U::PL_U(FileSystem::FileSystemController& fsc)
161 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, 162 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
162 }; 163 };
163 RegisterHandlers(functions); 164 RegisterHandlers(functions);
165
166 auto& fsc = system.GetFileSystemController();
167
164 // Attempt to load shared font data from disk 168 // Attempt to load shared font data from disk
165 const auto* nand = fsc.GetSystemNANDContents(); 169 const auto* nand = fsc.GetSystemNANDContents();
166 std::size_t offset = 0; 170 std::size_t offset = 0;
@@ -325,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
325 Kernel::MemoryState::Shared); 329 Kernel::MemoryState::Shared);
326 330
327 // Create shared font memory object 331 // Create shared font memory object
328 auto& kernel = Core::System::GetInstance().Kernel(); 332 auto& kernel = system.Kernel();
329 impl->shared_font_mem = Kernel::SharedMemory::Create( 333 impl->shared_font_mem = Kernel::SharedMemory::Create(
330 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, 334 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
331 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, 335 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index 35ca424d2..1063f4204 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -17,7 +17,7 @@ namespace NS {
17 17
18class PL_U final : public ServiceFramework<PL_U> { 18class PL_U final : public ServiceFramework<PL_U> {
19public: 19public:
20 PL_U(FileSystem::FileSystemController& fsc); 20 explicit PL_U(Core::System& system);
21 ~PL_U() override; 21 ~PL_U() override;
22 22
23private: 23private:
@@ -30,6 +30,7 @@ private:
30 30
31 struct Impl; 31 struct Impl;
32 std::unique_ptr<Impl> impl; 32 std::unique_ptr<Impl> impl;
33 Core::System& system;
33}; 34};
34 35
35} // namespace NS 36} // namespace NS
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 5b8248433..1b52511a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/nvdata.h" 11#include "core/hle/service/nvdrv/nvdata.h"
12#include "core/hle/service/service.h"
12 13
13namespace Core { 14namespace Core {
14class System; 15class System;
@@ -38,8 +39,9 @@ public:
38 * @param output A buffer where the output data will be written to. 39 * @param output A buffer where the output data will be written to.
39 * @returns The result code of the ioctl. 40 * @returns The result code of the ioctl.
40 */ 41 */
41 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 42 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
42 IoctlCtrl& ctrl) = 0; 43 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
44 IoctlVersion version) = 0;
43 45
44protected: 46protected:
45 Core::System& system; 47 Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 926a1285d..f764388bc 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,8 +17,9 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
18nvdisp_disp0 ::~nvdisp_disp0() = default; 18nvdisp_disp0 ::~nvdisp_disp0() = default;
19 19
20u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) { 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 23 UNIMPLEMENTED_MSG("Unimplemented ioctl");
23 return 0; 24 return 0;
24} 25}
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index e79e490ff..6fcdeee84 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,8 +20,9 @@ public:
20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
24 IoctlCtrl& ctrl) override; 24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
25 IoctlVersion version) override;
25 26
26 /// Performs a screen flip, drawing the buffer pointed to by the handle. 27 /// Performs a screen flip, drawing the buffer pointed to by the handle.
27 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 28 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 24ab3f2e9..6bc053f27 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -26,8 +26,9 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
27nvhost_as_gpu::~nvhost_as_gpu() = default; 27nvhost_as_gpu::~nvhost_as_gpu() = default;
28 28
29u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 29u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
30 IoctlCtrl& ctrl) { 30 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
31 IoctlVersion version) {
31 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 32 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
32 command.raw, input.size(), output.size()); 33 command.raw, input.size(), output.size());
33 34
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 30ca5f4c3..169fb8f0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -20,8 +20,9 @@ public:
20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvhost_as_gpu() override; 21 ~nvhost_as_gpu() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
24 IoctlCtrl& ctrl) override; 24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
25 IoctlVersion version) override;
25 26
26private: 27private:
27 enum class IoctlCommand : u32_le { 28 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 9a66a5f88..ff6b1abae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -19,8 +19,9 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
19 : nvdevice(system), events_interface{events_interface} {} 19 : nvdevice(system), events_interface{events_interface} {}
20nvhost_ctrl::~nvhost_ctrl() = default; 20nvhost_ctrl::~nvhost_ctrl() = default;
21 21
22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
23 IoctlCtrl& ctrl) { 23 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
24 IoctlVersion version) {
24 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 25 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
25 command.raw, input.size(), output.size()); 26 command.raw, input.size(), output.size());
26 27
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 14e6e7e57..9898623de 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -17,8 +17,9 @@ public:
17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); 17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
18 ~nvhost_ctrl() override; 18 ~nvhost_ctrl() override;
19 19
20 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) override; 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) override;
22 23
23private: 24private:
24 enum class IoctlCommand : u32_le { 25 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 988effd90..389ace76f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,14 +15,15 @@ namespace Service::Nvidia::Devices {
15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
19 IoctlCtrl& ctrl) { 19 const std::vector<u8>& input2, std::vector<u8>& output,
20 std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
20 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
21 command.raw, input.size(), output.size()); 22 command.raw, input.size(), output.size());
22 23
23 switch (static_cast<IoctlCommand>(command.raw)) { 24 switch (static_cast<IoctlCommand>(command.raw)) {
24 case IoctlCommand::IocGetCharacteristicsCommand: 25 case IoctlCommand::IocGetCharacteristicsCommand:
25 return GetCharacteristics(input, output); 26 return GetCharacteristics(input, output, output2, version);
26 case IoctlCommand::IocGetTPCMasksCommand: 27 case IoctlCommand::IocGetTPCMasksCommand:
27 return GetTPCMasks(input, output); 28 return GetTPCMasks(input, output);
28 case IoctlCommand::IocGetActiveSlotMaskCommand: 29 case IoctlCommand::IocGetActiveSlotMaskCommand:
@@ -44,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
44 return 0; 45 return 0;
45} 46}
46 47
47u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { 48u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
49 std::vector<u8>& output2, IoctlVersion version) {
48 LOG_DEBUG(Service_NVDRV, "called"); 50 LOG_DEBUG(Service_NVDRV, "called");
49 IoctlCharacteristics params{}; 51 IoctlCharacteristics params{};
50 std::memcpy(&params, input.data(), input.size()); 52 std::memcpy(&params, input.data(), input.size());
@@ -85,7 +87,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
85 params.gc.gr_compbit_store_base_hw = 0x0; 87 params.gc.gr_compbit_store_base_hw = 0x0;
86 params.gpu_characteristics_buf_size = 0xA0; 88 params.gpu_characteristics_buf_size = 0xA0;
87 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 89 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
88 std::memcpy(output.data(), &params, output.size()); 90
91 if (version == IoctlVersion::Version3) {
92 std::memcpy(output.data(), input.data(), output.size());
93 std::memcpy(output2.data(), &params.gc, output2.size());
94 } else {
95 std::memcpy(output.data(), &params, output.size());
96 }
89 return 0; 97 return 0;
90} 98}
91 99
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 2b035ae3f..642b0a2cb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_ctrl_gpu(Core::System& system); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
@@ -162,7 +163,8 @@ private:
162 }; 163 };
163 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); 164 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
164 165
165 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
167 std::vector<u8>& output2, IoctlVersion version);
166 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 168 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
167 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 169 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
168 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 170 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b4ee2a255..2b8d1bef6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -17,8 +17,9 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
18nvhost_gpu::~nvhost_gpu() = default; 18nvhost_gpu::~nvhost_gpu() = default;
19 19
20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) { 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) {
22 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 23 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
23 command.raw, input.size(), output.size()); 24 command.raw, input.size(), output.size());
24 25
@@ -50,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
50 return SubmitGPFIFO(input, output); 51 return SubmitGPFIFO(input, output);
51 } 52 }
52 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { 53 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
53 return KickoffPB(input, output); 54 return KickoffPB(input, output, input2, version);
54 } 55 }
55 } 56 }
56 57
@@ -173,7 +174,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
173 return 0; 174 return 0;
174} 175}
175 176
176u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) { 177u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
178 const std::vector<u8>& input2, IoctlVersion version) {
177 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 179 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
178 UNIMPLEMENTED(); 180 UNIMPLEMENTED();
179 } 181 }
@@ -183,9 +185,13 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
183 params.num_entries, params.flags.raw); 185 params.num_entries, params.flags.raw);
184 186
185 Tegra::CommandList entries(params.num_entries); 187 Tegra::CommandList entries(params.num_entries);
186 Memory::ReadBlock(params.address, entries.data(), 188 if (version == IoctlVersion::Version2) {
187 params.num_entries * sizeof(Tegra::CommandListHeader)); 189 std::memcpy(entries.data(), input2.data(),
188 190 params.num_entries * sizeof(Tegra::CommandListHeader));
191 } else {
192 Memory::ReadBlock(params.address, entries.data(),
193 params.num_entries * sizeof(Tegra::CommandListHeader));
194 }
189 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); 195 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
190 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); 196 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
191 197
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index d2e8fbae9..d056dd046 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -24,8 +24,9 @@ public:
24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
25 ~nvhost_gpu() override; 25 ~nvhost_gpu() override;
26 26
27 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 27 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
28 IoctlCtrl& ctrl) override; 28 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
29 IoctlVersion version) override;
29 30
30private: 31private:
31 enum class IoctlCommand : u32_le { 32 enum class IoctlCommand : u32_le {
@@ -183,7 +184,8 @@ private:
183 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 184 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
184 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 185 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
185 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 186 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
186 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output); 187 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
188 const std::vector<u8>& input2, IoctlVersion version);
187 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 189 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
188 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 190 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
189 191
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f572ad30f..bdae8b887 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} 13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
14nvhost_nvdec::~nvhost_nvdec() = default; 14nvhost_nvdec::~nvhost_nvdec() = default;
15 15
16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 2710f0511..cbdac8069 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_nvdec(Core::System& system); 16 explicit nvhost_nvdec(Core::System& system);
17 ~nvhost_nvdec() override; 17 ~nvhost_nvdec() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 38282956f..96e7b7dab 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 379766693..98dcac52f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_nvjpg(Core::System& system); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 70e8091db..c695b8863 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} 13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
14nvhost_vic::~nvhost_vic() = default; 14nvhost_vic::~nvhost_vic() = default;
15 15
16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 7d111977e..bec32bea1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_vic(Core::System& system); 16 explicit nvhost_vic(Core::System& system);
17 ~nvhost_vic() override; 17 ~nvhost_vic() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 223b496b7..8c742316c 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -28,8 +28,9 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
28 return object->addr; 28 return object->addr;
29} 29}
30 30
31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
32 IoctlCtrl& ctrl) { 32 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
33 IoctlVersion version) {
33 switch (static_cast<IoctlCommand>(command.raw)) { 34 switch (static_cast<IoctlCommand>(command.raw)) {
34 case IoctlCommand::Create: 35 case IoctlCommand::Create:
35 return IocCreate(input, output); 36 return IocCreate(input, output);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index bf4a101c2..73c2e8809 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -22,8 +22,9 @@ public:
22 /// Returns the allocated address of an nvmap object given its handle. 22 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 23 VAddr GetObjectAddress(u32 handle) const;
24 24
25 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 25 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
26 IoctlCtrl& ctrl) override; 26 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
27 IoctlVersion version) override;
27 28
28 /// Represents an nvmap object. 29 /// Represents an nvmap object.
29 struct Object { 30 struct Object {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d5be64ed2..5e0c23602 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -33,42 +33,77 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
33 rb.Push<u32>(0); 33 rb.Push<u32>(0);
34} 34}
35 35
36void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { 36void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
37 LOG_DEBUG(Service_NVDRV, "called");
38
39 IPC::RequestParser rp{ctx}; 37 IPC::RequestParser rp{ctx};
40 u32 fd = rp.Pop<u32>(); 38 u32 fd = rp.Pop<u32>();
41 u32 command = rp.Pop<u32>(); 39 u32 command = rp.Pop<u32>();
42 40
43 std::vector<u8> output(ctx.GetWriteBufferSize()); 41 /// Ioctl 3 has 2 outputs, first in the input params, second is the result
42 std::vector<u8> output(ctx.GetWriteBufferSize(0));
43 std::vector<u8> output2;
44 if (version == IoctlVersion::Version3) {
45 output2.resize((ctx.GetWriteBufferSize(1)));
46 }
47
48 /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
49 /// KickOfPB uses this
50 auto input = ctx.ReadBuffer(0);
51
52 std::vector<u8> input2;
53 if (version == IoctlVersion::Version2) {
54 input2 = ctx.ReadBuffer(1);
55 }
44 56
45 IoctlCtrl ctrl{}; 57 IoctlCtrl ctrl{};
46 58
47 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl); 59 u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
48 60
49 if (ctrl.must_delay) { 61 if (ctrl.must_delay) {
50 ctrl.fresh_call = false; 62 ctrl.fresh_call = false;
51 ctx.SleepClientThread( 63 ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
52 "NVServices::DelayedResponse", ctrl.timeout, 64 [=](Kernel::SharedPtr<Kernel::Thread> thread,
53 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 65 Kernel::HLERequestContext& ctx,
54 Kernel::ThreadWakeupReason reason) { 66 Kernel::ThreadWakeupReason reason) {
55 IoctlCtrl ctrl2{ctrl}; 67 IoctlCtrl ctrl2{ctrl};
56 std::vector<u8> output2 = output; 68 std::vector<u8> tmp_output = output;
57 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2); 69 std::vector<u8> tmp_output2 = output2;
58 ctx.WriteBuffer(output2); 70 u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
59 IPC::ResponseBuilder rb{ctx, 3}; 71 tmp_output2, ctrl2, version);
60 rb.Push(RESULT_SUCCESS); 72 ctx.WriteBuffer(tmp_output, 0);
61 rb.Push(result); 73 if (version == IoctlVersion::Version3) {
62 }, 74 ctx.WriteBuffer(tmp_output2, 1);
63 nvdrv->GetEventWriteable(ctrl.event_id)); 75 }
76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(result);
79 },
80 nvdrv->GetEventWriteable(ctrl.event_id));
64 } else { 81 } else {
65 ctx.WriteBuffer(output); 82 ctx.WriteBuffer(output);
83 if (version == IoctlVersion::Version3) {
84 ctx.WriteBuffer(output2, 1);
85 }
66 } 86 }
67 IPC::ResponseBuilder rb{ctx, 3}; 87 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
69 rb.Push(result); 89 rb.Push(result);
70} 90}
71 91
92void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
93 LOG_DEBUG(Service_NVDRV, "called");
94 IoctlBase(ctx, IoctlVersion::Version1);
95}
96
97void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
98 LOG_DEBUG(Service_NVDRV, "called");
99 IoctlBase(ctx, IoctlVersion::Version2);
100}
101
102void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
103 LOG_DEBUG(Service_NVDRV, "called");
104 IoctlBase(ctx, IoctlVersion::Version3);
105}
106
72void NVDRV::Close(Kernel::HLERequestContext& ctx) { 107void NVDRV::Close(Kernel::HLERequestContext& ctx) {
73 LOG_DEBUG(Service_NVDRV, "called"); 108 LOG_DEBUG(Service_NVDRV, "called");
74 109
@@ -154,8 +189,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
154 {8, &NVDRV::SetClientPID, "SetClientPID"}, 189 {8, &NVDRV::SetClientPID, "SetClientPID"},
155 {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, 190 {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
156 {10, nullptr, "InitializeDevtools"}, 191 {10, nullptr, "InitializeDevtools"},
157 {11, &NVDRV::Ioctl, "Ioctl2"}, 192 {11, &NVDRV::Ioctl2, "Ioctl2"},
158 {12, nullptr, "Ioctl3"}, 193 {12, &NVDRV::Ioctl3, "Ioctl3"},
159 {13, &NVDRV::FinishInitialize, "FinishInitialize"}, 194 {13, &NVDRV::FinishInitialize, "FinishInitialize"},
160 }; 195 };
161 RegisterHandlers(functions); 196 RegisterHandlers(functions);
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 10a0ecd52..9269ce00c 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -24,6 +24,8 @@ public:
24private: 24private:
25 void Open(Kernel::HLERequestContext& ctx); 25 void Open(Kernel::HLERequestContext& ctx);
26 void Ioctl(Kernel::HLERequestContext& ctx); 26 void Ioctl(Kernel::HLERequestContext& ctx);
27 void Ioctl2(Kernel::HLERequestContext& ctx);
28 void Ioctl3(Kernel::HLERequestContext& ctx);
27 void Close(Kernel::HLERequestContext& ctx); 29 void Close(Kernel::HLERequestContext& ctx);
28 void Initialize(Kernel::HLERequestContext& ctx); 30 void Initialize(Kernel::HLERequestContext& ctx);
29 void QueryEvent(Kernel::HLERequestContext& ctx); 31 void QueryEvent(Kernel::HLERequestContext& ctx);
@@ -31,6 +33,7 @@ private:
31 void FinishInitialize(Kernel::HLERequestContext& ctx); 33 void FinishInitialize(Kernel::HLERequestContext& ctx);
32 void GetStatus(Kernel::HLERequestContext& ctx); 34 void GetStatus(Kernel::HLERequestContext& ctx);
33 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); 35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
36 void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
34 37
35 std::shared_ptr<Module> nvdrv; 38 std::shared_ptr<Module> nvdrv;
36 39
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index ac03cbc23..529b03471 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -34,6 +34,12 @@ enum class EventState {
34 Busy = 3, 34 Busy = 3,
35}; 35};
36 36
37enum class IoctlVersion : u32 {
38 Version1,
39 Version2,
40 Version3,
41};
42
37struct IoctlCtrl { 43struct IoctlCtrl {
38 // First call done to the servioce for services that call itself again after a call. 44 // First call done to the servioce for services that call itself again after a call.
39 bool fresh_call{true}; 45 bool fresh_call{true};
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 2011a226a..307a7e928 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -71,13 +71,14 @@ u32 Module::Open(const std::string& device_name) {
71 return fd; 71 return fd;
72} 72}
73 73
74u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, 74u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
75 IoctlCtrl& ctrl) { 75 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
76 IoctlVersion version) {
76 auto itr = open_files.find(fd); 77 auto itr = open_files.find(fd);
77 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 78 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
78 79
79 auto& device = itr->second; 80 auto& device = itr->second;
80 return device->ioctl({command}, input, output, ctrl); 81 return device->ioctl({command}, input, input2, output, output2, ctrl, version);
81} 82}
82 83
83ResultCode Module::Close(u32 fd) { 84ResultCode Module::Close(u32 fd) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a339ab672..f8bb28969 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -106,8 +106,9 @@ public:
106 /// Opens a device node and returns a file descriptor to it. 106 /// Opens a device node and returns a file descriptor to it.
107 u32 Open(const std::string& device_name); 107 u32 Open(const std::string& device_name);
108 /// Sends an ioctl command to the specified file descriptor. 108 /// Sends an ioctl command to the specified file descriptor.
109 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, 109 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
110 IoctlCtrl& ctrl); 110 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
111 IoctlVersion version);
111 /// Closes a device file descriptor and returns operation success. 112 /// Closes a device file descriptor and returns operation success.
112 ResultCode Close(u32 fd); 113 ResultCode Close(u32 fd);
113 114
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index f9db79370..2e4d707b9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -29,26 +29,28 @@ namespace Service::NVFlinger {
29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); 29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); 30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
31 31
32NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 32NVFlinger::NVFlinger(Core::System& system) : system(system) {
33 displays.emplace_back(0, "Default"); 33 displays.emplace_back(0, "Default", system);
34 displays.emplace_back(1, "External"); 34 displays.emplace_back(1, "External", system);
35 displays.emplace_back(2, "Edid"); 35 displays.emplace_back(2, "Edid", system);
36 displays.emplace_back(3, "Internal"); 36 displays.emplace_back(3, "Internal", system);
37 displays.emplace_back(4, "Null"); 37 displays.emplace_back(4, "Null", system);
38 38
39 // Schedule the screen composition events 39 // Schedule the screen composition events
40 composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, 40 composition_event = system.CoreTiming().RegisterEvent(
41 s64 cycles_late) { 41 "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
42 Compose(); 42 Compose();
43 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); 43 const auto ticks =
44 this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event); 44 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
45 }); 45 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
46 46 composition_event);
47 core_timing.ScheduleEvent(frame_ticks, composition_event); 47 });
48
49 system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
48} 50}
49 51
50NVFlinger::~NVFlinger() { 52NVFlinger::~NVFlinger() {
51 core_timing.UnscheduleEvent(composition_event, 0); 53 system.CoreTiming().UnscheduleEvent(composition_event, 0);
52} 54}
53 55
54void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 56void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -185,11 +187,9 @@ void NVFlinger::Compose() {
185 MicroProfileFlip(); 187 MicroProfileFlip();
186 188
187 if (!buffer) { 189 if (!buffer) {
188 auto& system_instance = Core::System::GetInstance();
189
190 // There was no queued buffer to draw, render previous frame 190 // There was no queued buffer to draw, render previous frame
191 system_instance.GetPerfStats().EndGameFrame(); 191 system.GetPerfStats().EndGameFrame();
192 system_instance.GPU().SwapBuffers({}); 192 system.GPU().SwapBuffers({});
193 continue; 193 continue;
194 } 194 }
195 195
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 988be8726..5d7e3bfb8 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -38,7 +38,7 @@ class BufferQueue;
38 38
39class NVFlinger final { 39class NVFlinger final {
40public: 40public:
41 explicit NVFlinger(Core::Timing::CoreTiming& core_timing); 41 explicit NVFlinger(Core::System& system);
42 ~NVFlinger(); 42 ~NVFlinger();
43 43
44 /// Sets the NVDrv module instance to use to send buffers to the GPU. 44 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -105,8 +105,7 @@ private:
105 /// Event that handles screen composition. 105 /// Event that handles screen composition.
106 Core::Timing::EventType* composition_event; 106 Core::Timing::EventType* composition_event;
107 107
108 /// Core timing instance for registering/unregistering the composition event. 108 Core::System& system;
109 Core::Timing::CoreTiming& core_timing;
110}; 109};
111 110
112} // namespace Service::NVFlinger 111} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 0f79135ff..18d895263 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -15,7 +15,7 @@ namespace Service::PlayReport {
15 15
16class PlayReport final : public ServiceFramework<PlayReport> { 16class PlayReport final : public ServiceFramework<PlayReport> {
17public: 17public:
18 explicit PlayReport(Core::System& system, const char* name) 18 explicit PlayReport(const char* name, Core::System& system)
19 : ServiceFramework{name}, system(system) { 19 : ServiceFramework{name}, system(system) {
20 // clang-format off 20 // clang-format off
21 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
@@ -128,12 +128,12 @@ private:
128 Core::System& system; 128 Core::System& system;
129}; 129};
130 130
131void InstallInterfaces(Core::System& system) { 131void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
132 std::make_shared<PlayReport>(system, "prepo:a")->InstallAsService(system.ServiceManager()); 132 std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager);
133 std::make_shared<PlayReport>(system, "prepo:a2")->InstallAsService(system.ServiceManager()); 133 std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager);
134 std::make_shared<PlayReport>(system, "prepo:m")->InstallAsService(system.ServiceManager()); 134 std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager);
135 std::make_shared<PlayReport>(system, "prepo:s")->InstallAsService(system.ServiceManager()); 135 std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager);
136 std::make_shared<PlayReport>(system, "prepo:u")->InstallAsService(system.ServiceManager()); 136 std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager);
137} 137}
138 138
139} // namespace Service::PlayReport 139} // namespace Service::PlayReport
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h
index 0ebc3a938..a5682ee26 100644
--- a/src/core/hle/service/prepo/prepo.h
+++ b/src/core/hle/service/prepo/prepo.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::PlayReport { 15namespace Service::PlayReport {
12 16
13void InstallInterfaces(Core::System& system); 17void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
14 18
15} // namespace Service::PlayReport 19} // namespace Service::PlayReport
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 906fdc415..831a427de 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -198,50 +198,50 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
200 // here and pass it into the respective InstallInterfaces functions. 200 // here and pass it into the respective InstallInterfaces functions.
201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); 201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system);
202 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 202 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
203 203
204 SM::ServiceManager::InstallInterfaces(sm); 204 SM::ServiceManager::InstallInterfaces(sm);
205 205
206 Account::InstallInterfaces(system); 206 Account::InstallInterfaces(system);
207 AM::InstallInterfaces(*sm, nv_flinger, system); 207 AM::InstallInterfaces(*sm, nv_flinger, system);
208 AOC::InstallInterfaces(*sm); 208 AOC::InstallInterfaces(*sm, system);
209 APM::InstallInterfaces(system); 209 APM::InstallInterfaces(system);
210 Audio::InstallInterfaces(*sm, system); 210 Audio::InstallInterfaces(*sm, system);
211 BCAT::InstallInterfaces(*sm); 211 BCAT::InstallInterfaces(*sm);
212 BPC::InstallInterfaces(*sm); 212 BPC::InstallInterfaces(*sm);
213 BtDrv::InstallInterfaces(*sm); 213 BtDrv::InstallInterfaces(*sm, system);
214 BTM::InstallInterfaces(*sm); 214 BTM::InstallInterfaces(*sm, system);
215 Capture::InstallInterfaces(*sm); 215 Capture::InstallInterfaces(*sm);
216 ERPT::InstallInterfaces(*sm); 216 ERPT::InstallInterfaces(*sm);
217 ES::InstallInterfaces(*sm); 217 ES::InstallInterfaces(*sm);
218 EUPLD::InstallInterfaces(*sm); 218 EUPLD::InstallInterfaces(*sm);
219 Fatal::InstallInterfaces(*sm); 219 Fatal::InstallInterfaces(*sm, system);
220 FGM::InstallInterfaces(*sm); 220 FGM::InstallInterfaces(*sm);
221 FileSystem::InstallInterfaces(system); 221 FileSystem::InstallInterfaces(system);
222 Friend::InstallInterfaces(*sm); 222 Friend::InstallInterfaces(*sm, system);
223 Glue::InstallInterfaces(system); 223 Glue::InstallInterfaces(system);
224 GRC::InstallInterfaces(*sm); 224 GRC::InstallInterfaces(*sm);
225 HID::InstallInterfaces(*sm); 225 HID::InstallInterfaces(*sm, system);
226 LBL::InstallInterfaces(*sm); 226 LBL::InstallInterfaces(*sm);
227 LDN::InstallInterfaces(*sm); 227 LDN::InstallInterfaces(*sm);
228 LDR::InstallInterfaces(*sm); 228 LDR::InstallInterfaces(*sm, system);
229 LM::InstallInterfaces(*sm); 229 LM::InstallInterfaces(*sm);
230 Migration::InstallInterfaces(*sm); 230 Migration::InstallInterfaces(*sm);
231 Mii::InstallInterfaces(*sm); 231 Mii::InstallInterfaces(*sm);
232 MM::InstallInterfaces(*sm); 232 MM::InstallInterfaces(*sm);
233 NCM::InstallInterfaces(*sm); 233 NCM::InstallInterfaces(*sm);
234 NFC::InstallInterfaces(*sm); 234 NFC::InstallInterfaces(*sm);
235 NFP::InstallInterfaces(*sm); 235 NFP::InstallInterfaces(*sm, system);
236 NIFM::InstallInterfaces(*sm); 236 NIFM::InstallInterfaces(*sm, system);
237 NIM::InstallInterfaces(*sm); 237 NIM::InstallInterfaces(*sm, system);
238 NPNS::InstallInterfaces(*sm); 238 NPNS::InstallInterfaces(*sm);
239 NS::InstallInterfaces(*sm, system.GetFileSystemController()); 239 NS::InstallInterfaces(*sm, system);
240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system); 240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
241 PCIe::InstallInterfaces(*sm); 241 PCIe::InstallInterfaces(*sm);
242 PCTL::InstallInterfaces(*sm); 242 PCTL::InstallInterfaces(*sm);
243 PCV::InstallInterfaces(*sm); 243 PCV::InstallInterfaces(*sm);
244 PlayReport::InstallInterfaces(system); 244 PlayReport::InstallInterfaces(*sm, system);
245 PM::InstallInterfaces(system); 245 PM::InstallInterfaces(system);
246 PSC::InstallInterfaces(*sm); 246 PSC::InstallInterfaces(*sm);
247 PSM::InstallInterfaces(*sm); 247 PSM::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 1030185e0..9565e7de5 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -7,8 +7,8 @@
7namespace Service::Time { 7namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
10 const char* name) 10 Core::System& system, const char* name)
11 : Module::Interface(std::move(time), std::move(shared_memory), name) { 11 : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
12 // clang-format off 12 // clang-format off
13 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index bdf0883e2..5c63a07f4 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -13,7 +13,7 @@ class SharedMemory;
13class Time final : public Module::Interface { 13class Time final : public Module::Interface {
14public: 14public:
15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
16 const char* name); 16 Core::System& system, const char* name);
17 ~Time() override; 17 ~Time() override;
18}; 18};
19 19
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index ae6446204..1b9ab8401 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -126,8 +126,8 @@ private:
126 126
127class ISteadyClock final : public ServiceFramework<ISteadyClock> { 127class ISteadyClock final : public ServiceFramework<ISteadyClock> {
128public: 128public:
129 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory) 129 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
130 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) { 130 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
131 static const FunctionInfo functions[] = { 131 static const FunctionInfo functions[] = {
132 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, 132 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
133 }; 133 };
@@ -150,12 +150,13 @@ private:
150 } 150 }
151 151
152 SteadyClockTimePoint GetCurrentTimePoint() const { 152 SteadyClockTimePoint GetCurrentTimePoint() const {
153 const auto& core_timing = Core::System::GetInstance().CoreTiming(); 153 const auto& core_timing = system.CoreTiming();
154 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 154 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
155 return {static_cast<u64_le>(ms.count() / 1000), {}}; 155 return {static_cast<u64_le>(ms.count() / 1000), {}};
156 } 156 }
157 157
158 std::shared_ptr<SharedMemory> shared_memory; 158 std::shared_ptr<SharedMemory> shared_memory;
159 Core::System& system;
159}; 160};
160 161
161class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { 162class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -290,7 +291,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
290 291
291 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 292 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
292 rb.Push(RESULT_SUCCESS); 293 rb.Push(RESULT_SUCCESS);
293 rb.PushIpcInterface<ISteadyClock>(shared_memory); 294 rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
294} 295}
295 296
296void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 297void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -325,7 +326,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
325 return; 326 return;
326 } 327 }
327 328
328 const auto& core_timing = Core::System::GetInstance().CoreTiming(); 329 const auto& core_timing = system.CoreTiming();
329 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 330 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
330 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; 331 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
331 332
@@ -407,8 +408,10 @@ void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
407} 408}
408 409
409Module::Interface::Interface(std::shared_ptr<Module> time, 410Module::Interface::Interface(std::shared_ptr<Module> time,
410 std::shared_ptr<SharedMemory> shared_memory, const char* name) 411 std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
411 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {} 412 const char* name)
413 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
414 system(system) {}
412 415
413Module::Interface::~Interface() = default; 416Module::Interface::~Interface() = default;
414 417
@@ -416,9 +419,11 @@ void InstallInterfaces(Core::System& system) {
416 auto time = std::make_shared<Module>(); 419 auto time = std::make_shared<Module>();
417 auto shared_mem = std::make_shared<SharedMemory>(system); 420 auto shared_mem = std::make_shared<SharedMemory>(system);
418 421
419 std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager()); 422 std::make_shared<Time>(time, shared_mem, system, "time:a")
420 std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager()); 423 ->InstallAsService(system.ServiceManager());
421 std::make_shared<Time>(std::move(time), shared_mem, "time:u") 424 std::make_shared<Time>(time, shared_mem, system, "time:s")
425 ->InstallAsService(system.ServiceManager());
426 std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
422 ->InstallAsService(system.ServiceManager()); 427 ->InstallAsService(system.ServiceManager());
423} 428}
424 429
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index e0708f856..c32d32860 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -80,7 +80,8 @@ public:
80 class Interface : public ServiceFramework<Interface> { 80 class Interface : public ServiceFramework<Interface> {
81 public: 81 public:
82 explicit Interface(std::shared_ptr<Module> time, 82 explicit Interface(std::shared_ptr<Module> time,
83 std::shared_ptr<SharedMemory> shared_memory, const char* name); 83 std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
84 const char* name);
84 ~Interface() override; 85 ~Interface() override;
85 86
86 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); 87 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -97,6 +98,7 @@ public:
97 protected: 98 protected:
98 std::shared_ptr<Module> time; 99 std::shared_ptr<Module> time;
99 std::shared_ptr<SharedMemory> shared_memory; 100 std::shared_ptr<SharedMemory> shared_memory;
101 Core::System& system;
100 }; 102 };
101}; 103};
102 104
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index a8d088305..006a6d9ff 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -15,8 +15,8 @@
15 15
16namespace Service::VI { 16namespace Service::VI {
17 17
18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { 18Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
19 auto& kernel = Core::System::GetInstance().Kernel(); 19 auto& kernel = system.Kernel();
20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
21 fmt::format("Display VSync Event {}", id)); 21 fmt::format("Display VSync Event {}", id));
22} 22}
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 2acd46ff8..f56b5badc 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -26,7 +26,7 @@ public:
26 /// @param id The unique ID for this display. 26 /// @param id The unique ID for this display.
27 /// @param name The name for this display. 27 /// @param name The name for this display.
28 /// 28 ///
29 Display(u64 id, std::string name); 29 Display(u64 id, std::string name, Core::System& system);
30 ~Display(); 30 ~Display();
31 31
32 Display(const Display&) = delete; 32 Display(const Display&) = delete;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 70c90109f..e75c700ad 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -152,8 +152,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
152 auto& system = Core::System::GetInstance(); 152 auto& system = Core::System::GetInstance();
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) { 154 if (!cheats.empty()) {
155 system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base, 155 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
156 load_base + program_image.size());
157 } 156 }
158 } 157 }
159 158
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
new file mode 100644
index 000000000..b56cb0627
--- /dev/null
+++ b/src/core/memory/cheat_engine.cpp
@@ -0,0 +1,234 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <locale>
6#include "common/hex_util.h"
7#include "common/microprofile.h"
8#include "common/swap.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/service/hid/controllers/npad.h"
14#include "core/hle/service/hid/hid.h"
15#include "core/hle/service/sm/sm.h"
16#include "core/memory/cheat_engine.h"
17
18namespace Memory {
19
20constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
21constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
22
23StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
24 const CheatProcessMetadata& metadata)
25 : system(system), metadata(metadata) {}
26
27StandardVmCallbacks::~StandardVmCallbacks() = default;
28
29void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
30 ReadBlock(SanitizeAddress(address), data, size);
31}
32
33void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
34 WriteBlock(SanitizeAddress(address), data, size);
35}
36
37u64 StandardVmCallbacks::HidKeysDown() {
38 const auto applet_resource =
39 system.ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
40 if (applet_resource == nullptr) {
41 LOG_WARNING(CheatEngine,
42 "Attempted to read input state, but applet resource is not initialized!");
43 return false;
44 }
45
46 const auto press_state =
47 applet_resource
48 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
49 .GetAndResetPressState();
50 return press_state & KEYPAD_BITMASK;
51}
52
53void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
54 LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
55}
56
57void StandardVmCallbacks::CommandLog(std::string_view data) {
58 LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
59 data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
60}
61
62VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
63 if ((in < metadata.main_nso_extents.base ||
64 in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
65 (in < metadata.heap_extents.base ||
66 in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
67 LOG_ERROR(CheatEngine,
68 "Cheat attempting to access memory at invalid address={:016X}, if this "
69 "persists, "
70 "the cheat may be incorrect. However, this may be normal early in execution if "
71 "the game has not properly set up yet.",
72 in);
73 return 0; ///< Invalid addresses will hard crash
74 }
75
76 return in;
77}
78
79CheatParser::~CheatParser() = default;
80
81TextCheatParser::~TextCheatParser() = default;
82
83namespace {
84template <char match>
85std::string_view ExtractName(std::string_view data, std::size_t start_index) {
86 auto end_index = start_index;
87 while (data[end_index] != match) {
88 ++end_index;
89 if (end_index > data.size() ||
90 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
91 return {};
92 }
93 }
94
95 return data.substr(start_index, end_index - start_index);
96}
97} // Anonymous namespace
98
99std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
100 std::string_view data) const {
101 std::vector<CheatEntry> out(1);
102 std::optional<u64> current_entry = std::nullopt;
103
104 for (std::size_t i = 0; i < data.size(); ++i) {
105 if (::isspace(data[i])) {
106 continue;
107 }
108
109 if (data[i] == '{') {
110 current_entry = 0;
111
112 if (out[*current_entry].definition.num_opcodes > 0) {
113 return {};
114 }
115
116 const auto name = ExtractName<'}'>(data, i + 1);
117 if (name.empty()) {
118 return {};
119 }
120
121 std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
122 std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
123 name.size()));
124 out[*current_entry]
125 .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
126 '\0';
127
128 i += name.length() + 1;
129 } else if (data[i] == '[') {
130 current_entry = out.size();
131 out.emplace_back();
132
133 const auto name = ExtractName<']'>(data, i + 1);
134 if (name.empty()) {
135 return {};
136 }
137
138 std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
139 std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
140 name.size()));
141 out[*current_entry]
142 .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
143 '\0';
144
145 i += name.length() + 1;
146 } else if (::isxdigit(data[i])) {
147 if (!current_entry || out[*current_entry].definition.num_opcodes >=
148 out[*current_entry].definition.opcodes.size()) {
149 return {};
150 }
151
152 const auto hex = std::string(data.substr(i, 8));
153 if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
154 return {};
155 }
156
157 out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
158 std::stoul(hex, nullptr, 0x10);
159
160 i += 8;
161 } else {
162 return {};
163 }
164 }
165
166 out[0].enabled = out[0].definition.num_opcodes > 0;
167 out[0].cheat_id = 0;
168
169 for (u32 i = 1; i < out.size(); ++i) {
170 out[i].enabled = out[i].definition.num_opcodes > 0;
171 out[i].cheat_id = i;
172 }
173
174 return out;
175}
176
177CheatEngine::CheatEngine(Core::System& system, std::vector<CheatEntry> cheats,
178 const std::array<u8, 0x20>& build_id)
179 : system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique<StandardVmCallbacks>(
180 system, metadata)},
181 cheats(std::move(cheats)) {
182 metadata.main_nso_build_id = build_id;
183}
184
185CheatEngine::~CheatEngine() {
186 core_timing.UnscheduleEvent(event, 0);
187}
188
189void CheatEngine::Initialize() {
190 event = core_timing.RegisterEvent(
191 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
192 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
193 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
194
195 metadata.process_id = system.CurrentProcess()->GetProcessID();
196 metadata.title_id = system.CurrentProcess()->GetTitleID();
197
198 const auto& vm_manager = system.CurrentProcess()->VMManager();
199 metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()};
200 metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(),
201 vm_manager.GetAddressSpaceSize()};
202 metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()};
203
204 is_pending_reload.exchange(true);
205}
206
207void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
208 metadata.main_nso_extents = {main_region_begin, main_region_size};
209}
210
211void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
212 this->cheats = std::move(cheats);
213 is_pending_reload.exchange(true);
214}
215
216MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
217
218void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
219 if (is_pending_reload.exchange(false)) {
220 vm.LoadProgram(cheats);
221 }
222
223 if (vm.GetProgramSize() == 0) {
224 return;
225 }
226
227 MICROPROFILE_SCOPE(Cheat_Engine);
228
229 vm.Execute(metadata);
230
231 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
232}
233
234} // namespace Memory
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
new file mode 100644
index 000000000..0f012e9b5
--- /dev/null
+++ b/src/core/memory/cheat_engine.h
@@ -0,0 +1,86 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <vector>
9#include "common/common_types.h"
10#include "core/memory/dmnt_cheat_types.h"
11#include "core/memory/dmnt_cheat_vm.h"
12
13namespace Core {
14class System;
15}
16
17namespace Core::Timing {
18class CoreTiming;
19struct EventType;
20} // namespace Core::Timing
21
22namespace Memory {
23
24class StandardVmCallbacks : public DmntCheatVm::Callbacks {
25public:
26 StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
27 ~StandardVmCallbacks() override;
28
29 void MemoryRead(VAddr address, void* data, u64 size) override;
30 void MemoryWrite(VAddr address, const void* data, u64 size) override;
31 u64 HidKeysDown() override;
32 void DebugLog(u8 id, u64 value) override;
33 void CommandLog(std::string_view data) override;
34
35private:
36 VAddr SanitizeAddress(VAddr address) const;
37
38 const CheatProcessMetadata& metadata;
39 const Core::System& system;
40};
41
42// Intermediary class that parses a text file or other disk format for storing cheats into a
43// CheatList object, that can be used for execution.
44class CheatParser {
45public:
46 virtual ~CheatParser();
47
48 virtual std::vector<CheatEntry> Parse(const Core::System& system,
49 std::string_view data) const = 0;
50};
51
52// CheatParser implementation that parses text files
53class TextCheatParser final : public CheatParser {
54public:
55 ~TextCheatParser() override;
56
57 std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override;
58};
59
60// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
61class CheatEngine final {
62public:
63 CheatEngine(Core::System& system_, std::vector<CheatEntry> cheats_,
64 const std::array<u8, 0x20>& build_id);
65 ~CheatEngine();
66
67 void Initialize();
68 void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
69
70 void Reload(std::vector<CheatEntry> cheats);
71
72private:
73 void FrameCallback(u64 userdata, s64 cycles_late);
74
75 DmntCheatVm vm;
76 CheatProcessMetadata metadata;
77
78 std::vector<CheatEntry> cheats;
79 std::atomic_bool is_pending_reload{false};
80
81 Core::Timing::EventType* event{};
82 Core::Timing::CoreTiming& core_timing;
83 Core::System& system;
84};
85
86} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h
new file mode 100644
index 000000000..bf68fa0fe
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_types.h
@@ -0,0 +1,58 @@
1/*
2 * Copyright (c) 2018-2019 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2019 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#pragma once
26
27#include "common/common_types.h"
28
29namespace Memory {
30
31struct MemoryRegionExtents {
32 u64 base{};
33 u64 size{};
34};
35
36struct CheatProcessMetadata {
37 u64 process_id{};
38 u64 title_id{};
39 MemoryRegionExtents main_nso_extents{};
40 MemoryRegionExtents heap_extents{};
41 MemoryRegionExtents alias_extents{};
42 MemoryRegionExtents address_space_extents{};
43 std::array<u8, 0x20> main_nso_build_id{};
44};
45
46struct CheatDefinition {
47 std::array<char, 0x40> readable_name{};
48 u32 num_opcodes{};
49 std::array<u32, 0x100> opcodes{};
50};
51
52struct CheatEntry {
53 bool enabled{};
54 u32 cheat_id{};
55 CheatDefinition definition{};
56};
57
58} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
new file mode 100644
index 000000000..cc16d15a4
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -0,0 +1,1212 @@
1/*
2 * Copyright (c) 2018-2019 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2019 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#include "common/assert.h"
26#include "common/scope_exit.h"
27#include "core/memory/dmnt_cheat_types.h"
28#include "core/memory/dmnt_cheat_vm.h"
29
30namespace Memory {
31
32DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {}
33
34DmntCheatVm::~DmntCheatVm() = default;
35
36void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
37 callbacks->DebugLog(static_cast<u8>(log_id), value);
38}
39
40void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
41 if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
42 callbacks->CommandLog("Opcode: Store Static");
43 callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
44 callbacks->CommandLog(
45 fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
46 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
47 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
48 callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
49 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
50 callbacks->CommandLog("Opcode: Begin Conditional");
51 callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
52 callbacks->CommandLog(
53 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
54 callbacks->CommandLog(
55 fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
56 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
57 callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
58 } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) {
59 callbacks->CommandLog("Opcode: End Conditional");
60 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
61 if (ctrl_loop->start_loop) {
62 callbacks->CommandLog("Opcode: Start Loop");
63 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
64 callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
65 } else {
66 callbacks->CommandLog("Opcode: End Loop");
67 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
68 }
69 } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
70 callbacks->CommandLog("Opcode: Load Register Static");
71 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
72 callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value));
73 } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
74 callbacks->CommandLog("Opcode: Load Register Memory");
75 callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
76 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
77 callbacks->CommandLog(
78 fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
79 callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
80 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
81 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
82 callbacks->CommandLog("Opcode: Store Static to Address");
83 callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
84 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
85 if (str_static->add_offset_reg) {
86 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
87 }
88 callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
89 callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
90 } else if (auto perform_math_static =
91 std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
92 callbacks->CommandLog("Opcode: Perform Static Arithmetic");
93 callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
94 callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
95 callbacks->CommandLog(
96 fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
97 callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
98 } else if (auto begin_keypress_cond =
99 std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
100 callbacks->CommandLog("Opcode: Begin Keypress Conditional");
101 callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
102 } else if (auto perform_math_reg =
103 std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
104 callbacks->CommandLog("Opcode: Perform Register Arithmetic");
105 callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
106 callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
107 callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
108 if (perform_math_reg->has_immediate) {
109 callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
110 } else {
111 callbacks->CommandLog(
112 fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
113 }
114 } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
115 callbacks->CommandLog("Opcode: Store Register to Address");
116 callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
117 callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
118 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
119 callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
120 switch (str_register->ofs_type) {
121 case StoreRegisterOffsetType::None:
122 break;
123 case StoreRegisterOffsetType::Reg:
124 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
125 break;
126 case StoreRegisterOffsetType::Imm:
127 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
128 break;
129 case StoreRegisterOffsetType::MemReg:
130 callbacks->CommandLog(
131 fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
132 break;
133 case StoreRegisterOffsetType::MemImm:
134 case StoreRegisterOffsetType::MemImmReg:
135 callbacks->CommandLog(
136 fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
137 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
138 break;
139 }
140 } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
141 callbacks->CommandLog("Opcode: Begin Register Conditional");
142 callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
143 callbacks->CommandLog(
144 fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
145 callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
146 switch (begin_reg_cond->comp_type) {
147 case CompareRegisterValueType::StaticValue:
148 callbacks->CommandLog("Comp Type: Static Value");
149 callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
150 break;
151 case CompareRegisterValueType::OtherRegister:
152 callbacks->CommandLog("Comp Type: Other Register");
153 callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
154 break;
155 case CompareRegisterValueType::MemoryRelAddr:
156 callbacks->CommandLog("Comp Type: Memory Relative Address");
157 callbacks->CommandLog(
158 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
159 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
160 break;
161 case CompareRegisterValueType::MemoryOfsReg:
162 callbacks->CommandLog("Comp Type: Memory Offset Register");
163 callbacks->CommandLog(
164 fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
165 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
166 break;
167 case CompareRegisterValueType::RegisterRelAddr:
168 callbacks->CommandLog("Comp Type: Register Relative Address");
169 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
170 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
171 break;
172 case CompareRegisterValueType::RegisterOfsReg:
173 callbacks->CommandLog("Comp Type: Register Offset Register");
174 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
175 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
176 break;
177 }
178 } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
179 callbacks->CommandLog("Opcode: Save or Restore Register");
180 callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
181 callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
182 callbacks->CommandLog(
183 fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type)));
184 } else if (auto save_restore_regmask =
185 std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
186 callbacks->CommandLog("Opcode: Save or Restore Register Mask");
187 callbacks->CommandLog(
188 fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
189 for (std::size_t i = 0; i < NumRegisters; i++) {
190 callbacks->CommandLog(
191 fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
192 }
193 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
194 callbacks->CommandLog("Opcode: Debug Log");
195 callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
196 callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
197 callbacks->CommandLog(
198 fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
199 switch (debug_log->val_type) {
200 case DebugLogValueType::RegisterValue:
201 callbacks->CommandLog("Val Type: Register Value");
202 callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
203 break;
204 case DebugLogValueType::MemoryRelAddr:
205 callbacks->CommandLog("Val Type: Memory Relative Address");
206 callbacks->CommandLog(
207 fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
208 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
209 break;
210 case DebugLogValueType::MemoryOfsReg:
211 callbacks->CommandLog("Val Type: Memory Offset Register");
212 callbacks->CommandLog(
213 fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
214 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
215 break;
216 case DebugLogValueType::RegisterRelAddr:
217 callbacks->CommandLog("Val Type: Register Relative Address");
218 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
219 callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
220 break;
221 case DebugLogValueType::RegisterOfsReg:
222 callbacks->CommandLog("Val Type: Register Offset Register");
223 callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
224 callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
225 break;
226 }
227 } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
228 callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
229 }
230}
231
232DmntCheatVm::Callbacks::~Callbacks() = default;
233
234bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
235 // If we've ever seen a decode failure, return false.
236 bool valid = decode_success;
237 CheatVmOpcode opcode = {};
238 SCOPE_EXIT({
239 decode_success &= valid;
240 if (valid) {
241 out = opcode;
242 }
243 });
244
245 // Helper function for getting instruction dwords.
246 const auto GetNextDword = [&] {
247 if (instruction_ptr >= num_opcodes) {
248 valid = false;
249 return static_cast<u32>(0);
250 }
251 return program[instruction_ptr++];
252 };
253
254 // Helper function for parsing a VmInt.
255 const auto GetNextVmInt = [&](const u32 bit_width) {
256 VmInt val{};
257
258 const u32 first_dword = GetNextDword();
259 switch (bit_width) {
260 case 1:
261 val.bit8 = static_cast<u8>(first_dword);
262 break;
263 case 2:
264 val.bit16 = static_cast<u16>(first_dword);
265 break;
266 case 4:
267 val.bit32 = first_dword;
268 break;
269 case 8:
270 val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword());
271 break;
272 }
273
274 return val;
275 };
276
277 // Read opcode.
278 const u32 first_dword = GetNextDword();
279 if (!valid) {
280 return valid;
281 }
282
283 auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF));
284 if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) {
285 opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
286 ((first_dword >> 24) & 0xF));
287 }
288 if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) {
289 opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
290 ((first_dword >> 20) & 0xF));
291 }
292
293 // detect condition start.
294 switch (opcode_type) {
295 case CheatVmOpcodeType::BeginConditionalBlock:
296 case CheatVmOpcodeType::BeginKeypressConditionalBlock:
297 case CheatVmOpcodeType::BeginRegisterConditionalBlock:
298 opcode.begin_conditional_block = true;
299 break;
300 default:
301 opcode.begin_conditional_block = false;
302 break;
303 }
304
305 switch (opcode_type) {
306 case CheatVmOpcodeType::StoreStatic: {
307 StoreStaticOpcode store_static{};
308 // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
309 // Read additional words.
310 const u32 second_dword = GetNextDword();
311 store_static.bit_width = (first_dword >> 24) & 0xF;
312 store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
313 store_static.offset_register = ((first_dword >> 16) & 0xF);
314 store_static.rel_address =
315 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
316 store_static.value = GetNextVmInt(store_static.bit_width);
317 opcode.opcode = store_static;
318 } break;
319 case CheatVmOpcodeType::BeginConditionalBlock: {
320 BeginConditionalOpcode begin_cond{};
321 // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
322 // Read additional words.
323 const u32 second_dword = GetNextDword();
324 begin_cond.bit_width = (first_dword >> 24) & 0xF;
325 begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
326 begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
327 begin_cond.rel_address =
328 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
329 begin_cond.value = GetNextVmInt(begin_cond.bit_width);
330 opcode.opcode = begin_cond;
331 } break;
332 case CheatVmOpcodeType::EndConditionalBlock: {
333 // 20000000
334 // There's actually nothing left to process here!
335 opcode.opcode = EndConditionalOpcode{};
336 } break;
337 case CheatVmOpcodeType::ControlLoop: {
338 ControlLoopOpcode ctrl_loop{};
339 // 300R0000 VVVVVVVV
340 // 310R0000
341 // Parse register, whether loop start or loop end.
342 ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
343 ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
344
345 // Read number of iters if loop start.
346 if (ctrl_loop.start_loop) {
347 ctrl_loop.num_iters = GetNextDword();
348 }
349 opcode.opcode = ctrl_loop;
350 } break;
351 case CheatVmOpcodeType::LoadRegisterStatic: {
352 LoadRegisterStaticOpcode ldr_static{};
353 // 400R0000 VVVVVVVV VVVVVVVV
354 // Read additional words.
355 ldr_static.reg_index = ((first_dword >> 16) & 0xF);
356 ldr_static.value =
357 (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
358 opcode.opcode = ldr_static;
359 } break;
360 case CheatVmOpcodeType::LoadRegisterMemory: {
361 LoadRegisterMemoryOpcode ldr_memory{};
362 // 5TMRI0AA AAAAAAAA
363 // Read additional words.
364 const u32 second_dword = GetNextDword();
365 ldr_memory.bit_width = (first_dword >> 24) & 0xF;
366 ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
367 ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
368 ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
369 ldr_memory.rel_address =
370 (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
371 opcode.opcode = ldr_memory;
372 } break;
373 case CheatVmOpcodeType::StoreStaticToAddress: {
374 StoreStaticToAddressOpcode str_static{};
375 // 6T0RIor0 VVVVVVVV VVVVVVVV
376 // Read additional words.
377 str_static.bit_width = (first_dword >> 24) & 0xF;
378 str_static.reg_index = ((first_dword >> 16) & 0xF);
379 str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
380 str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
381 str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
382 str_static.value =
383 (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
384 opcode.opcode = str_static;
385 } break;
386 case CheatVmOpcodeType::PerformArithmeticStatic: {
387 PerformArithmeticStaticOpcode perform_math_static{};
388 // 7T0RC000 VVVVVVVV
389 // Read additional words.
390 perform_math_static.bit_width = (first_dword >> 24) & 0xF;
391 perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
392 perform_math_static.math_type =
393 static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF);
394 perform_math_static.value = GetNextDword();
395 opcode.opcode = perform_math_static;
396 } break;
397 case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
398 BeginKeypressConditionalOpcode begin_keypress_cond{};
399 // 8kkkkkkk
400 // Just parse the mask.
401 begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
402 } break;
403 case CheatVmOpcodeType::PerformArithmeticRegister: {
404 PerformArithmeticRegisterOpcode perform_math_reg{};
405 // 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
406 perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
407 perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF);
408 perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
409 perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
410 perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
411 if (perform_math_reg.has_immediate) {
412 perform_math_reg.src_reg_2_index = 0;
413 perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
414 } else {
415 perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
416 }
417 opcode.opcode = perform_math_reg;
418 } break;
419 case CheatVmOpcodeType::StoreRegisterToAddress: {
420 StoreRegisterToAddressOpcode str_register{};
421 // ATSRIOxa (aaaaaaaa)
422 // A = opcode 10
423 // T = bit width
424 // S = src register index
425 // R = address register index
426 // I = 1 if increment address register, 0 if not increment address register
427 // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
428 // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
429 // Relative Address
430 // x = offset register (for offset type 1), memory type (for offset type 3)
431 // a = relative address (for offset type 2+3)
432 str_register.bit_width = (first_dword >> 24) & 0xF;
433 str_register.str_reg_index = ((first_dword >> 20) & 0xF);
434 str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
435 str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
436 str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF));
437 str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
438 switch (str_register.ofs_type) {
439 case StoreRegisterOffsetType::None:
440 case StoreRegisterOffsetType::Reg:
441 // Nothing more to do
442 break;
443 case StoreRegisterOffsetType::Imm:
444 str_register.rel_address =
445 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
446 break;
447 case StoreRegisterOffsetType::MemReg:
448 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
449 break;
450 case StoreRegisterOffsetType::MemImm:
451 case StoreRegisterOffsetType::MemImmReg:
452 str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
453 str_register.rel_address =
454 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
455 break;
456 default:
457 str_register.ofs_type = StoreRegisterOffsetType::None;
458 break;
459 }
460 opcode.opcode = str_register;
461 } break;
462 case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
463 BeginRegisterConditionalOpcode begin_reg_cond{};
464 // C0TcSX##
465 // C0TcS0Ma aaaaaaaa
466 // C0TcS1Mr
467 // C0TcS2Ra aaaaaaaa
468 // C0TcS3Rr
469 // C0TcS400 VVVVVVVV (VVVVVVVV)
470 // C0TcS5X0
471 // C0 = opcode 0xC0
472 // T = bit width
473 // c = condition type.
474 // S = source register.
475 // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
476 // register,
477 // 2 = register with relative offset, 3 = register with offset register, 4 = static
478 // value, 5 = other register.
479 // M = memory type.
480 // R = address register.
481 // a = relative address.
482 // r = offset register.
483 // X = other register.
484 // V = value.
485 begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
486 begin_reg_cond.cond_type =
487 static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
488 begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
489 begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF);
490
491 switch (begin_reg_cond.comp_type) {
492 case CompareRegisterValueType::StaticValue:
493 begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width);
494 break;
495 case CompareRegisterValueType::OtherRegister:
496 begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
497 break;
498 case CompareRegisterValueType::MemoryRelAddr:
499 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
500 begin_reg_cond.rel_address =
501 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
502 break;
503 case CompareRegisterValueType::MemoryOfsReg:
504 begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
505 begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
506 break;
507 case CompareRegisterValueType::RegisterRelAddr:
508 begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
509 begin_reg_cond.rel_address =
510 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
511 break;
512 case CompareRegisterValueType::RegisterOfsReg:
513 begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
514 begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
515 break;
516 }
517 opcode.opcode = begin_reg_cond;
518 } break;
519 case CheatVmOpcodeType::SaveRestoreRegister: {
520 SaveRestoreRegisterOpcode save_restore_reg{};
521 // C10D0Sx0
522 // C1 = opcode 0xC1
523 // D = destination index.
524 // S = source index.
525 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
526 // a register.
527 // NOTE: If we add more save slots later, current encoding is backwards compatible.
528 save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
529 save_restore_reg.src_index = (first_dword >> 8) & 0xF;
530 save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF);
531 opcode.opcode = save_restore_reg;
532 } break;
533 case CheatVmOpcodeType::SaveRestoreRegisterMask: {
534 SaveRestoreRegisterMaskOpcode save_restore_regmask{};
535 // C2x0XXXX
536 // C2 = opcode 0xC2
537 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
538 // X = 16-bit bitmask, bit i --> save or restore register i.
539 save_restore_regmask.op_type =
540 static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF);
541 for (std::size_t i = 0; i < NumRegisters; i++) {
542 save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
543 }
544 opcode.opcode = save_restore_regmask;
545 } break;
546 case CheatVmOpcodeType::DebugLog: {
547 DebugLogOpcode debug_log{};
548 // FFFTIX##
549 // FFFTI0Ma aaaaaaaa
550 // FFFTI1Mr
551 // FFFTI2Ra aaaaaaaa
552 // FFFTI3Rr
553 // FFFTI4X0
554 // FFF = opcode 0xFFF
555 // T = bit width.
556 // I = log id.
557 // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
558 // register,
559 // 2 = register with relative offset, 3 = register with offset register, 4 = register
560 // value.
561 // M = memory type.
562 // R = address register.
563 // a = relative address.
564 // r = offset register.
565 // X = value register.
566 debug_log.bit_width = (first_dword >> 16) & 0xF;
567 debug_log.log_id = ((first_dword >> 12) & 0xF);
568 debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF);
569
570 switch (debug_log.val_type) {
571 case DebugLogValueType::RegisterValue:
572 debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
573 break;
574 case DebugLogValueType::MemoryRelAddr:
575 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
576 debug_log.rel_address =
577 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
578 break;
579 case DebugLogValueType::MemoryOfsReg:
580 debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
581 debug_log.ofs_reg_index = (first_dword & 0xF);
582 break;
583 case DebugLogValueType::RegisterRelAddr:
584 debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
585 debug_log.rel_address =
586 ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
587 break;
588 case DebugLogValueType::RegisterOfsReg:
589 debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
590 debug_log.ofs_reg_index = (first_dword & 0xF);
591 break;
592 }
593 opcode.opcode = debug_log;
594 } break;
595 case CheatVmOpcodeType::ExtendedWidth:
596 case CheatVmOpcodeType::DoubleExtendedWidth:
597 default:
598 // Unrecognized instruction cannot be decoded.
599 valid = false;
600 opcode.opcode = UnrecognizedInstruction{opcode_type};
601 break;
602 }
603
604 // End decoding.
605 return valid;
606}
607
608void DmntCheatVm::SkipConditionalBlock() {
609 if (condition_depth > 0) {
610 // We want to continue until we're out of the current block.
611 const std::size_t desired_depth = condition_depth - 1;
612
613 CheatVmOpcode skip_opcode{};
614 while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) {
615 // Decode instructions until we see end of the current conditional block.
616 // NOTE: This is broken in gateway's implementation.
617 // Gateway currently checks for "0x2" instead of "0x20000000"
618 // In addition, they do a linear scan instead of correctly decoding opcodes.
619 // This causes issues if "0x2" appears as an immediate in the conditional block...
620
621 // We also support nesting of conditional blocks, and Gateway does not.
622 if (skip_opcode.begin_conditional_block) {
623 condition_depth++;
624 } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
625 condition_depth--;
626 }
627 }
628 } else {
629 // Skipping, but condition_depth = 0.
630 // This is an error condition.
631 // However, I don't actually believe it is possible for this to happen.
632 // I guess we'll throw a fatal error here, so as to encourage me to fix the VM
633 // in the event that someone triggers it? I don't know how you'd do that.
634 UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM");
635 }
636}
637
638u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
639 switch (bit_width) {
640 case 1:
641 return value.bit8;
642 case 2:
643 return value.bit16;
644 case 4:
645 return value.bit32;
646 case 8:
647 return value.bit64;
648 default:
649 // Invalid bit width -> return 0.
650 return 0;
651 }
652}
653
654u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
655 MemoryAccessType mem_type, u64 rel_address) {
656 switch (mem_type) {
657 case MemoryAccessType::MainNso:
658 default:
659 return metadata.main_nso_extents.base + rel_address;
660 case MemoryAccessType::Heap:
661 return metadata.heap_extents.base + rel_address;
662 }
663}
664
665void DmntCheatVm::ResetState() {
666 registers.fill(0);
667 saved_values.fill(0);
668 loop_tops.fill(0);
669 instruction_ptr = 0;
670 condition_depth = 0;
671 decode_success = true;
672}
673
674bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
675 // Reset opcode count.
676 num_opcodes = 0;
677
678 for (std::size_t i = 0; i < entries.size(); i++) {
679 if (entries[i].enabled) {
680 // Bounds check.
681 if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) {
682 num_opcodes = 0;
683 return false;
684 }
685
686 for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
687 program[num_opcodes++] = entries[i].definition.opcodes[n];
688 }
689 }
690 }
691
692 return true;
693}
694
695void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
696 CheatVmOpcode cur_opcode{};
697
698 // Get Keys down.
699 u64 kDown = callbacks->HidKeysDown();
700
701 callbacks->CommandLog("Started VM execution.");
702 callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
703 callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
704 callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
705
706 // Clear VM state.
707 ResetState();
708
709 // Loop until program finishes.
710 while (DecodeNextOpcode(cur_opcode)) {
711 callbacks->CommandLog(
712 fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
713
714 for (std::size_t i = 0; i < NumRegisters; i++) {
715 callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
716 }
717
718 for (std::size_t i = 0; i < NumRegisters; i++) {
719 callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
720 }
721 LogOpcode(cur_opcode);
722
723 // Increment conditional depth, if relevant.
724 if (cur_opcode.begin_conditional_block) {
725 condition_depth++;
726 }
727
728 if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) {
729 // Calculate address, write value to memory.
730 u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type,
731 store_static->rel_address +
732 registers[store_static->offset_register]);
733 u64 dst_value = GetVmInt(store_static->value, store_static->bit_width);
734 switch (store_static->bit_width) {
735 case 1:
736 case 2:
737 case 4:
738 case 8:
739 callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
740 break;
741 }
742 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
743 // Read value from memory.
744 u64 src_address =
745 GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
746 u64 src_value = 0;
747 switch (store_static->bit_width) {
748 case 1:
749 case 2:
750 case 4:
751 case 8:
752 callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
753 break;
754 }
755 // Check against condition.
756 u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width);
757 bool cond_met = false;
758 switch (begin_cond->cond_type) {
759 case ConditionalComparisonType::GT:
760 cond_met = src_value > cond_value;
761 break;
762 case ConditionalComparisonType::GE:
763 cond_met = src_value >= cond_value;
764 break;
765 case ConditionalComparisonType::LT:
766 cond_met = src_value < cond_value;
767 break;
768 case ConditionalComparisonType::LE:
769 cond_met = src_value <= cond_value;
770 break;
771 case ConditionalComparisonType::EQ:
772 cond_met = src_value == cond_value;
773 break;
774 case ConditionalComparisonType::NE:
775 cond_met = src_value != cond_value;
776 break;
777 }
778 // Skip conditional block if condition not met.
779 if (!cond_met) {
780 SkipConditionalBlock();
781 }
782 } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
783 // Decrement the condition depth.
784 // We will assume, graciously, that mismatched conditional block ends are a nop.
785 if (condition_depth > 0) {
786 condition_depth--;
787 }
788 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
789 if (ctrl_loop->start_loop) {
790 // Start a loop.
791 registers[ctrl_loop->reg_index] = ctrl_loop->num_iters;
792 loop_tops[ctrl_loop->reg_index] = instruction_ptr;
793 } else {
794 // End a loop.
795 registers[ctrl_loop->reg_index]--;
796 if (registers[ctrl_loop->reg_index] != 0) {
797 instruction_ptr = loop_tops[ctrl_loop->reg_index];
798 }
799 }
800 } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) {
801 // Set a register to a static value.
802 registers[ldr_static->reg_index] = ldr_static->value;
803 } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) {
804 // Choose source address.
805 u64 src_address;
806 if (ldr_memory->load_from_reg) {
807 src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address;
808 } else {
809 src_address =
810 GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address);
811 }
812 // Read into register. Gateway only reads on valid bitwidth.
813 switch (ldr_memory->bit_width) {
814 case 1:
815 case 2:
816 case 4:
817 case 8:
818 callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
819 ldr_memory->bit_width);
820 break;
821 }
822 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
823 // Calculate address.
824 u64 dst_address = registers[str_static->reg_index];
825 u64 dst_value = str_static->value;
826 if (str_static->add_offset_reg) {
827 dst_address += registers[str_static->offset_reg_index];
828 }
829 // Write value to memory. Gateway only writes on valid bitwidth.
830 switch (str_static->bit_width) {
831 case 1:
832 case 2:
833 case 4:
834 case 8:
835 callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
836 break;
837 }
838 // Increment register if relevant.
839 if (str_static->increment_reg) {
840 registers[str_static->reg_index] += str_static->bit_width;
841 }
842 } else if (auto perform_math_static =
843 std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) {
844 // Do requested math.
845 switch (perform_math_static->math_type) {
846 case RegisterArithmeticType::Addition:
847 registers[perform_math_static->reg_index] +=
848 static_cast<u64>(perform_math_static->value);
849 break;
850 case RegisterArithmeticType::Subtraction:
851 registers[perform_math_static->reg_index] -=
852 static_cast<u64>(perform_math_static->value);
853 break;
854 case RegisterArithmeticType::Multiplication:
855 registers[perform_math_static->reg_index] *=
856 static_cast<u64>(perform_math_static->value);
857 break;
858 case RegisterArithmeticType::LeftShift:
859 registers[perform_math_static->reg_index] <<=
860 static_cast<u64>(perform_math_static->value);
861 break;
862 case RegisterArithmeticType::RightShift:
863 registers[perform_math_static->reg_index] >>=
864 static_cast<u64>(perform_math_static->value);
865 break;
866 default:
867 // Do not handle extensions here.
868 break;
869 }
870 // Apply bit width.
871 switch (perform_math_static->bit_width) {
872 case 1:
873 registers[perform_math_static->reg_index] =
874 static_cast<u8>(registers[perform_math_static->reg_index]);
875 break;
876 case 2:
877 registers[perform_math_static->reg_index] =
878 static_cast<u16>(registers[perform_math_static->reg_index]);
879 break;
880 case 4:
881 registers[perform_math_static->reg_index] =
882 static_cast<u32>(registers[perform_math_static->reg_index]);
883 break;
884 case 8:
885 registers[perform_math_static->reg_index] =
886 static_cast<u64>(registers[perform_math_static->reg_index]);
887 break;
888 }
889 } else if (auto begin_keypress_cond =
890 std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) {
891 // Check for keypress.
892 if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
893 // Keys not pressed. Skip conditional block.
894 SkipConditionalBlock();
895 }
896 } else if (auto perform_math_reg =
897 std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
898 const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index];
899 const u64 operand_2_value =
900 perform_math_reg->has_immediate
901 ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width)
902 : registers[perform_math_reg->src_reg_2_index];
903
904 u64 res_val = 0;
905 // Do requested math.
906 switch (perform_math_reg->math_type) {
907 case RegisterArithmeticType::Addition:
908 res_val = operand_1_value + operand_2_value;
909 break;
910 case RegisterArithmeticType::Subtraction:
911 res_val = operand_1_value - operand_2_value;
912 break;
913 case RegisterArithmeticType::Multiplication:
914 res_val = operand_1_value * operand_2_value;
915 break;
916 case RegisterArithmeticType::LeftShift:
917 res_val = operand_1_value << operand_2_value;
918 break;
919 case RegisterArithmeticType::RightShift:
920 res_val = operand_1_value >> operand_2_value;
921 break;
922 case RegisterArithmeticType::LogicalAnd:
923 res_val = operand_1_value & operand_2_value;
924 break;
925 case RegisterArithmeticType::LogicalOr:
926 res_val = operand_1_value | operand_2_value;
927 break;
928 case RegisterArithmeticType::LogicalNot:
929 res_val = ~operand_1_value;
930 break;
931 case RegisterArithmeticType::LogicalXor:
932 res_val = operand_1_value ^ operand_2_value;
933 break;
934 case RegisterArithmeticType::None:
935 res_val = operand_1_value;
936 break;
937 }
938
939 // Apply bit width.
940 switch (perform_math_reg->bit_width) {
941 case 1:
942 res_val = static_cast<u8>(res_val);
943 break;
944 case 2:
945 res_val = static_cast<u16>(res_val);
946 break;
947 case 4:
948 res_val = static_cast<u32>(res_val);
949 break;
950 case 8:
951 res_val = static_cast<u64>(res_val);
952 break;
953 }
954
955 // Save to register.
956 registers[perform_math_reg->dst_reg_index] = res_val;
957 } else if (auto str_register =
958 std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) {
959 // Calculate address.
960 u64 dst_value = registers[str_register->str_reg_index];
961 u64 dst_address = registers[str_register->addr_reg_index];
962 switch (str_register->ofs_type) {
963 case StoreRegisterOffsetType::None:
964 // Nothing more to do
965 break;
966 case StoreRegisterOffsetType::Reg:
967 dst_address += registers[str_register->ofs_reg_index];
968 break;
969 case StoreRegisterOffsetType::Imm:
970 dst_address += str_register->rel_address;
971 break;
972 case StoreRegisterOffsetType::MemReg:
973 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
974 registers[str_register->addr_reg_index]);
975 break;
976 case StoreRegisterOffsetType::MemImm:
977 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
978 str_register->rel_address);
979 break;
980 case StoreRegisterOffsetType::MemImmReg:
981 dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
982 registers[str_register->addr_reg_index] +
983 str_register->rel_address);
984 break;
985 }
986
987 // Write value to memory. Write only on valid bitwidth.
988 switch (str_register->bit_width) {
989 case 1:
990 case 2:
991 case 4:
992 case 8:
993 callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
994 break;
995 }
996
997 // Increment register if relevant.
998 if (str_register->increment_reg) {
999 registers[str_register->addr_reg_index] += str_register->bit_width;
1000 }
1001 } else if (auto begin_reg_cond =
1002 std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) {
1003 // Get value from register.
1004 u64 src_value = 0;
1005 switch (begin_reg_cond->bit_width) {
1006 case 1:
1007 src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul);
1008 break;
1009 case 2:
1010 src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul);
1011 break;
1012 case 4:
1013 src_value =
1014 static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul);
1015 break;
1016 case 8:
1017 src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] &
1018 0xFFFFFFFFFFFFFFFFul);
1019 break;
1020 }
1021
1022 // Read value from memory.
1023 u64 cond_value = 0;
1024 if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) {
1025 cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width);
1026 } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) {
1027 switch (begin_reg_cond->bit_width) {
1028 case 1:
1029 cond_value =
1030 static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul);
1031 break;
1032 case 2:
1033 cond_value =
1034 static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul);
1035 break;
1036 case 4:
1037 cond_value =
1038 static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul);
1039 break;
1040 case 8:
1041 cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] &
1042 0xFFFFFFFFFFFFFFFFul);
1043 break;
1044 }
1045 } else {
1046 u64 cond_address = 0;
1047 switch (begin_reg_cond->comp_type) {
1048 case CompareRegisterValueType::MemoryRelAddr:
1049 cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
1050 begin_reg_cond->rel_address);
1051 break;
1052 case CompareRegisterValueType::MemoryOfsReg:
1053 cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
1054 registers[begin_reg_cond->ofs_reg_index]);
1055 break;
1056 case CompareRegisterValueType::RegisterRelAddr:
1057 cond_address =
1058 registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address;
1059 break;
1060 case CompareRegisterValueType::RegisterOfsReg:
1061 cond_address = registers[begin_reg_cond->addr_reg_index] +
1062 registers[begin_reg_cond->ofs_reg_index];
1063 break;
1064 default:
1065 break;
1066 }
1067 switch (begin_reg_cond->bit_width) {
1068 case 1:
1069 case 2:
1070 case 4:
1071 case 8:
1072 callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
1073 break;
1074 }
1075 }
1076
1077 // Check against condition.
1078 bool cond_met = false;
1079 switch (begin_reg_cond->cond_type) {
1080 case ConditionalComparisonType::GT:
1081 cond_met = src_value > cond_value;
1082 break;
1083 case ConditionalComparisonType::GE:
1084 cond_met = src_value >= cond_value;
1085 break;
1086 case ConditionalComparisonType::LT:
1087 cond_met = src_value < cond_value;
1088 break;
1089 case ConditionalComparisonType::LE:
1090 cond_met = src_value <= cond_value;
1091 break;
1092 case ConditionalComparisonType::EQ:
1093 cond_met = src_value == cond_value;
1094 break;
1095 case ConditionalComparisonType::NE:
1096 cond_met = src_value != cond_value;
1097 break;
1098 }
1099
1100 // Skip conditional block if condition not met.
1101 if (!cond_met) {
1102 SkipConditionalBlock();
1103 }
1104 } else if (auto save_restore_reg =
1105 std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
1106 // Save or restore a register.
1107 switch (save_restore_reg->op_type) {
1108 case SaveRestoreRegisterOpType::ClearRegs:
1109 registers[save_restore_reg->dst_index] = 0ul;
1110 break;
1111 case SaveRestoreRegisterOpType::ClearSaved:
1112 saved_values[save_restore_reg->dst_index] = 0ul;
1113 break;
1114 case SaveRestoreRegisterOpType::Save:
1115 saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index];
1116 break;
1117 case SaveRestoreRegisterOpType::Restore:
1118 default:
1119 registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index];
1120 break;
1121 }
1122 } else if (auto save_restore_regmask =
1123 std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) {
1124 // Save or restore register mask.
1125 u64* src;
1126 u64* dst;
1127 switch (save_restore_regmask->op_type) {
1128 case SaveRestoreRegisterOpType::ClearSaved:
1129 case SaveRestoreRegisterOpType::Save:
1130 src = registers.data();
1131 dst = saved_values.data();
1132 break;
1133 case SaveRestoreRegisterOpType::ClearRegs:
1134 case SaveRestoreRegisterOpType::Restore:
1135 default:
1136 src = registers.data();
1137 dst = saved_values.data();
1138 break;
1139 }
1140 for (std::size_t i = 0; i < NumRegisters; i++) {
1141 if (save_restore_regmask->should_operate[i]) {
1142 switch (save_restore_regmask->op_type) {
1143 case SaveRestoreRegisterOpType::ClearSaved:
1144 case SaveRestoreRegisterOpType::ClearRegs:
1145 dst[i] = 0ul;
1146 break;
1147 case SaveRestoreRegisterOpType::Save:
1148 case SaveRestoreRegisterOpType::Restore:
1149 default:
1150 dst[i] = src[i];
1151 break;
1152 }
1153 }
1154 }
1155 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
1156 // Read value from memory.
1157 u64 log_value = 0;
1158 if (debug_log->val_type == DebugLogValueType::RegisterValue) {
1159 switch (debug_log->bit_width) {
1160 case 1:
1161 log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul);
1162 break;
1163 case 2:
1164 log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul);
1165 break;
1166 case 4:
1167 log_value =
1168 static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul);
1169 break;
1170 case 8:
1171 log_value = static_cast<u64>(registers[debug_log->val_reg_index] &
1172 0xFFFFFFFFFFFFFFFFul);
1173 break;
1174 }
1175 } else {
1176 u64 val_address = 0;
1177 switch (debug_log->val_type) {
1178 case DebugLogValueType::MemoryRelAddr:
1179 val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
1180 debug_log->rel_address);
1181 break;
1182 case DebugLogValueType::MemoryOfsReg:
1183 val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
1184 registers[debug_log->ofs_reg_index]);
1185 break;
1186 case DebugLogValueType::RegisterRelAddr:
1187 val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address;
1188 break;
1189 case DebugLogValueType::RegisterOfsReg:
1190 val_address =
1191 registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index];
1192 break;
1193 default:
1194 break;
1195 }
1196 switch (debug_log->bit_width) {
1197 case 1:
1198 case 2:
1199 case 4:
1200 case 8:
1201 callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
1202 break;
1203 }
1204 }
1205
1206 // Log value.
1207 DebugLog(debug_log->log_id, log_value);
1208 }
1209 }
1210}
1211
1212} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
new file mode 100644
index 000000000..c36212cf1
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -0,0 +1,321 @@
1/*
2 * Copyright (c) 2018-2019 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2019 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#pragma once
26
27#include <variant>
28#include <vector>
29#include <fmt/printf.h>
30#include "common/common_types.h"
31#include "core/memory/dmnt_cheat_types.h"
32
33namespace Memory {
34
35enum class CheatVmOpcodeType : u32 {
36 StoreStatic = 0,
37 BeginConditionalBlock = 1,
38 EndConditionalBlock = 2,
39 ControlLoop = 3,
40 LoadRegisterStatic = 4,
41 LoadRegisterMemory = 5,
42 StoreStaticToAddress = 6,
43 PerformArithmeticStatic = 7,
44 BeginKeypressConditionalBlock = 8,
45
46 // These are not implemented by Gateway's VM.
47 PerformArithmeticRegister = 9,
48 StoreRegisterToAddress = 10,
49 Reserved11 = 11,
50
51 // This is a meta entry, and not a real opcode.
52 // This is to facilitate multi-nybble instruction decoding.
53 ExtendedWidth = 12,
54
55 // Extended width opcodes.
56 BeginRegisterConditionalBlock = 0xC0,
57 SaveRestoreRegister = 0xC1,
58 SaveRestoreRegisterMask = 0xC2,
59
60 // This is a meta entry, and not a real opcode.
61 // This is to facilitate multi-nybble instruction decoding.
62 DoubleExtendedWidth = 0xF0,
63
64 // Double-extended width opcodes.
65 DebugLog = 0xFFF,
66};
67
68enum class MemoryAccessType : u32 {
69 MainNso = 0,
70 Heap = 1,
71};
72
73enum class ConditionalComparisonType : u32 {
74 GT = 1,
75 GE = 2,
76 LT = 3,
77 LE = 4,
78 EQ = 5,
79 NE = 6,
80};
81
82enum class RegisterArithmeticType : u32 {
83 Addition = 0,
84 Subtraction = 1,
85 Multiplication = 2,
86 LeftShift = 3,
87 RightShift = 4,
88
89 // These are not supported by Gateway's VM.
90 LogicalAnd = 5,
91 LogicalOr = 6,
92 LogicalNot = 7,
93 LogicalXor = 8,
94
95 None = 9,
96};
97
98enum class StoreRegisterOffsetType : u32 {
99 None = 0,
100 Reg = 1,
101 Imm = 2,
102 MemReg = 3,
103 MemImm = 4,
104 MemImmReg = 5,
105};
106
107enum class CompareRegisterValueType : u32 {
108 MemoryRelAddr = 0,
109 MemoryOfsReg = 1,
110 RegisterRelAddr = 2,
111 RegisterOfsReg = 3,
112 StaticValue = 4,
113 OtherRegister = 5,
114};
115
116enum class SaveRestoreRegisterOpType : u32 {
117 Restore = 0,
118 Save = 1,
119 ClearSaved = 2,
120 ClearRegs = 3,
121};
122
123enum class DebugLogValueType : u32 {
124 MemoryRelAddr = 0,
125 MemoryOfsReg = 1,
126 RegisterRelAddr = 2,
127 RegisterOfsReg = 3,
128 RegisterValue = 4,
129};
130
131union VmInt {
132 u8 bit8;
133 u16 bit16;
134 u32 bit32;
135 u64 bit64;
136};
137
138struct StoreStaticOpcode {
139 u32 bit_width{};
140 MemoryAccessType mem_type{};
141 u32 offset_register{};
142 u64 rel_address{};
143 VmInt value{};
144};
145
146struct BeginConditionalOpcode {
147 u32 bit_width{};
148 MemoryAccessType mem_type{};
149 ConditionalComparisonType cond_type{};
150 u64 rel_address{};
151 VmInt value{};
152};
153
154struct EndConditionalOpcode {};
155
156struct ControlLoopOpcode {
157 bool start_loop{};
158 u32 reg_index{};
159 u32 num_iters{};
160};
161
162struct LoadRegisterStaticOpcode {
163 u32 reg_index{};
164 u64 value{};
165};
166
167struct LoadRegisterMemoryOpcode {
168 u32 bit_width{};
169 MemoryAccessType mem_type{};
170 u32 reg_index{};
171 bool load_from_reg{};
172 u64 rel_address{};
173};
174
175struct StoreStaticToAddressOpcode {
176 u32 bit_width{};
177 u32 reg_index{};
178 bool increment_reg{};
179 bool add_offset_reg{};
180 u32 offset_reg_index{};
181 u64 value{};
182};
183
184struct PerformArithmeticStaticOpcode {
185 u32 bit_width{};
186 u32 reg_index{};
187 RegisterArithmeticType math_type{};
188 u32 value{};
189};
190
191struct BeginKeypressConditionalOpcode {
192 u32 key_mask{};
193};
194
195struct PerformArithmeticRegisterOpcode {
196 u32 bit_width{};
197 RegisterArithmeticType math_type{};
198 u32 dst_reg_index{};
199 u32 src_reg_1_index{};
200 u32 src_reg_2_index{};
201 bool has_immediate{};
202 VmInt value{};
203};
204
205struct StoreRegisterToAddressOpcode {
206 u32 bit_width{};
207 u32 str_reg_index{};
208 u32 addr_reg_index{};
209 bool increment_reg{};
210 StoreRegisterOffsetType ofs_type{};
211 MemoryAccessType mem_type{};
212 u32 ofs_reg_index{};
213 u64 rel_address{};
214};
215
216struct BeginRegisterConditionalOpcode {
217 u32 bit_width{};
218 ConditionalComparisonType cond_type{};
219 u32 val_reg_index{};
220 CompareRegisterValueType comp_type{};
221 MemoryAccessType mem_type{};
222 u32 addr_reg_index{};
223 u32 other_reg_index{};
224 u32 ofs_reg_index{};
225 u64 rel_address{};
226 VmInt value{};
227};
228
229struct SaveRestoreRegisterOpcode {
230 u32 dst_index{};
231 u32 src_index{};
232 SaveRestoreRegisterOpType op_type{};
233};
234
235struct SaveRestoreRegisterMaskOpcode {
236 SaveRestoreRegisterOpType op_type{};
237 std::array<bool, 0x10> should_operate{};
238};
239
240struct DebugLogOpcode {
241 u32 bit_width{};
242 u32 log_id{};
243 DebugLogValueType val_type{};
244 MemoryAccessType mem_type{};
245 u32 addr_reg_index{};
246 u32 val_reg_index{};
247 u32 ofs_reg_index{};
248 u64 rel_address{};
249};
250
251struct UnrecognizedInstruction {
252 CheatVmOpcodeType opcode{};
253};
254
255struct CheatVmOpcode {
256 bool begin_conditional_block{};
257 std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
258 LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
259 PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
260 PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
261 BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
262 SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction>
263 opcode{};
264};
265
266class DmntCheatVm {
267public:
268 /// Helper Type for DmntCheatVm <=> yuzu Interface
269 class Callbacks {
270 public:
271 virtual ~Callbacks();
272
273 virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
274 virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
275
276 virtual u64 HidKeysDown() = 0;
277
278 virtual void DebugLog(u8 id, u64 value) = 0;
279 virtual void CommandLog(std::string_view data) = 0;
280 };
281
282 static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
283 static constexpr std::size_t NumRegisters = 0x10;
284
285 explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks);
286 ~DmntCheatVm();
287
288 std::size_t GetProgramSize() const {
289 return this->num_opcodes;
290 }
291
292 bool LoadProgram(const std::vector<CheatEntry>& cheats);
293 void Execute(const CheatProcessMetadata& metadata);
294
295private:
296 std::unique_ptr<Callbacks> callbacks;
297
298 std::size_t num_opcodes = 0;
299 std::size_t instruction_ptr = 0;
300 std::size_t condition_depth = 0;
301 bool decode_success = false;
302 std::array<u32, MaximumProgramOpcodeCount> program{};
303 std::array<u64, NumRegisters> registers{};
304 std::array<u64, NumRegisters> saved_values{};
305 std::array<std::size_t, NumRegisters> loop_tops{};
306
307 bool DecodeNextOpcode(CheatVmOpcode& out);
308 void SkipConditionalBlock();
309 void ResetState();
310
311 // For implementing the DebugLog opcode.
312 void DebugLog(u32 log_id, u64 value);
313
314 void LogOpcode(const CheatVmOpcode& opcode);
315
316 static u64 GetVmInt(VmInt value, u32 bit_width);
317 static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
318 MemoryAccessType mem_type, u64 rel_address);
319};
320
321}; // namespace Memory
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 98a8b5337..7ff44f06d 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -29,8 +29,8 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
29} 29}
30 30
31void Fermi2D::HandleSurfaceCopy() { 31void Fermi2D::HandleSurfaceCopy() {
32 LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", 32 LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}",
33 static_cast<u32>(regs.operation)); 33 static_cast<u32>(regs.operation));
34 34
35 // TODO(Subv): Only raw copies are implemented. 35 // TODO(Subv): Only raw copies are implemented.
36 ASSERT(regs.operation == Operation::SrcCopy); 36 ASSERT(regs.operation == Operation::SrcCopy);
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index fb3d1112c..b318aedb8 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -92,6 +92,10 @@ void Maxwell3D::InitializeRegisterDefaults() {
92 92
93 // Some games (like Super Mario Odyssey) assume that SRGB is enabled. 93 // Some games (like Super Mario Odyssey) assume that SRGB is enabled.
94 regs.framebuffer_srgb = 1; 94 regs.framebuffer_srgb = 1;
95 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
96 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
97 mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
98 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
95} 99}
96 100
97#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name)) 101#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
@@ -256,6 +260,9 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
256 260
257 // Execute the current macro. 261 // Execute the current macro.
258 macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); 262 macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters);
263 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
264 FlushMMEInlineDraw();
265 }
259} 266}
260 267
261void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { 268void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
@@ -416,6 +423,97 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
416 } 423 }
417} 424}
418 425
426void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
427 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
428 if (mme_draw.gl_begin_consume) {
429 mme_draw.current_mode = expected_mode;
430 mme_draw.current_count = count;
431 mme_draw.instance_count = 1;
432 mme_draw.gl_begin_consume = false;
433 mme_draw.gl_end_count = 0;
434 }
435 return;
436 } else {
437 if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
438 mme_draw.instance_mode && mme_draw.gl_begin_consume) {
439 mme_draw.instance_count++;
440 mme_draw.gl_begin_consume = false;
441 return;
442 } else {
443 FlushMMEInlineDraw();
444 }
445 }
446 // Tail call in case it needs to retry.
447 StepInstance(expected_mode, count);
448}
449
450void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) {
451 const u32 method = method_call.method;
452 if (mme_inline[method]) {
453 regs.reg_array[method] = method_call.argument;
454 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
455 method == MAXWELL3D_REG_INDEX(index_array.count)) {
456 const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
457 ? MMEDrawMode::Array
458 : MMEDrawMode::Indexed;
459 StepInstance(expected_mode, method_call.argument);
460 } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) {
461 mme_draw.instance_mode =
462 (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0);
463 mme_draw.gl_begin_consume = true;
464 } else {
465 mme_draw.gl_end_count++;
466 }
467 } else {
468 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
469 FlushMMEInlineDraw();
470 }
471 CallMethod(method_call);
472 }
473}
474
475void Maxwell3D::FlushMMEInlineDraw() {
476 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
477 regs.vertex_buffer.count);
478 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
479 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
480
481 auto debug_context = system.GetGPUDebugContext();
482
483 if (debug_context) {
484 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
485 }
486
487 // Both instance configuration registers can not be set at the same time.
488 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
489 "Illegal combination of instancing parameters");
490
491 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
492 if (ShouldExecute()) {
493 rasterizer.DrawMultiBatch(is_indexed);
494 }
495
496 if (debug_context) {
497 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
498 }
499
500 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
501 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
502 // it's possible that it is incorrect and that there is some other register used to specify the
503 // drawing mode.
504 if (is_indexed) {
505 regs.index_array.count = 0;
506 } else {
507 regs.vertex_buffer.count = 0;
508 }
509 mme_draw.current_mode = MMEDrawMode::Undefined;
510 mme_draw.current_count = 0;
511 mme_draw.instance_count = 0;
512 mme_draw.instance_mode = false;
513 mme_draw.gl_begin_consume = false;
514 mme_draw.gl_end_count = 0;
515}
516
419void Maxwell3D::ProcessMacroUpload(u32 data) { 517void Maxwell3D::ProcessMacroUpload(u32 data) {
420 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), 518 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
421 "upload_address exceeded macro_memory size!"); 519 "upload_address exceeded macro_memory size!");
@@ -564,7 +662,9 @@ void Maxwell3D::DrawArrays() {
564 } 662 }
565 663
566 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 664 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
567 rasterizer.AccelerateDrawBatch(is_indexed); 665 if (ShouldExecute()) {
666 rasterizer.DrawBatch(is_indexed);
667 }
568 668
569 if (debug_context) { 669 if (debug_context) {
570 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); 670 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index e5ec90717..4c97759ed 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -811,8 +811,9 @@ public:
811 INSERT_PADDING_WORDS(0x21); 811 INSERT_PADDING_WORDS(0x21);
812 812
813 u32 vb_element_base; 813 u32 vb_element_base;
814 u32 vb_base_instance;
814 815
815 INSERT_PADDING_WORDS(0x36); 816 INSERT_PADDING_WORDS(0x35);
816 817
817 union { 818 union {
818 BitField<0, 1, u32> c0; 819 BitField<0, 1, u32> c0;
@@ -1238,6 +1239,11 @@ public:
1238 /// Write the value to the register identified by method. 1239 /// Write the value to the register identified by method.
1239 void CallMethod(const GPU::MethodCall& method_call); 1240 void CallMethod(const GPU::MethodCall& method_call);
1240 1241
1242 /// Write the value to the register identified by method.
1243 void CallMethodFromMME(const GPU::MethodCall& method_call);
1244
1245 void FlushMMEInlineDraw();
1246
1241 /// Given a Texture Handle, returns the TSC and TIC entries. 1247 /// Given a Texture Handle, returns the TSC and TIC entries.
1242 Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, 1248 Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
1243 std::size_t offset) const; 1249 std::size_t offset) const;
@@ -1263,6 +1269,21 @@ public:
1263 return execute_on; 1269 return execute_on;
1264 } 1270 }
1265 1271
1272 enum class MMEDrawMode : u32 {
1273 Undefined,
1274 Array,
1275 Indexed,
1276 };
1277
1278 struct MMEDrawState {
1279 MMEDrawMode current_mode{MMEDrawMode::Undefined};
1280 u32 current_count{};
1281 u32 instance_count{};
1282 bool instance_mode{};
1283 bool gl_begin_consume{};
1284 u32 gl_end_count{};
1285 } mme_draw;
1286
1266private: 1287private:
1267 void InitializeRegisterDefaults(); 1288 void InitializeRegisterDefaults();
1268 1289
@@ -1275,6 +1296,8 @@ private:
1275 /// Start offsets of each macro in macro_memory 1296 /// Start offsets of each macro in macro_memory
1276 std::array<u32, 0x80> macro_positions = {}; 1297 std::array<u32, 0x80> macro_positions = {};
1277 1298
1299 std::array<bool, Regs::NUM_REGS> mme_inline{};
1300
1278 /// Memory for macro code 1301 /// Memory for macro code
1279 MacroMemory macro_memory; 1302 MacroMemory macro_memory;
1280 1303
@@ -1346,6 +1369,9 @@ private:
1346 1369
1347 /// Handles a write to the VERTEX_END_GL register, triggering a draw. 1370 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
1348 void DrawArrays(); 1371 void DrawArrays();
1372
1373 // Handles a instance drawcall from MME
1374 void StepInstance(MMEDrawMode expected_mode, u32 count);
1349}; 1375};
1350 1376
1351#define ASSERT_REG_POSITION(field_name, position) \ 1377#define ASSERT_REG_POSITION(field_name, position) \
@@ -1402,6 +1428,7 @@ ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1402ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); 1428ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1403ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1429ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1404ASSERT_REG_POSITION(vb_element_base, 0x50D); 1430ASSERT_REG_POSITION(vb_element_base, 0x50D);
1431ASSERT_REG_POSITION(vb_base_instance, 0x50E);
1405ASSERT_REG_POSITION(clip_distance_enabled, 0x544); 1432ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
1406ASSERT_REG_POSITION(point_size, 0x546); 1433ASSERT_REG_POSITION(point_size, 0x546);
1407ASSERT_REG_POSITION(zeta_enable, 0x54E); 1434ASSERT_REG_POSITION(zeta_enable, 0x54E);
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 2c47541cb..76cfe8107 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -122,6 +122,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
122 case RenderTargetFormat::RGBA16_UINT: 122 case RenderTargetFormat::RGBA16_UINT:
123 case RenderTargetFormat::RGBA16_UNORM: 123 case RenderTargetFormat::RGBA16_UNORM:
124 case RenderTargetFormat::RGBA16_FLOAT: 124 case RenderTargetFormat::RGBA16_FLOAT:
125 case RenderTargetFormat::RGBX16_FLOAT:
125 case RenderTargetFormat::RG32_FLOAT: 126 case RenderTargetFormat::RG32_FLOAT:
126 case RenderTargetFormat::RG32_UINT: 127 case RenderTargetFormat::RG32_UINT:
127 return 8; 128 return 8;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 78bc0601a..29fa8e95b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -42,6 +42,7 @@ enum class RenderTargetFormat : u32 {
42 RGBA16_FLOAT = 0xCA, 42 RGBA16_FLOAT = 0xCA,
43 RG32_FLOAT = 0xCB, 43 RG32_FLOAT = 0xCB,
44 RG32_UINT = 0xCD, 44 RG32_UINT = 0xCD,
45 RGBX16_FLOAT = 0xCE,
45 BGRA8_UNORM = 0xCF, 46 BGRA8_UNORM = 0xCF,
46 BGRA8_SRGB = 0xD0, 47 BGRA8_SRGB = 0xD0,
47 RGB10_A2_UNORM = 0xD1, 48 RGB10_A2_UNORM = 0xD1,
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 62afc0d11..dbaeac6db 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -257,7 +257,7 @@ void MacroInterpreter::SetMethodAddress(u32 address) {
257} 257}
258 258
259void MacroInterpreter::Send(u32 value) { 259void MacroInterpreter::Send(u32 value) {
260 maxwell3d.CallMethod({method_address.address, value}); 260 maxwell3d.CallMethodFromMME({method_address.address, value});
261 // Increment the method address by the method increment. 261 // Increment the method address by the method increment.
262 method_address.address.Assign(method_address.address.Value() + 262 method_address.address.Assign(method_address.address.Value() +
263 method_address.increment.Value()); 263 method_address.increment.Value());
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index 084f85e67..ab71870ab 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -83,6 +83,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
83 MortonCopy<true, PixelFormat::RG8U>, 83 MortonCopy<true, PixelFormat::RG8U>,
84 MortonCopy<true, PixelFormat::RG8S>, 84 MortonCopy<true, PixelFormat::RG8S>,
85 MortonCopy<true, PixelFormat::RG32UI>, 85 MortonCopy<true, PixelFormat::RG32UI>,
86 MortonCopy<true, PixelFormat::RGBX16F>,
86 MortonCopy<true, PixelFormat::R32UI>, 87 MortonCopy<true, PixelFormat::R32UI>,
87 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 88 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
88 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 89 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
@@ -151,6 +152,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
151 MortonCopy<false, PixelFormat::RG8U>, 152 MortonCopy<false, PixelFormat::RG8U>,
152 MortonCopy<false, PixelFormat::RG8S>, 153 MortonCopy<false, PixelFormat::RG8S>,
153 MortonCopy<false, PixelFormat::RG32UI>, 154 MortonCopy<false, PixelFormat::RG32UI>,
155 MortonCopy<false, PixelFormat::RGBX16F>,
154 MortonCopy<false, PixelFormat::R32UI>, 156 MortonCopy<false, PixelFormat::R32UI>,
155 nullptr, 157 nullptr,
156 nullptr, 158 nullptr,
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 6b3f2d50a..5b0eca9e2 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -29,7 +29,10 @@ public:
29 virtual ~RasterizerInterface() {} 29 virtual ~RasterizerInterface() {}
30 30
31 /// Draw the current batch of vertex arrays 31 /// Draw the current batch of vertex arrays
32 virtual void DrawArrays() = 0; 32 virtual bool DrawBatch(bool is_indexed) = 0;
33
34 /// Draw the current batch of multiple instances of vertex arrays
35 virtual bool DrawMultiBatch(bool is_indexed) = 0;
33 36
34 /// Clear the current framebuffer 37 /// Clear the current framebuffer
35 virtual void Clear() = 0; 38 virtual void Clear() = 0;
@@ -69,10 +72,6 @@ public:
69 return false; 72 return false;
70 } 73 }
71 74
72 virtual bool AccelerateDrawBatch(bool is_indexed) {
73 return false;
74 }
75
76 /// Increase/decrease the number of object in pages touching the specified region 75 /// Increase/decrease the number of object in pages touching the specified region
77 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} 76 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
78 77
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
index 7c926bd48..a5d69d78d 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
@@ -35,21 +35,16 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
35 local_state.draw.draw_framebuffer = framebuffer.handle; 35 local_state.draw.draw_framebuffer = framebuffer.handle;
36 local_state.ApplyFramebufferState(); 36 local_state.ApplyFramebufferState();
37 37
38 if (key.is_single_buffer) { 38 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
39 if (key.color_attachments[0] != GL_NONE && key.colors[0]) { 39 if (key.colors[index]) {
40 key.colors[0]->Attach(key.color_attachments[0], GL_DRAW_FRAMEBUFFER); 40 key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
41 glDrawBuffer(key.color_attachments[0]); 41 GL_DRAW_FRAMEBUFFER);
42 } else {
43 glDrawBuffer(GL_NONE);
44 }
45 } else {
46 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
47 if (key.colors[index]) {
48 key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
49 GL_DRAW_FRAMEBUFFER);
50 }
51 } 42 }
43 }
44 if (key.colors_count) {
52 glDrawBuffers(key.colors_count, key.color_attachments.data()); 45 glDrawBuffers(key.colors_count, key.color_attachments.data());
46 } else {
47 glDrawBuffer(GL_NONE);
53 } 48 }
54 49
55 if (key.zeta) { 50 if (key.zeta) {
@@ -67,9 +62,9 @@ std::size_t FramebufferCacheKey::Hash() const {
67} 62}
68 63
69bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const { 64bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const {
70 return std::tie(is_single_buffer, stencil_enable, colors_count, color_attachments, colors, 65 return std::tie(stencil_enable, colors_count, color_attachments, colors, zeta) ==
71 zeta) == std::tie(rhs.is_single_buffer, rhs.stencil_enable, rhs.colors_count, 66 std::tie(rhs.stencil_enable, rhs.colors_count, rhs.color_attachments, rhs.colors,
72 rhs.color_attachments, rhs.colors, rhs.zeta); 67 rhs.zeta);
73} 68}
74 69
75} // namespace OpenGL 70} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.h b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
index a3a996353..424344c48 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
@@ -19,7 +19,6 @@
19namespace OpenGL { 19namespace OpenGL {
20 20
21struct alignas(sizeof(u64)) FramebufferCacheKey { 21struct alignas(sizeof(u64)) FramebufferCacheKey {
22 bool is_single_buffer = false;
23 bool stencil_enable = false; 22 bool stencil_enable = false;
24 u16 colors_count = 0; 23 u16 colors_count = 0;
25 24
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4dd08bccb..6a17bed72 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -49,40 +49,6 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
49MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 49MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
50MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); 50MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100));
51 51
52struct DrawParameters {
53 GLenum primitive_mode;
54 GLsizei count;
55 GLint current_instance;
56 bool use_indexed;
57
58 GLint vertex_first;
59
60 GLenum index_format;
61 GLint base_vertex;
62 GLintptr index_buffer_offset;
63
64 void DispatchDraw() const {
65 if (use_indexed) {
66 const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
67 if (current_instance > 0) {
68 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
69 index_buffer_ptr, 1, base_vertex,
70 current_instance);
71 } else {
72 glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
73 base_vertex);
74 }
75 } else {
76 if (current_instance > 0) {
77 glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1,
78 current_instance);
79 } else {
80 glDrawArrays(primitive_mode, vertex_first, count);
81 }
82 }
83 }
84};
85
86static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, 52static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
87 const GLShader::ConstBufferEntry& entry) { 53 const GLShader::ConstBufferEntry& entry) {
88 if (!entry.IsIndirect()) { 54 if (!entry.IsIndirect()) {
@@ -270,29 +236,6 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
270 return offset; 236 return offset;
271} 237}
272 238
273DrawParameters RasterizerOpenGL::SetupDraw(GLintptr index_buffer_offset) {
274 const auto& gpu = system.GPU().Maxwell3D();
275 const auto& regs = gpu.regs;
276 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
277
278 DrawParameters params{};
279 params.current_instance = gpu.state.current_instance;
280
281 params.use_indexed = is_indexed;
282 params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
283
284 if (is_indexed) {
285 params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
286 params.count = regs.index_array.count;
287 params.index_buffer_offset = index_buffer_offset;
288 params.base_vertex = static_cast<GLint>(regs.vb_element_base);
289 } else {
290 params.count = regs.vertex_buffer.count;
291 params.vertex_first = regs.vertex_buffer.first;
292 }
293 return params;
294}
295
296void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 239void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
297 MICROPROFILE_SCOPE(OpenGL_Shader); 240 MICROPROFILE_SCOPE(OpenGL_Shader);
298 auto& gpu = system.GPU().Maxwell3D(); 241 auto& gpu = system.GPU().Maxwell3D();
@@ -399,12 +342,6 @@ std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
399 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); 342 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
400} 343}
401 344
402bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
403 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
404 DrawArrays();
405 return true;
406}
407
408template <typename Map, typename Interval> 345template <typename Map, typename Interval>
409static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { 346static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
410 return boost::make_iterator_range(map.equal_range(interval)); 347 return boost::make_iterator_range(map.equal_range(interval));
@@ -445,99 +382,51 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
445 shader_cache.LoadDiskCache(stop_loading, callback); 382 shader_cache.LoadDiskCache(stop_loading, callback);
446} 383}
447 384
448std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( 385void RasterizerOpenGL::ConfigureFramebuffers() {
449 OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
450 std::optional<std::size_t> single_color_target) {
451 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 386 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
452 auto& gpu = system.GPU().Maxwell3D(); 387 auto& gpu = system.GPU().Maxwell3D();
453 const auto& regs = gpu.regs; 388 if (!gpu.dirty.render_settings) {
454 389 return;
455 const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
456 single_color_target};
457 if (fb_config_state == current_framebuffer_config_state && !gpu.dirty.render_settings) {
458 // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
459 // single color targets). This is done because the guest registers may not change but the
460 // host framebuffer may contain different attachments
461 return current_depth_stencil_usage;
462 } 390 }
463 gpu.dirty.render_settings = false; 391 gpu.dirty.render_settings = false;
464 current_framebuffer_config_state = fb_config_state;
465 392
466 texture_cache.GuardRenderTargets(true); 393 texture_cache.GuardRenderTargets(true);
467 394
468 View depth_surface{}; 395 View depth_surface = texture_cache.GetDepthBufferSurface(true);
469 if (using_depth_fb) {
470 depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents);
471 } else {
472 texture_cache.SetEmptyDepthBuffer();
473 }
474 396
397 const auto& regs = gpu.regs;
398 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
475 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); 399 UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
476 400
477 // Bind the framebuffer surfaces 401 // Bind the framebuffer surfaces
478 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
479
480 FramebufferCacheKey fbkey; 402 FramebufferCacheKey fbkey;
403 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
404 View color_surface{texture_cache.GetColorBufferSurface(index, true)};
481 405
482 if (using_color_fb) { 406 if (color_surface) {
483 if (single_color_target) { 407 // Assume that a surface will be written to if it is used as a framebuffer, even
484 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 408 // if the shader doesn't actually write to it.
485 View color_surface{ 409 texture_cache.MarkColorBufferInUse(index);
486 texture_cache.GetColorBufferSurface(*single_color_target, preserve_contents)};
487
488 if (color_surface) {
489 // Assume that a surface will be written to if it is used as a framebuffer, even if
490 // the shader doesn't actually write to it.
491 texture_cache.MarkColorBufferInUse(*single_color_target);
492 }
493
494 fbkey.is_single_buffer = true;
495 fbkey.color_attachments[0] =
496 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target);
497 fbkey.colors[0] = color_surface;
498 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
499 if (index != *single_color_target) {
500 texture_cache.SetEmptyColorBuffer(index);
501 }
502 }
503 } else {
504 // Multiple color attachments are enabled
505 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
506 View color_surface{texture_cache.GetColorBufferSurface(index, preserve_contents)};
507
508 if (color_surface) {
509 // Assume that a surface will be written to if it is used as a framebuffer, even
510 // if the shader doesn't actually write to it.
511 texture_cache.MarkColorBufferInUse(index);
512 }
513
514 fbkey.color_attachments[index] =
515 GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
516 fbkey.colors[index] = color_surface;
517 }
518 fbkey.is_single_buffer = false;
519 fbkey.colors_count = regs.rt_control.count;
520 } 410 }
521 } else { 411
522 // No color attachments are enabled - leave them as zero 412 fbkey.color_attachments[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
523 fbkey.is_single_buffer = true; 413 fbkey.colors[index] = std::move(color_surface);
524 } 414 }
415 fbkey.colors_count = regs.rt_control.count;
525 416
526 if (depth_surface) { 417 if (depth_surface) {
527 // Assume that a surface will be written to if it is used as a framebuffer, even if 418 // Assume that a surface will be written to if it is used as a framebuffer, even if
528 // the shader doesn't actually write to it. 419 // the shader doesn't actually write to it.
529 texture_cache.MarkDepthBufferInUse(); 420 texture_cache.MarkDepthBufferInUse();
530 421
531 fbkey.zeta = depth_surface;
532 fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil; 422 fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
423 fbkey.zeta = std::move(depth_surface);
533 } 424 }
534 425
535 texture_cache.GuardRenderTargets(false); 426 texture_cache.GuardRenderTargets(false);
536 427
537 current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey); 428 state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey);
538 SyncViewport(current_state); 429 SyncViewport(state);
539
540 return current_depth_stencil_usage = {static_cast<bool>(depth_surface), fbkey.stencil_enable};
541} 430}
542 431
543void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, 432void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
@@ -688,17 +577,9 @@ void RasterizerOpenGL::Clear() {
688 } 577 }
689} 578}
690 579
691void RasterizerOpenGL::DrawArrays() { 580void RasterizerOpenGL::DrawPrelude() {
692 if (accelerate_draw == AccelDraw::Disabled)
693 return;
694
695 MICROPROFILE_SCOPE(OpenGL_Drawing);
696 auto& gpu = system.GPU().Maxwell3D(); 581 auto& gpu = system.GPU().Maxwell3D();
697 582
698 if (!gpu.ShouldExecute()) {
699 return;
700 }
701
702 SyncColorMask(); 583 SyncColorMask();
703 SyncFragmentColorClampState(); 584 SyncFragmentColorClampState();
704 SyncMultiSampleState(); 585 SyncMultiSampleState();
@@ -743,10 +624,7 @@ void RasterizerOpenGL::DrawArrays() {
743 // Upload vertex and index data. 624 // Upload vertex and index data.
744 SetupVertexBuffer(vao); 625 SetupVertexBuffer(vao);
745 SetupVertexInstances(vao); 626 SetupVertexInstances(vao);
746 const GLintptr index_buffer_offset = SetupIndexBuffer(); 627 index_buffer_offset = SetupIndexBuffer();
747
748 // Setup draw parameters. It will automatically choose what glDraw* method to use.
749 const DrawParameters params = SetupDraw(index_buffer_offset);
750 628
751 // Prepare packed bindings. 629 // Prepare packed bindings.
752 bind_ubo_pushbuffer.Setup(0); 630 bind_ubo_pushbuffer.Setup(0);
@@ -754,10 +632,11 @@ void RasterizerOpenGL::DrawArrays() {
754 632
755 // Setup shaders and their used resources. 633 // Setup shaders and their used resources.
756 texture_cache.GuardSamplers(true); 634 texture_cache.GuardSamplers(true);
757 SetupShaders(params.primitive_mode); 635 const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
636 SetupShaders(primitive_mode);
758 texture_cache.GuardSamplers(false); 637 texture_cache.GuardSamplers(false);
759 638
760 ConfigureFramebuffers(state); 639 ConfigureFramebuffers();
761 640
762 // Signal the buffer cache that we are not going to upload more things. 641 // Signal the buffer cache that we are not going to upload more things.
763 const bool invalidate = buffer_cache.Unmap(); 642 const bool invalidate = buffer_cache.Unmap();
@@ -778,11 +657,107 @@ void RasterizerOpenGL::DrawArrays() {
778 if (texture_cache.TextureBarrier()) { 657 if (texture_cache.TextureBarrier()) {
779 glTextureBarrier(); 658 glTextureBarrier();
780 } 659 }
660}
661
662struct DrawParams {
663 bool is_indexed{};
664 bool is_instanced{};
665 GLenum primitive_mode{};
666 GLint count{};
667 GLint base_vertex{};
668
669 // Indexed settings
670 GLenum index_format{};
671 GLintptr index_buffer_offset{};
672
673 // Instanced setting
674 GLint num_instances{};
675 GLint base_instance{};
676
677 void DispatchDraw() {
678 if (is_indexed) {
679 const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
680 if (is_instanced) {
681 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
682 index_buffer_ptr, num_instances,
683 base_vertex, base_instance);
684 } else {
685 glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
686 base_vertex);
687 }
688 } else {
689 if (is_instanced) {
690 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
691 base_instance);
692 } else {
693 glDrawArrays(primitive_mode, base_vertex, count);
694 }
695 }
696 }
697};
781 698
782 params.DispatchDraw(); 699bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
700 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
783 701
702 MICROPROFILE_SCOPE(OpenGL_Drawing);
703
704 DrawPrelude();
705
706 auto& maxwell3d = system.GPU().Maxwell3D();
707 const auto& regs = maxwell3d.regs;
708 const auto current_instance = maxwell3d.state.current_instance;
709 DrawParams draw_call{};
710 draw_call.is_indexed = is_indexed;
711 draw_call.num_instances = static_cast<GLint>(1);
712 draw_call.base_instance = static_cast<GLint>(current_instance);
713 draw_call.is_instanced = current_instance > 0;
714 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
715 if (draw_call.is_indexed) {
716 draw_call.count = static_cast<GLint>(regs.index_array.count);
717 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
718 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
719 draw_call.index_buffer_offset = index_buffer_offset;
720 } else {
721 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
722 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
723 }
724 draw_call.DispatchDraw();
725
726 maxwell3d.dirty.memory_general = false;
784 accelerate_draw = AccelDraw::Disabled; 727 accelerate_draw = AccelDraw::Disabled;
785 gpu.dirty.memory_general = false; 728 return true;
729}
730
731bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
732 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
733
734 MICROPROFILE_SCOPE(OpenGL_Drawing);
735
736 DrawPrelude();
737
738 auto& maxwell3d = system.GPU().Maxwell3D();
739 const auto& regs = maxwell3d.regs;
740 const auto& draw_setup = maxwell3d.mme_draw;
741 DrawParams draw_call{};
742 draw_call.is_indexed = is_indexed;
743 draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
744 draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
745 draw_call.is_instanced = draw_setup.instance_count > 1;
746 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
747 if (draw_call.is_indexed) {
748 draw_call.count = static_cast<GLint>(regs.index_array.count);
749 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
750 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
751 draw_call.index_buffer_offset = index_buffer_offset;
752 } else {
753 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
754 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
755 }
756 draw_call.DispatchDraw();
757
758 maxwell3d.dirty.memory_general = false;
759 accelerate_draw = AccelDraw::Disabled;
760 return true;
786} 761}
787 762
788void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { 763void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index eada752e0..9c10ebda3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -57,7 +57,8 @@ public:
57 ScreenInfo& info); 57 ScreenInfo& info);
58 ~RasterizerOpenGL() override; 58 ~RasterizerOpenGL() override;
59 59
60 void DrawArrays() override; 60 bool DrawBatch(bool is_indexed) override;
61 bool DrawMultiBatch(bool is_indexed) override;
61 void Clear() override; 62 void Clear() override;
62 void DispatchCompute(GPUVAddr code_addr) override; 63 void DispatchCompute(GPUVAddr code_addr) override;
63 void FlushAll() override; 64 void FlushAll() override;
@@ -71,45 +72,13 @@ public:
71 const Tegra::Engines::Fermi2D::Config& copy_config) override; 72 const Tegra::Engines::Fermi2D::Config& copy_config) override;
72 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 73 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
73 u32 pixel_stride) override; 74 u32 pixel_stride) override;
74 bool AccelerateDrawBatch(bool is_indexed) override;
75 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; 75 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
76 void LoadDiskResources(const std::atomic_bool& stop_loading, 76 void LoadDiskResources(const std::atomic_bool& stop_loading,
77 const VideoCore::DiskResourceLoadCallback& callback) override; 77 const VideoCore::DiskResourceLoadCallback& callback) override;
78 78
79private: 79private:
80 struct FramebufferConfigState { 80 /// Configures the color and depth framebuffer states.
81 bool using_color_fb{}; 81 void ConfigureFramebuffers();
82 bool using_depth_fb{};
83 bool preserve_contents{};
84 std::optional<std::size_t> single_color_target;
85
86 bool operator==(const FramebufferConfigState& rhs) const {
87 return std::tie(using_color_fb, using_depth_fb, preserve_contents,
88 single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb,
89 rhs.preserve_contents,
90 rhs.single_color_target);
91 }
92 bool operator!=(const FramebufferConfigState& rhs) const {
93 return !operator==(rhs);
94 }
95 };
96
97 /**
98 * Configures the color and depth framebuffer states.
99 *
100 * @param current_state The current OpenGL state.
101 * @param using_color_fb If true, configure color framebuffers.
102 * @param using_depth_fb If true, configure the depth/stencil framebuffer.
103 * @param preserve_contents If true, tries to preserve data from a previously used
104 * framebuffer.
105 * @param single_color_target Specifies if a single color buffer target should be used.
106 *
107 * @returns If depth (first) or stencil (second) are being stored in the bound zeta texture
108 * (requires using_depth_fb to be true)
109 */
110 std::pair<bool, bool> ConfigureFramebuffers(
111 OpenGLState& current_state, bool using_color_fb = true, bool using_depth_fb = true,
112 bool preserve_contents = true, std::optional<std::size_t> single_color_target = {});
113 82
114 void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, 83 void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
115 bool using_depth_fb, bool using_stencil_fb); 84 bool using_depth_fb, bool using_stencil_fb);
@@ -136,6 +105,9 @@ private:
136 void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, 105 void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
137 std::size_t size); 106 std::size_t size);
138 107
108 /// Syncs all the state, shaders, render targets and textures setting before a draw call.
109 void DrawPrelude();
110
139 /// Configures the current textures to use for the draw command. Returns shaders texture buffer 111 /// Configures the current textures to use for the draw command. Returns shaders texture buffer
140 /// usage. 112 /// usage.
141 TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 113 TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
@@ -228,9 +200,6 @@ private:
228 OGLVertexArray> 200 OGLVertexArray>
229 vertex_array_cache; 201 vertex_array_cache;
230 202
231 FramebufferConfigState current_framebuffer_config_state;
232 std::pair<bool, bool> current_depth_stencil_usage{};
233
234 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; 203 static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
235 OGLBufferCache buffer_cache; 204 OGLBufferCache buffer_cache;
236 205
@@ -250,7 +219,7 @@ private:
250 219
251 GLintptr SetupIndexBuffer(); 220 GLintptr SetupIndexBuffer();
252 221
253 DrawParameters SetupDraw(GLintptr index_buffer_offset); 222 GLintptr index_buffer_offset;
254 223
255 void SetupShaders(GLenum primitive_mode); 224 void SetupShaders(GLenum primitive_mode);
256 225
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 76439e7ab..74cb59bc1 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -462,6 +462,14 @@ private:
462 code.AddLine("float gl_PointSize;"); 462 code.AddLine("float gl_PointSize;");
463 } 463 }
464 464
465 if (ir.UsesInstanceId()) {
466 code.AddLine("int gl_InstanceID;");
467 }
468
469 if (ir.UsesVertexId()) {
470 code.AddLine("int gl_VertexID;");
471 }
472
465 --code.scope; 473 --code.scope;
466 code.AddLine("}};"); 474 code.AddLine("}};");
467 code.AddNewLine(); 475 code.AddNewLine();
@@ -964,7 +972,7 @@ private:
964 switch (element) { 972 switch (element) {
965 case 2: 973 case 2:
966 // Config pack's first value is instance_id. 974 // Config pack's first value is instance_id.
967 return {"config_pack[0]", Type::Uint}; 975 return {"gl_InstanceID", Type::Int};
968 case 3: 976 case 3:
969 return {"gl_VertexID", Type::Int}; 977 return {"gl_VertexID", Type::Int};
970 } 978 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 4f135fe03..173b76c4e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -97,6 +97,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format
97 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U 97 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
98 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 98 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
99 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 99 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
100 {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBX16F
100 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 101 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
101 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 102 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
102 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 103 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 0bbbf6851..3c5acda3e 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -143,6 +143,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
143 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U 143 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U
144 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S 144 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S
145 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI 145 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI
146 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RGBX16F
146 {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI 147 {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI
147 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8 148 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8
148 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5 149 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index bbbab0bca..2c357f310 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -114,6 +114,18 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
114 break; 114 break;
115 } 115 }
116 } 116 }
117 if (index == Attribute::Index::TessCoordInstanceIDVertexID) {
118 switch (element) {
119 case 2:
120 uses_instance_id = true;
121 break;
122 case 3:
123 uses_vertex_id = true;
124 break;
125 default:
126 break;
127 }
128 }
117 if (index == Attribute::Index::ClipDistances0123 || 129 if (index == Attribute::Index::ClipDistances0123 ||
118 index == Attribute::Index::ClipDistances4567) { 130 index == Attribute::Index::ClipDistances4567) {
119 const auto clip_index = 131 const auto clip_index =
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 6aed9bb84..2f03d83ba 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -124,6 +124,14 @@ public:
124 return uses_point_size; 124 return uses_point_size;
125 } 125 }
126 126
127 bool UsesInstanceId() const {
128 return uses_instance_id;
129 }
130
131 bool UsesVertexId() const {
132 return uses_vertex_id;
133 }
134
127 bool HasPhysicalAttributes() const { 135 bool HasPhysicalAttributes() const {
128 return uses_physical_attributes; 136 return uses_physical_attributes;
129 } 137 }
@@ -373,6 +381,8 @@ private:
373 bool uses_viewport_index{}; 381 bool uses_viewport_index{};
374 bool uses_point_size{}; 382 bool uses_point_size{};
375 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes 383 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
384 bool uses_instance_id{};
385 bool uses_vertex_id{};
376 386
377 Tegra::Shader::Header header; 387 Tegra::Shader::Header header;
378}; 388};
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 53d0142cb..250afc6d6 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -159,6 +159,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
159 return PixelFormat::R32UI; 159 return PixelFormat::R32UI;
160 case Tegra::RenderTargetFormat::RG32_UINT: 160 case Tegra::RenderTargetFormat::RG32_UINT:
161 return PixelFormat::RG32UI; 161 return PixelFormat::RG32UI;
162 case Tegra::RenderTargetFormat::RGBX16_FLOAT:
163 return PixelFormat::RGBX16F;
162 default: 164 default:
163 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 165 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
164 UNREACHABLE(); 166 UNREACHABLE();
@@ -415,6 +417,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
415 case Tegra::RenderTargetFormat::RG8_SNORM: 417 case Tegra::RenderTargetFormat::RG8_SNORM:
416 return ComponentType::SNorm; 418 return ComponentType::SNorm;
417 case Tegra::RenderTargetFormat::RGBA16_FLOAT: 419 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
420 case Tegra::RenderTargetFormat::RGBX16_FLOAT:
418 case Tegra::RenderTargetFormat::R11G11B10_FLOAT: 421 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
419 case Tegra::RenderTargetFormat::RGBA32_FLOAT: 422 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
420 case Tegra::RenderTargetFormat::RG32_FLOAT: 423 case Tegra::RenderTargetFormat::RG32_FLOAT:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 19268b7cd..1e1c432a5 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -57,36 +57,37 @@ enum class PixelFormat {
57 RG8U = 39, 57 RG8U = 39,
58 RG8S = 40, 58 RG8S = 40,
59 RG32UI = 41, 59 RG32UI = 41,
60 R32UI = 42, 60 RGBX16F = 42,
61 ASTC_2D_8X8 = 43, 61 R32UI = 43,
62 ASTC_2D_8X5 = 44, 62 ASTC_2D_8X8 = 44,
63 ASTC_2D_5X4 = 45, 63 ASTC_2D_8X5 = 45,
64 BGRA8_SRGB = 46, 64 ASTC_2D_5X4 = 46,
65 DXT1_SRGB = 47, 65 BGRA8_SRGB = 47,
66 DXT23_SRGB = 48, 66 DXT1_SRGB = 48,
67 DXT45_SRGB = 49, 67 DXT23_SRGB = 49,
68 BC7U_SRGB = 50, 68 DXT45_SRGB = 50,
69 ASTC_2D_4X4_SRGB = 51, 69 BC7U_SRGB = 51,
70 ASTC_2D_8X8_SRGB = 52, 70 ASTC_2D_4X4_SRGB = 52,
71 ASTC_2D_8X5_SRGB = 53, 71 ASTC_2D_8X8_SRGB = 53,
72 ASTC_2D_5X4_SRGB = 54, 72 ASTC_2D_8X5_SRGB = 54,
73 ASTC_2D_5X5 = 55, 73 ASTC_2D_5X4_SRGB = 55,
74 ASTC_2D_5X5_SRGB = 56, 74 ASTC_2D_5X5 = 56,
75 ASTC_2D_10X8 = 57, 75 ASTC_2D_5X5_SRGB = 57,
76 ASTC_2D_10X8_SRGB = 58, 76 ASTC_2D_10X8 = 58,
77 ASTC_2D_10X8_SRGB = 59,
77 78
78 MaxColorFormat, 79 MaxColorFormat,
79 80
80 // Depth formats 81 // Depth formats
81 Z32F = 59, 82 Z32F = 60,
82 Z16 = 60, 83 Z16 = 61,
83 84
84 MaxDepthFormat, 85 MaxDepthFormat,
85 86
86 // DepthStencil formats 87 // DepthStencil formats
87 Z24S8 = 61, 88 Z24S8 = 62,
88 S8Z24 = 62, 89 S8Z24 = 63,
89 Z32FS8 = 63, 90 Z32FS8 = 64,
90 91
91 MaxDepthStencilFormat, 92 MaxDepthStencilFormat,
92 93
@@ -166,6 +167,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
166 0, // RG8U 167 0, // RG8U
167 0, // RG8S 168 0, // RG8S
168 0, // RG32UI 169 0, // RG32UI
170 0, // RGBX16F
169 0, // R32UI 171 0, // R32UI
170 2, // ASTC_2D_8X8 172 2, // ASTC_2D_8X8
171 2, // ASTC_2D_8X5 173 2, // ASTC_2D_8X5
@@ -249,6 +251,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
249 1, // RG8U 251 1, // RG8U
250 1, // RG8S 252 1, // RG8S
251 1, // RG32UI 253 1, // RG32UI
254 1, // RGBX16F
252 1, // R32UI 255 1, // R32UI
253 8, // ASTC_2D_8X8 256 8, // ASTC_2D_8X8
254 8, // ASTC_2D_8X5 257 8, // ASTC_2D_8X5
@@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
324 1, // RG8U 327 1, // RG8U
325 1, // RG8S 328 1, // RG8S
326 1, // RG32UI 329 1, // RG32UI
330 1, // RGBX16F
327 1, // R32UI 331 1, // R32UI
328 8, // ASTC_2D_8X8 332 8, // ASTC_2D_8X8
329 5, // ASTC_2D_8X5 333 5, // ASTC_2D_8X5
@@ -399,6 +403,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
399 16, // RG8U 403 16, // RG8U
400 16, // RG8S 404 16, // RG8S
401 64, // RG32UI 405 64, // RG32UI
406 64, // RGBX16F
402 32, // R32UI 407 32, // R32UI
403 128, // ASTC_2D_8X8 408 128, // ASTC_2D_8X8
404 128, // ASTC_2D_8X5 409 128, // ASTC_2D_8X5
@@ -489,6 +494,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
489 SurfaceCompression::None, // RG8U 494 SurfaceCompression::None, // RG8U
490 SurfaceCompression::None, // RG8S 495 SurfaceCompression::None, // RG8S
491 SurfaceCompression::None, // RG32UI 496 SurfaceCompression::None, // RG32UI
497 SurfaceCompression::None, // RGBX16F
492 SurfaceCompression::None, // R32UI 498 SurfaceCompression::None, // R32UI
493 SurfaceCompression::Converted, // ASTC_2D_8X8 499 SurfaceCompression::Converted, // ASTC_2D_8X8
494 SurfaceCompression::Converted, // ASTC_2D_8X5 500 SurfaceCompression::Converted, // ASTC_2D_8X5
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index b49446be9..98bc9b391 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QCheckBox>
5#include <QSpinBox> 6#include <QSpinBox>
6#include "core/core.h" 7#include "core/core.h"
7#include "core/settings.h" 8#include "core/settings.h"