summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/templates/build-mock.yml5
-rw-r--r--.ci/templates/build-single.yml15
-rw-r--r--.ci/templates/release-download.yml13
-rw-r--r--.ci/templates/release-github.yml11
-rw-r--r--.ci/templates/release-universal.yml10
-rw-r--r--.ci/yuzu-mainline-step1.yml8
-rw-r--r--.ci/yuzu-mainline-step2.yml (renamed from .ci/yuzu-mainline.yml)15
-rw-r--r--README.md6
m---------externals/dynarmic0
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/audio_renderer.h25
-rw-r--r--src/common/CMakeLists.txt3
-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.cpp85
-rw-r--r--src/core/core.h22
-rw-r--r--src/core/crypto/partition_data_manager.cpp4
-rw-r--r--src/core/crypto/partition_data_manager.h1
-rw-r--r--src/core/file_sys/bis_factory.cpp101
-rw-r--r--src/core/file_sys/bis_factory.h38
-rw-r--r--src/core/file_sys/card_image.cpp24
-rw-r--r--src/core/file_sys/card_image.h9
-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/content_archive.cpp8
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp69
-rw-r--r--src/core/file_sys/patch_manager.h6
-rw-r--r--src/core/file_sys/registered_cache.cpp188
-rw-r--r--src/core/file_sys/registered_cache.h25
-rw-r--r--src/core/file_sys/romfs_factory.cpp16
-rw-r--r--src/core/file_sys/romfs_factory.h4
-rw-r--r--src/core/file_sys/savedata_factory.cpp68
-rw-r--r--src/core/file_sys/savedata_factory.h6
-rw-r--r--src/core/file_sys/sdmc_factory.cpp27
-rw-r--r--src/core/file_sys/sdmc_factory.h13
-rw-r--r--src/core/file_sys/submission_package.cpp29
-rw-r--r--src/core/hle/service/acc/acc.cpp14
-rw-r--r--src/core/hle/service/am/am.cpp57
-rw-r--r--src/core/hle/service/am/am.h5
-rw-r--r--src/core/hle/service/am/applet_ae.h6
-rw-r--r--src/core/hle/service/am/applet_oe.h6
-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/filesystem/filesystem.cpp329
-rw-r--r--src/core/hle/service/filesystem/filesystem.h116
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp80
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h4
-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.h13
-rw-r--r--src/core/hle/service/ns/pl_u.cpp11
-rw-r--r--src/core/hle/service/ns/pl_u.h15
-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.cpp28
-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.cpp105
-rw-r--r--src/core/hle/service/prepo/prepo.h6
-rw-r--r--src/core/hle/service/service.cpp27
-rw-r--r--src/core/hle/service/service.h8
-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/deconstructed_rom_directory.cpp4
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/loader/nro.cpp4
-rw-r--r--src/core/loader/nso.cpp3
-rw-r--r--src/core/loader/nsp.cpp31
-rw-r--r--src/core/loader/xci.cpp4
-rw-r--r--src/core/memory.cpp9
-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/core/perf_stats.cpp47
-rw-r--r--src/core/perf_stats.h21
-rw-r--r--src/core/reporter.cpp10
-rw-r--r--src/core/reporter.h10
-rw-r--r--src/core/settings.cpp5
-rw-r--r--src/core/settings.h35
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp104
-rw-r--r--src/video_core/engines/maxwell_3d.h29
-rw-r--r--src/video_core/engines/shader_bytecode.h13
-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_opengl/maxwell_to_gl.h4
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp1
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp29
-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/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp75
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.ui80
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp177
-rw-r--r--src/yuzu/configuration/configure_filesystem.h43
-rw-r--r--src/yuzu/configuration/configure_filesystem.ui395
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/main.cpp98
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu_cmd/config.cpp25
-rw-r--r--src/yuzu_cmd/default_ini.h16
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
-rw-r--r--src/yuzu_tester/yuzu.cpp4
192 files changed, 5130 insertions, 1645 deletions
diff --git a/.ci/templates/build-mock.yml b/.ci/templates/build-mock.yml
new file mode 100644
index 000000000..e7aba93de
--- /dev/null
+++ b/.ci/templates/build-mock.yml
@@ -0,0 +1,5 @@
1steps:
2 - script: mkdir artifacts || echo 'X' > artifacts/T1.txt
3 - publish: artifacts
4 artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
5 displayName: 'Upload Artifacts' \ No newline at end of file
diff --git a/.ci/templates/build-single.yml b/.ci/templates/build-single.yml
index 357731eb9..9bc27247e 100644
--- a/.ci/templates/build-single.yml
+++ b/.ci/templates/build-single.yml
@@ -3,17 +3,18 @@ parameters:
3 cache: 'false' 3 cache: 'false'
4 4
5steps: 5steps:
6- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
7 displayName: 'Determine Build Name'
6- task: DockerInstaller@0 8- task: DockerInstaller@0
7 displayName: 'Prepare Environment' 9 displayName: 'Prepare Environment'
8 inputs: 10 inputs:
9 dockerVersion: '17.09.0-ce' 11 dockerVersion: '17.09.0-ce'
10- ${{ if eq(parameters.cache, 'true') }}: 12- task: CacheBeta@0
11 - task: CacheBeta@0 13 displayName: 'Cache Build System'
12 displayName: 'Cache Build System' 14 inputs:
13 inputs: 15 key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
14 key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix) 16 path: $(System.DefaultWorkingDirectory)/ccache
15 path: $(System.DefaultWorkingDirectory)/ccache 17 cacheHitVar: CACHE_RESTORED
16 cacheHitVar: CACHE_RESTORED
17- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh 18- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
18 displayName: 'Build' 19 displayName: 'Build'
19- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh 20- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
diff --git a/.ci/templates/release-download.yml b/.ci/templates/release-download.yml
new file mode 100644
index 000000000..50ca06bb2
--- /dev/null
+++ b/.ci/templates/release-download.yml
@@ -0,0 +1,13 @@
1steps:
2 - task: DownloadPipelineArtifact@2
3 displayName: 'Download Windows Release'
4 inputs:
5 artifactName: 'yuzu-$(BuildName)-windows-mingw'
6 buildType: 'current'
7 targetPath: '$(Build.ArtifactStagingDirectory)'
8 - task: DownloadPipelineArtifact@2
9 displayName: 'Download Linux Release'
10 inputs:
11 artifactName: 'yuzu-$(BuildName)-linux'
12 buildType: 'current'
13 targetPath: '$(Build.ArtifactStagingDirectory)' \ No newline at end of file
diff --git a/.ci/templates/release-github.yml b/.ci/templates/release-github.yml
new file mode 100644
index 000000000..39fd47f1c
--- /dev/null
+++ b/.ci/templates/release-github.yml
@@ -0,0 +1,11 @@
1steps:
2 - template: ./release-download.yml
3 - task: GitHubRelease@0
4 inputs:
5 action: 'create'
6 title: 'yuzu $(BuildName) #$(Build.BuildId)'
7 assets: '$(Build.ArtifactStagingDirectory)/*'
8 gitHubConnection: $(GitHubReleaseConnectionName)
9 repositoryName: '$(Build.Repository.Name)'
10 target: '$(Build.SourceVersion)'
11 tagSource: 'auto' \ No newline at end of file
diff --git a/.ci/templates/release-universal.yml b/.ci/templates/release-universal.yml
new file mode 100644
index 000000000..707697007
--- /dev/null
+++ b/.ci/templates/release-universal.yml
@@ -0,0 +1,10 @@
1steps:
2 - template: ./release-download.yml
3 - task: UniversalPackages@0
4 displayName: Publish Artifacts
5 inputs:
6 command: publish
7 publishDirectory: '$(Build.ArtifactStagingDirectory)'
8 vstsFeedPublish: 'yuzu-$(BuildName)'
9 vstsFeedPackagePublish: 'main'
10 packagePublishDescription: 'Yuzu Windows and Linux Executable Packages' \ No newline at end of file
diff --git a/.ci/yuzu-mainline-step1.yml b/.ci/yuzu-mainline-step1.yml
new file mode 100644
index 000000000..3fd33d75a
--- /dev/null
+++ b/.ci/yuzu-mainline-step1.yml
@@ -0,0 +1,8 @@
1trigger:
2- master
3
4stages:
5- stage: merge
6 displayName: 'merge'
7 jobs:
8 - template: ./templates/merge.yml
diff --git a/.ci/yuzu-mainline.yml b/.ci/yuzu-mainline-step2.yml
index 2930a8564..fec724d11 100644
--- a/.ci/yuzu-mainline.yml
+++ b/.ci/yuzu-mainline-step2.yml
@@ -2,12 +2,7 @@ trigger:
2- master 2- master
3 3
4stages: 4stages:
5- stage: merge
6 displayName: 'merge'
7 jobs:
8 - template: ./templates/merge.yml
9- stage: format 5- stage: format
10 dependsOn: merge
11 displayName: 'format' 6 displayName: 'format'
12 jobs: 7 jobs:
13 - job: format 8 - job: format
@@ -17,9 +12,17 @@ stages:
17 steps: 12 steps:
18 - template: ./templates/format-check.yml 13 - template: ./templates/format-check.yml
19- stage: build 14- stage: build
20 displayName: 'build'
21 dependsOn: format 15 dependsOn: format
16 displayName: 'build'
22 jobs: 17 jobs:
23 - template: ./templates/build-standard.yml 18 - template: ./templates/build-standard.yml
24 parameters: 19 parameters:
25 cache: 'true' 20 cache: 'true'
21- stage: release
22 displayName: 'Release'
23 dependsOn: build
24 jobs:
25 - job: github
26 displayName: 'GitHub Release'
27 steps:
28 - template: ./templates/release-github.yml \ No newline at end of file
diff --git a/README.md b/README.md
index 430c6dd65..ecace43f2 100644
--- a/README.md
+++ b/README.md
@@ -6,9 +6,11 @@ yuzu emulator
6 6
7yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). 7yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
8 8
9It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes. 9It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
10 10
11yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success. 11yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
12
13Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
12 14
13yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. 15yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
14 16
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 2683a9a3e316b5c3f387bbe6787732b9ff44b8d Subproject 087a74417abfb0a8ae3bc1463d0d476a9bf94e5
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/CMakeLists.txt b/src/common/CMakeLists.txt
index 01abdb3bb..dfed8b51d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -10,6 +10,9 @@ if (DEFINED ENV{CI})
10 elseif(DEFINED ENV{APPVEYOR}) 10 elseif(DEFINED ENV{APPVEYOR})
11 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) 11 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
12 set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) 12 set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
13 elseif(DEFINED ENV{AZURE})
14 set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
15 set(BUILD_TAG $ENV{AZURE_REPO_TAG})
13 endif() 16 endif()
14endif() 17endif()
15add_custom_command(OUTPUT scm_rev.cpp 18add_custom_command(OUTPUT scm_rev.cpp
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 3d0978cbf..92ba42fb9 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,8 +14,14 @@
14#include "core/core_cpu.h" 14#include "core/core_cpu.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/cpu_core_manager.h" 16#include "core/cpu_core_manager.h"
17#include "core/file_sys/bis_factory.h"
18#include "core/file_sys/card_image.h"
17#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/patch_manager.h"
18#include "core/file_sys/registered_cache.h" 21#include "core/file_sys/registered_cache.h"
22#include "core/file_sys/romfs_factory.h"
23#include "core/file_sys/savedata_factory.h"
24#include "core/file_sys/sdmc_factory.h"
19#include "core/file_sys/vfs_concat.h" 25#include "core/file_sys/vfs_concat.h"
20#include "core/file_sys/vfs_real.h" 26#include "core/file_sys/vfs_real.h"
21#include "core/gdbstub/gdbstub.h" 27#include "core/gdbstub/gdbstub.h"
@@ -27,17 +33,17 @@
27#include "core/hle/kernel/thread.h" 33#include "core/hle/kernel/thread.h"
28#include "core/hle/service/am/applets/applets.h" 34#include "core/hle/service/am/applets/applets.h"
29#include "core/hle/service/apm/controller.h" 35#include "core/hle/service/apm/controller.h"
36#include "core/hle/service/filesystem/filesystem.h"
30#include "core/hle/service/glue/manager.h" 37#include "core/hle/service/glue/manager.h"
31#include "core/hle/service/service.h" 38#include "core/hle/service/service.h"
32#include "core/hle/service/sm/sm.h" 39#include "core/hle/service/sm/sm.h"
33#include "core/loader/loader.h" 40#include "core/loader/loader.h"
41#include "core/memory/cheat_engine.h"
34#include "core/perf_stats.h" 42#include "core/perf_stats.h"
35#include "core/reporter.h" 43#include "core/reporter.h"
36#include "core/settings.h" 44#include "core/settings.h"
37#include "core/telemetry_session.h" 45#include "core/telemetry_session.h"
38#include "core/tools/freezer.h" 46#include "core/tools/freezer.h"
39#include "file_sys/cheat_engine.h"
40#include "file_sys/patch_manager.h"
41#include "video_core/debug_utils/debug_utils.h" 47#include "video_core/debug_utils/debug_utils.h"
42#include "video_core/renderer_base.h" 48#include "video_core/renderer_base.h"
43#include "video_core/video_core.h" 49#include "video_core/video_core.h"
@@ -157,13 +163,10 @@ struct System::Impl {
157 gpu_core = VideoCore::CreateGPU(system); 163 gpu_core = VideoCore::CreateGPU(system);
158 164
159 is_powered_on = true; 165 is_powered_on = true;
166 exit_lock = false;
160 167
161 LOG_DEBUG(Core, "Initialized OK"); 168 LOG_DEBUG(Core, "Initialized OK");
162 169
163 // Reset counters and set time origin to current frame
164 GetAndResetPerfStats();
165 perf_stats.BeginSystemFrame();
166
167 return ResultStatus::Success; 170 return ResultStatus::Success;
168 } 171 }
169 172
@@ -202,10 +205,34 @@ struct System::Impl {
202 gpu_core->Start(); 205 gpu_core->Start();
203 cpu_core_manager.StartThreads(); 206 cpu_core_manager.StartThreads();
204 207
208 // Initialize cheat engine
209 if (cheat_engine) {
210 cheat_engine->Initialize();
211 }
212
205 // 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.
206 main_process->Run(load_parameters->main_thread_priority, 214 main_process->Run(load_parameters->main_thread_priority,
207 load_parameters->main_thread_stack_size); 215 load_parameters->main_thread_stack_size);
208 216
217 if (Settings::values.gamecard_inserted) {
218 if (Settings::values.gamecard_current_game) {
219 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
220 } else if (!Settings::values.gamecard_path.empty()) {
221 fs_controller.SetGameCard(
222 GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
223 }
224 }
225
226 u64 title_id{0};
227 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
228 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
229 static_cast<u32>(load_result));
230 }
231 perf_stats = std::make_unique<PerfStats>(title_id);
232 // Reset counters and set time origin to current frame
233 GetAndResetPerfStats();
234 perf_stats->BeginSystemFrame();
235
209 status = ResultStatus::Success; 236 status = ResultStatus::Success;
210 return status; 237 return status;
211 } 238 }
@@ -219,8 +246,11 @@ struct System::Impl {
219 perf_results.game_fps); 246 perf_results.game_fps);
220 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 247 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
221 perf_results.frametime * 1000.0); 248 perf_results.frametime * 1000.0);
249 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
250 perf_stats->GetMeanFrametime());
222 251
223 is_powered_on = false; 252 is_powered_on = false;
253 exit_lock = false;
224 254
225 // Shutdown emulation session 255 // Shutdown emulation session
226 renderer.reset(); 256 renderer.reset();
@@ -229,6 +259,7 @@ struct System::Impl {
229 service_manager.reset(); 259 service_manager.reset();
230 cheat_engine.reset(); 260 cheat_engine.reset();
231 telemetry_session.reset(); 261 telemetry_session.reset();
262 perf_stats.reset();
232 gpu_core.reset(); 263 gpu_core.reset();
233 264
234 // Close all CPU/threading state 265 // Close all CPU/threading state
@@ -286,7 +317,7 @@ struct System::Impl {
286 } 317 }
287 318
288 PerfStatsResults GetAndResetPerfStats() { 319 PerfStatsResults GetAndResetPerfStats() {
289 return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); 320 return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
290 } 321 }
291 322
292 Timing::CoreTiming core_timing; 323 Timing::CoreTiming core_timing;
@@ -295,6 +326,7 @@ struct System::Impl {
295 FileSys::VirtualFilesystem virtual_filesystem; 326 FileSys::VirtualFilesystem virtual_filesystem;
296 /// ContentProviderUnion instance 327 /// ContentProviderUnion instance
297 std::unique_ptr<FileSys::ContentProviderUnion> content_provider; 328 std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
329 Service::FileSystem::FileSystemController fs_controller;
298 /// AppLoader used to load the current executing application 330 /// AppLoader used to load the current executing application
299 std::unique_ptr<Loader::AppLoader> app_loader; 331 std::unique_ptr<Loader::AppLoader> app_loader;
300 std::unique_ptr<VideoCore::RendererBase> renderer; 332 std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -303,8 +335,9 @@ struct System::Impl {
303 std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; 335 std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
304 CpuCoreManager cpu_core_manager; 336 CpuCoreManager cpu_core_manager;
305 bool is_powered_on = false; 337 bool is_powered_on = false;
338 bool exit_lock = false;
306 339
307 std::unique_ptr<FileSys::CheatEngine> cheat_engine; 340 std::unique_ptr<Memory::CheatEngine> cheat_engine;
308 std::unique_ptr<Tools::Freezer> memory_freezer; 341 std::unique_ptr<Tools::Freezer> memory_freezer;
309 342
310 /// Frontend applets 343 /// Frontend applets
@@ -327,7 +360,7 @@ struct System::Impl {
327 ResultStatus status = ResultStatus::Success; 360 ResultStatus status = ResultStatus::Success;
328 std::string status_details = ""; 361 std::string status_details = "";
329 362
330 Core::PerfStats perf_stats; 363 std::unique_ptr<Core::PerfStats> perf_stats;
331 Core::FrameLimiter frame_limiter; 364 Core::FrameLimiter frame_limiter;
332}; 365};
333 366
@@ -480,11 +513,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
480} 513}
481 514
482Core::PerfStats& System::GetPerfStats() { 515Core::PerfStats& System::GetPerfStats() {
483 return impl->perf_stats; 516 return *impl->perf_stats;
484} 517}
485 518
486const Core::PerfStats& System::GetPerfStats() const { 519const Core::PerfStats& System::GetPerfStats() const {
487 return impl->perf_stats; 520 return *impl->perf_stats;
488} 521}
489 522
490Core::FrameLimiter& System::FrameLimiter() { 523Core::FrameLimiter& System::FrameLimiter() {
@@ -519,13 +552,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
519 return impl->debug_context.get(); 552 return impl->debug_context.get();
520} 553}
521 554
522void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
523 const std::string& build_id, VAddr code_region_start,
524 VAddr code_region_end) {
525 impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
526 code_region_start, code_region_end);
527}
528
529void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 555void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
530 impl->virtual_filesystem = std::move(vfs); 556 impl->virtual_filesystem = std::move(vfs);
531} 557}
@@ -534,6 +560,13 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
534 return impl->virtual_filesystem; 560 return impl->virtual_filesystem;
535} 561}
536 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
537void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { 570void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
538 impl->applet_manager.SetAppletFrontendSet(std::move(set)); 571 impl->applet_manager.SetAppletFrontendSet(std::move(set));
539} 572}
@@ -562,6 +595,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
562 return *impl->content_provider; 595 return *impl->content_provider;
563} 596}
564 597
598Service::FileSystem::FileSystemController& System::GetFileSystemController() {
599 return impl->fs_controller;
600}
601
602const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
603 return impl->fs_controller;
604}
605
565void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 606void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
566 FileSys::ContentProvider* provider) { 607 FileSys::ContentProvider* provider) {
567 impl->content_provider->SetSlot(slot, provider); 608 impl->content_provider->SetSlot(slot, provider);
@@ -591,6 +632,14 @@ const Service::APM::Controller& System::GetAPMController() const {
591 return impl->apm_controller; 632 return impl->apm_controller;
592} 633}
593 634
635void System::SetExitLock(bool locked) {
636 impl->exit_lock = locked;
637}
638
639bool System::GetExitLock() const {
640 return impl->exit_lock;
641}
642
594System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 643System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
595 return impl->Init(*this, emu_window); 644 return impl->Init(*this, emu_window);
596} 645}
diff --git a/src/core/core.h b/src/core/core.h
index 0138d93b0..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 {
@@ -47,6 +50,10 @@ namespace APM {
47class Controller; 50class Controller;
48} 51}
49 52
53namespace FileSystem {
54class FileSystemController;
55} // namespace FileSystem
56
50namespace Glue { 57namespace Glue {
51class ARPManager; 58class ARPManager;
52} 59}
@@ -282,8 +289,9 @@ public:
282 289
283 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 290 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
284 291
285 void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, 292 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
286 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);
287 295
288 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 296 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
289 297
@@ -299,6 +307,10 @@ public:
299 307
300 const FileSys::ContentProvider& GetContentProvider() const; 308 const FileSys::ContentProvider& GetContentProvider() const;
301 309
310 Service::FileSystem::FileSystemController& GetFileSystemController();
311
312 const Service::FileSystem::FileSystemController& GetFileSystemController() const;
313
302 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 314 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
303 FileSys::ContentProvider* provider); 315 FileSys::ContentProvider* provider);
304 316
@@ -314,6 +326,10 @@ public:
314 326
315 const Service::APM::Controller& GetAPMController() const; 327 const Service::APM::Controller& GetAPMController() const;
316 328
329 void SetExitLock(bool locked);
330
331 bool GetExitLock() const;
332
317private: 333private:
318 System(); 334 System();
319 335
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 01a969be9..594cd82c5 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
480 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key); 480 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
481} 481}
482 482
483FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
484 return prodinfo_decrypted;
485}
486
483std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { 487std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
484 std::array<u8, 0x240> out{}; 488 std::array<u8, 0x240> out{};
485 if (prodinfo_decrypted != nullptr) 489 if (prodinfo_decrypted != nullptr)
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 0ad007c72..7a7b5d038 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -84,6 +84,7 @@ public:
84 bool HasProdInfo() const; 84 bool HasProdInfo() const;
85 FileSys::VirtualFile GetProdInfoRaw() const; 85 FileSys::VirtualFile GetProdInfoRaw() const;
86 void DecryptProdInfo(std::array<u8, 0x20> bis_key); 86 void DecryptProdInfo(std::array<u8, 0x20> bis_key);
87 FileSys::VirtualFile GetDecryptedProdInfo() const;
87 std::array<u8, 0x240> GetETicketExtendedKek() const; 88 std::array<u8, 0x240> GetETicketExtendedKek() const;
88 89
89private: 90private:
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index e29f70b3a..8f758d6d9 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -3,8 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h> 5#include <fmt/format.h>
6#include "common/file_util.h"
7#include "core/core.h"
6#include "core/file_sys/bis_factory.h" 8#include "core/file_sys/bis_factory.h"
9#include "core/file_sys/mode.h"
7#include "core/file_sys/registered_cache.h" 10#include "core/file_sys/registered_cache.h"
11#include "core/settings.h"
8 12
9namespace FileSys { 13namespace FileSys {
10 14
@@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
14 sysnand_cache(std::make_unique<RegisteredCache>( 18 sysnand_cache(std::make_unique<RegisteredCache>(
15 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 19 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
16 usrnand_cache(std::make_unique<RegisteredCache>( 20 usrnand_cache(std::make_unique<RegisteredCache>(
17 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 21 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
22 sysnand_placeholder(std::make_unique<PlaceholderCache>(
23 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
24 usrnand_placeholder(std::make_unique<PlaceholderCache>(
25 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
18 26
19BISFactory::~BISFactory() = default; 27BISFactory::~BISFactory() = default;
20 28
29VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
30 return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
31}
32
33VirtualDir BISFactory::GetUserNANDContentDirectory() const {
34 return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
35}
36
21RegisteredCache* BISFactory::GetSystemNANDContents() const { 37RegisteredCache* BISFactory::GetSystemNANDContents() const {
22 return sysnand_cache.get(); 38 return sysnand_cache.get();
23} 39}
@@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
26 return usrnand_cache.get(); 42 return usrnand_cache.get();
27} 43}
28 44
45PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
46 return sysnand_placeholder.get();
47}
48
49PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
50 return usrnand_placeholder.get();
51}
52
29VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 53VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
30 // LayeredFS doesn't work on updates and title id-less homebrew 54 // LayeredFS doesn't work on updates and title id-less homebrew
31 if (title_id == 0 || (title_id & 0x800) > 0) 55 if (title_id == 0 || (title_id & 0xFFF) == 0x800)
32 return nullptr; 56 return nullptr;
33 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); 57 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
34} 58}
@@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
39 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); 63 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
40} 64}
41 65
66VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
67 switch (id) {
68 case BisPartitionId::CalibrationFile:
69 return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
70 case BisPartitionId::SafeMode:
71 return GetOrCreateDirectoryRelative(nand_root, "/safe");
72 case BisPartitionId::System:
73 return GetOrCreateDirectoryRelative(nand_root, "/system");
74 case BisPartitionId::User:
75 return GetOrCreateDirectoryRelative(nand_root, "/user");
76 default:
77 return nullptr;
78 }
79}
80
81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
82 Core::Crypto::KeyManager keys;
83 Core::Crypto::PartitionDataManager pdm{
84 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
86 keys.PopulateFromPartitionData(pdm);
87
88 switch (id) {
89 case BisPartitionId::CalibrationBinary:
90 return pdm.GetDecryptedProdInfo();
91 case BisPartitionId::BootConfigAndPackage2Part1:
92 case BisPartitionId::BootConfigAndPackage2Part2:
93 case BisPartitionId::BootConfigAndPackage2Part3:
94 case BisPartitionId::BootConfigAndPackage2Part4:
95 case BisPartitionId::BootConfigAndPackage2Part5:
96 case BisPartitionId::BootConfigAndPackage2Part6: {
97 const auto new_id = static_cast<u8>(id) -
98 static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
99 static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
100 return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
101 }
102 default:
103 return nullptr;
104 }
105}
106
107VirtualDir BISFactory::GetImageDirectory() const {
108 return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
109}
110
111u64 BISFactory::GetSystemNANDFreeSpace() const {
112 const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
113 if (sys_dir == nullptr)
114 return 0;
115
116 return GetSystemNANDTotalSpace() - sys_dir->GetSize();
117}
118
119u64 BISFactory::GetSystemNANDTotalSpace() const {
120 return static_cast<u64>(Settings::values.nand_system_size);
121}
122
123u64 BISFactory::GetUserNANDFreeSpace() const {
124 const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
125 if (usr_dir == nullptr)
126 return 0;
127
128 return GetUserNANDTotalSpace() - usr_dir->GetSize();
129}
130
131u64 BISFactory::GetUserNANDTotalSpace() const {
132 return static_cast<u64>(Settings::values.nand_user_size);
133}
134
135u64 BISFactory::GetFullNANDTotalSpace() const {
136 return static_cast<u64>(Settings::values.nand_total_size);
137}
138
42} // namespace FileSys 139} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 453c11ad2..bdfe728c9 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -10,7 +10,25 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13enum class BisPartitionId : u32 {
14 UserDataRoot = 20,
15 CalibrationBinary = 27,
16 CalibrationFile = 28,
17 BootConfigAndPackage2Part1 = 21,
18 BootConfigAndPackage2Part2 = 22,
19 BootConfigAndPackage2Part3 = 23,
20 BootConfigAndPackage2Part4 = 24,
21 BootConfigAndPackage2Part5 = 25,
22 BootConfigAndPackage2Part6 = 26,
23 SafeMode = 29,
24 System = 31,
25 SystemProperEncryption = 32,
26 SystemProperPartition = 33,
27 User = 30,
28};
29
13class RegisteredCache; 30class RegisteredCache;
31class PlaceholderCache;
14 32
15/// File system interface to the Built-In Storage 33/// File system interface to the Built-In Storage
16/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND 34/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -20,12 +38,29 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); 38 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
21 ~BISFactory(); 39 ~BISFactory();
22 40
41 VirtualDir GetSystemNANDContentDirectory() const;
42 VirtualDir GetUserNANDContentDirectory() const;
43
23 RegisteredCache* GetSystemNANDContents() const; 44 RegisteredCache* GetSystemNANDContents() const;
24 RegisteredCache* GetUserNANDContents() const; 45 RegisteredCache* GetUserNANDContents() const;
25 46
47 PlaceholderCache* GetSystemNANDPlaceholder() const;
48 PlaceholderCache* GetUserNANDPlaceholder() const;
49
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 50 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 VirtualDir GetModificationDumpRoot(u64 title_id) const; 51 VirtualDir GetModificationDumpRoot(u64 title_id) const;
28 52
53 VirtualDir OpenPartition(BisPartitionId id) const;
54 VirtualFile OpenPartitionStorage(BisPartitionId id) const;
55
56 VirtualDir GetImageDirectory() const;
57
58 u64 GetSystemNANDFreeSpace() const;
59 u64 GetSystemNANDTotalSpace() const;
60 u64 GetUserNANDFreeSpace() const;
61 u64 GetUserNANDTotalSpace() const;
62 u64 GetFullNANDTotalSpace() const;
63
29private: 64private:
30 VirtualDir nand_root; 65 VirtualDir nand_root;
31 VirtualDir load_root; 66 VirtualDir load_root;
@@ -33,6 +68,9 @@ private:
33 68
34 std::unique_ptr<RegisteredCache> sysnand_cache; 69 std::unique_ptr<RegisteredCache> sysnand_cache;
35 std::unique_ptr<RegisteredCache> usrnand_cache; 70 std::unique_ptr<RegisteredCache> usrnand_cache;
71
72 std::unique_ptr<PlaceholderCache> sysnand_placeholder;
73 std::unique_ptr<PlaceholderCache> usrnand_placeholder;
36}; 74};
37 75
38} // namespace FileSys 76} // namespace FileSys
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 626ed0042..db54113a0 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -12,12 +12,16 @@
12#include "core/file_sys/content_archive.h" 12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/partition_filesystem.h" 14#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/romfs.h"
15#include "core/file_sys/submission_package.h" 16#include "core/file_sys/submission_package.h"
17#include "core/file_sys/vfs_concat.h"
16#include "core/file_sys/vfs_offset.h" 18#include "core/file_sys/vfs_offset.h"
19#include "core/file_sys/vfs_vector.h"
17#include "core/loader/loader.h" 20#include "core/loader/loader.h"
18 21
19namespace FileSys { 22namespace FileSys {
20 23
24constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
21constexpr std::array partition_names{ 25constexpr std::array partition_names{
22 "update", 26 "update",
23 "normal", 27 "normal",
@@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const {
175 return file->GetContainingDirectory(); 179 return file->GetContainingDirectory();
176} 180}
177 181
182VirtualDir XCI::ConcatenatedPseudoDirectory() {
183 const auto out = std::make_shared<VectorVfsDirectory>();
184 for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
185 const auto& part = GetPartition(part_id);
186 if (part == nullptr)
187 continue;
188
189 for (const auto& file : part->GetFiles())
190 out->AddFile(file);
191 }
192
193 return out;
194}
195
196std::array<u8, 0x200> XCI::GetCertificate() const {
197 std::array<u8, 0x200> out;
198 file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
199 return out;
200}
201
178Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 202Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
179 const auto partition_index = static_cast<std::size_t>(part); 203 const auto partition_index = static_cast<std::size_t>(part);
180 const auto& partition = partitions[partition_index]; 204 const auto& partition = partitions[partition_index];
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index a350496f7..3e6b92ff3 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -91,6 +91,8 @@ public:
91 VirtualDir GetLogoPartition() const; 91 VirtualDir GetLogoPartition() const;
92 92
93 u64 GetProgramTitleID() const; 93 u64 GetProgramTitleID() const;
94 u32 GetSystemUpdateVersion();
95 u64 GetSystemUpdateTitleID() const;
94 96
95 bool HasProgramNCA() const; 97 bool HasProgramNCA() const;
96 VirtualFile GetProgramNCAFile() const; 98 VirtualFile GetProgramNCAFile() const;
@@ -106,6 +108,11 @@ public:
106 108
107 VirtualDir GetParentDirectory() const override; 109 VirtualDir GetParentDirectory() const override;
108 110
111 // Creates a directory that contains all the NCAs in the gamecard
112 VirtualDir ConcatenatedPseudoDirectory();
113
114 std::array<u8, 0x200> GetCertificate() const;
115
109private: 116private:
110 Loader::ResultStatus AddNCAFromPartition(XCIPartition part); 117 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
111 118
@@ -120,6 +127,8 @@ private:
120 std::shared_ptr<NCA> program; 127 std::shared_ptr<NCA> program;
121 std::vector<std::shared_ptr<NCA>> ncas; 128 std::vector<std::shared_ptr<NCA>> ncas;
122 129
130 u64 update_normal_partition_end;
131
123 Core::Crypto::KeyManager keys; 132 Core::Crypto::KeyManager keys;
124}; 133};
125} // namespace FileSys 134} // namespace FileSys
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/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index ce5c69b41..ea5c92f61 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const {
528 return header.title_id; 528 return header.title_id;
529} 529}
530 530
531std::array<u8, 16> NCA::GetRightsId() const {
532 return header.rights_id;
533}
534
535u32 NCA::GetSDKVersion() const {
536 return header.sdk_version;
537}
538
531bool NCA::IsUpdate() const { 539bool NCA::IsUpdate() const {
532 return is_update; 540 return is_update;
533} 541}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 15b9e6624..e249079b5 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -112,6 +112,8 @@ public:
112 112
113 NCAContentType GetType() const; 113 NCAContentType GetType() const;
114 u64 GetTitleId() const; 114 u64 GetTitleId() const;
115 std::array<u8, 0x10> GetRightsId() const;
116 u32 GetSDKVersion() const;
115 bool IsUpdate() const; 117 bool IsUpdate() const;
116 118
117 VirtualFile GetRomFS() const; 119 VirtualFile GetRomFS() const;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index a8f80e2c6..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 {
@@ -63,7 +64,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
63 64
64 if (Settings::values.dump_exefs) { 65 if (Settings::values.dump_exefs) {
65 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); 66 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
66 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 67 const auto dump_dir =
68 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
67 if (dump_dir != nullptr) { 69 if (dump_dir != nullptr) {
68 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); 70 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
69 VfsRawCopyD(exefs, exefs_dir); 71 VfsRawCopyD(exefs, exefs_dir);
@@ -88,7 +90,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
88 } 90 }
89 91
90 // LayeredExeFS 92 // LayeredExeFS
91 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 93 const auto load_dir =
94 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
92 if (load_dir != nullptr && load_dir->GetSize() > 0) { 95 if (load_dir != nullptr && load_dir->GetSize() > 0) {
93 auto patch_dirs = load_dir->GetSubdirectories(); 96 auto patch_dirs = load_dir->GetSubdirectories();
94 std::sort( 97 std::sort(
@@ -174,7 +177,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
174 if (Settings::values.dump_nso) { 177 if (Settings::values.dump_nso) {
175 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, 178 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
176 title_id); 179 title_id);
177 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 180 const auto dump_dir =
181 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
178 if (dump_dir != nullptr) { 182 if (dump_dir != nullptr) {
179 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 183 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
180 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); 184 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -186,7 +190,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
186 190
187 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); 191 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
188 192
189 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 193 const auto load_dir =
194 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
195 if (load_dir == nullptr) {
196 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
197 return nso;
198 }
199
190 auto patch_dirs = load_dir->GetSubdirectories(); 200 auto patch_dirs = load_dir->GetSubdirectories();
191 std::sort(patch_dirs.begin(), patch_dirs.end(), 201 std::sort(patch_dirs.begin(), patch_dirs.end(),
192 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 202 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -224,7 +234,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
224 234
225 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); 235 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
226 236
227 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 237 const auto load_dir =
238 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
239 if (load_dir == nullptr) {
240 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
241 return false;
242 }
243
228 auto patch_dirs = load_dir->GetSubdirectories(); 244 auto patch_dirs = load_dir->GetSubdirectories();
229 std::sort(patch_dirs.begin(), patch_dirs.end(), 245 std::sort(patch_dirs.begin(), patch_dirs.end(),
230 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 246 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -232,9 +248,10 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
232 return !CollectPatches(patch_dirs, build_id).empty(); 248 return !CollectPatches(patch_dirs, build_id).empty();
233} 249}
234 250
235static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id, 251namespace {
236 const std::array<u8, 0x20>& build_id_, 252std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
237 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) {
238 const auto build_id_raw = Common::HexToString(build_id_, upper); 255 const auto build_id_raw = Common::HexToString(build_id_, upper);
239 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);
240 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); 257 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -252,31 +269,39 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
252 return std::nullopt; 269 return std::nullopt;
253 } 270 }
254 271
255 TextCheatParser parser; 272 Memory::TextCheatParser parser;
256 return parser.Parse(system, data); 273 return parser.Parse(
274 system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
257} 275}
258 276
259std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, 277} // Anonymous namespace
260 const std::array<u8, 32>& build_id_) const { 278
261 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 279std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
280 const Core::System& system, const std::array<u8, 32>& build_id_) const {
281 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
282 if (load_dir == nullptr) {
283 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
284 return {};
285 }
286
262 auto patch_dirs = load_dir->GetSubdirectories(); 287 auto patch_dirs = load_dir->GetSubdirectories();
263 std::sort(patch_dirs.begin(), patch_dirs.end(), 288 std::sort(patch_dirs.begin(), patch_dirs.end(),
264 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 289 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
265 290
266 std::vector<CheatList> out; 291 std::vector<Memory::CheatEntry> out;
267 out.reserve(patch_dirs.size());
268 for (const auto& subdir : patch_dirs) { 292 for (const auto& subdir : patch_dirs) {
269 auto cheats_dir = subdir->GetSubdirectory("cheats"); 293 auto cheats_dir = subdir->GetSubdirectory("cheats");
270 if (cheats_dir != nullptr) { 294 if (cheats_dir != nullptr) {
271 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); 295 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
272 if (res.has_value()) { 296 if (res.has_value()) {
273 out.push_back(std::move(*res)); 297 std::copy(res->begin(), res->end(), std::back_inserter(out));
274 continue; 298 continue;
275 } 299 }
276 300
277 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); 301 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
278 if (res.has_value()) 302 if (res.has_value()) {
279 out.push_back(std::move(*res)); 303 std::copy(res->begin(), res->end(), std::back_inserter(out));
304 }
280 } 305 }
281 } 306 }
282 307
@@ -284,7 +309,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
284} 309}
285 310
286static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 311static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
287 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 312 const auto load_dir =
313 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
288 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 314 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
289 load_dir == nullptr || load_dir->GetSize() <= 0) { 315 load_dir == nullptr || load_dir->GetSize() <= 0) {
290 return; 316 return;
@@ -393,6 +419,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
393 419
394std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( 420std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
395 VirtualFile update_raw) const { 421 VirtualFile update_raw) const {
422 if (title_id == 0)
423 return {};
396 std::map<std::string, std::string, std::less<>> out; 424 std::map<std::string, std::string, std::less<>> out;
397 const auto& installed = Core::System::GetInstance().GetContentProvider(); 425 const auto& installed = Core::System::GetInstance().GetContentProvider();
398 const auto& disabled = Settings::values.disabled_addons[title_id]; 426 const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -423,7 +451,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
423 } 451 }
424 452
425 // General Mods (LayeredFS and IPS) 453 // General Mods (LayeredFS and IPS)
426 const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 454 const auto mod_dir =
455 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
427 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 456 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
428 for (const auto& mod : mod_dir->GetSubdirectories()) { 457 for (const auto& mod : mod_dir->GetSubdirectories()) {
429 std::string types; 458 std::string types;
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/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 3725b10f7..ac3fbd849 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <random>
6#include <regex> 7#include <regex>
7#include <mbedtls/sha256.h> 8#include <mbedtls/sha256.h>
8#include "common/assert.h" 9#include "common/assert.h"
@@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
48static bool FollowsNcaIdFormat(std::string_view name) { 49static bool FollowsNcaIdFormat(std::string_view name) {
49 static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | 50 static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 51 std::regex_constants::icase);
51 return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); 52 static const std::regex nca_id_cnmt_regex(
53 "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
54 return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
55 (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
52} 56}
53 57
54static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, 58static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
55 bool within_two_digit) { 59 bool within_two_digit, bool cnmt_suffix) {
56 if (!within_two_digit) { 60 if (!within_two_digit)
57 return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper)); 61 return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
58 } 62 Common::HexToString(nca_id, second_hex_upper));
59 63
60 Core::Crypto::SHA256Hash hash{}; 64 Core::Crypto::SHA256Hash hash{};
61 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); 65 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
62 return fmt::format("/000000{:02X}/{}.nca", hash[0], 66 return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
63 Common::HexToString(nca_id, second_hex_upper)); 67 Common::HexToString(nca_id, second_hex_upper));
64} 68}
65 69
@@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
127 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); 131 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
128} 132}
129 133
134PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
135
136bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
137 const auto path = GetRelativePathFromNcaID(id, false, true, false);
138
139 if (dir->GetFileRelative(path) != nullptr) {
140 return false;
141 }
142
143 Core::Crypto::SHA256Hash hash{};
144 mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
145 const auto dirname = fmt::format("000000{:02X}", hash[0]);
146
147 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
148
149 if (dir2 == nullptr)
150 return false;
151
152 const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
153
154 if (file == nullptr)
155 return false;
156
157 return file->Resize(size);
158}
159
160bool PlaceholderCache::Delete(const NcaID& id) const {
161 const auto path = GetRelativePathFromNcaID(id, false, true, false);
162
163 if (dir->GetFileRelative(path) == nullptr) {
164 return false;
165 }
166
167 Core::Crypto::SHA256Hash hash{};
168 mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
169 const auto dirname = fmt::format("000000{:02X}", hash[0]);
170
171 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
172
173 const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
174
175 return res;
176}
177
178bool PlaceholderCache::Exists(const NcaID& id) const {
179 const auto path = GetRelativePathFromNcaID(id, false, true, false);
180
181 return dir->GetFileRelative(path) != nullptr;
182}
183
184bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
185 const auto path = GetRelativePathFromNcaID(id, false, true, false);
186 const auto file = dir->GetFileRelative(path);
187
188 if (file == nullptr)
189 return false;
190
191 return file->WriteBytes(data, offset) == data.size();
192}
193
194bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
195 const NcaID& install) const {
196 const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
197 const auto file = dir->GetFileRelative(path);
198
199 if (file == nullptr)
200 return false;
201
202 const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
203
204 if (res != InstallResult::Success)
205 return false;
206
207 return Delete(placeholder);
208}
209
210bool PlaceholderCache::CleanAll() const {
211 return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
212}
213
214std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
215 const auto path = GetRelativePathFromNcaID(id, false, true, false);
216 const auto file = dir->GetFileRelative(path);
217
218 if (file == nullptr)
219 return std::nullopt;
220
221 NCA nca{file};
222
223 if (nca.GetStatus() != Loader::ResultStatus::Success &&
224 nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
225 return std::nullopt;
226 }
227
228 const auto rights_id = nca.GetRightsId();
229 if (rights_id == NcaID{})
230 return std::nullopt;
231
232 return rights_id;
233}
234
235u64 PlaceholderCache::Size(const NcaID& id) const {
236 const auto path = GetRelativePathFromNcaID(id, false, true, false);
237 const auto file = dir->GetFileRelative(path);
238
239 if (file == nullptr)
240 return 0;
241
242 return file->GetSize();
243}
244
245bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
246 const auto path = GetRelativePathFromNcaID(id, false, true, false);
247 const auto file = dir->GetFileRelative(path);
248
249 if (file == nullptr)
250 return false;
251
252 return file->Resize(new_size);
253}
254
255std::vector<NcaID> PlaceholderCache::List() const {
256 std::vector<NcaID> out;
257 for (const auto& sdir : dir->GetSubdirectories()) {
258 for (const auto& file : sdir->GetFiles()) {
259 const auto name = file->GetName();
260 if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
261 name[35] == 'a') {
262 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
263 }
264 }
265 }
266 return out;
267}
268
269NcaID PlaceholderCache::Generate() {
270 std::random_device device;
271 std::mt19937 gen(device());
272 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
273
274 NcaID out{};
275
276 const auto v1 = distribution(gen);
277 const auto v2 = distribution(gen);
278 std::memcpy(out.data(), &v1, sizeof(u64));
279 std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
280
281 return out;
282}
283
130VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 284VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
131 std::string_view path) const { 285 std::string_view path) const {
132 const auto file = dir->GetFileRelative(path); 286 const auto file = dir->GetFileRelative(path);
@@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
169 323
170VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { 324VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
171 VirtualFile file; 325 VirtualFile file;
172 // Try all four modes of file storage: 326 // Try all five relevant modes of file storage:
173 // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir) 327 // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
174 // 00: /000000**/{:032X}.nca 328 // 000: /000000**/{:032X}.nca
175 // 01: /{:032X}.nca 329 // 010: /{:032X}.nca
176 // 10: /000000**/{:032x}.nca 330 // 100: /000000**/{:032x}.nca
177 // 11: /{:032x}.nca 331 // 110: /{:032x}.nca
178 for (u8 i = 0; i < 4; ++i) { 332 // 111: /{:032x}.cnmt.nca
179 const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); 333 for (u8 i = 0; i < 8; ++i) {
334 if ((i % 2) == 1 && i != 7)
335 continue;
336 const auto path =
337 GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
180 file = OpenFileOrDirectoryConcat(dir, path); 338 file = OpenFileOrDirectoryConcat(dir, path);
181 if (file != nullptr) 339 if (file != nullptr)
182 return file; 340 return file;
@@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
472 memcpy(id.data(), hash.data(), 16); 630 memcpy(id.data(), hash.data(), 16);
473 } 631 }
474 632
475 std::string path = GetRelativePathFromNcaID(id, false, true); 633 std::string path = GetRelativePathFromNcaID(id, false, true, false);
476 634
477 if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { 635 if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
478 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); 636 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 4398d63e1..d1eec240e 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -25,6 +25,8 @@ enum class NCAContentType : u8;
25enum class TitleType : u8; 25enum class TitleType : u8;
26 26
27struct ContentRecord; 27struct ContentRecord;
28struct MetaRecord;
29class RegisteredCache;
28 30
29using NcaID = std::array<u8, 0x10>; 31using NcaID = std::array<u8, 0x10>;
30using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 32using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -89,6 +91,27 @@ protected:
89 Core::Crypto::KeyManager keys; 91 Core::Crypto::KeyManager keys;
90}; 92};
91 93
94class PlaceholderCache {
95public:
96 explicit PlaceholderCache(VirtualDir dir);
97
98 bool Create(const NcaID& id, u64 size) const;
99 bool Delete(const NcaID& id) const;
100 bool Exists(const NcaID& id) const;
101 bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
102 bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
103 bool CleanAll() const;
104 std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
105 u64 Size(const NcaID& id) const;
106 bool SetSize(const NcaID& id, u64 new_size) const;
107 std::vector<NcaID> List() const;
108
109 static NcaID Generate();
110
111private:
112 VirtualDir dir;
113};
114
92/* 115/*
93 * A class that catalogues NCAs in the registered directory structure. 116 * A class that catalogues NCAs in the registered directory structure.
94 * Nintendo's registered format follows this structure: 117 * Nintendo's registered format follows this structure:
@@ -103,6 +126,8 @@ protected:
103 * when 4GB splitting can be ignored.) 126 * when 4GB splitting can be ignored.)
104 */ 127 */
105class RegisteredCache : public ContentProvider { 128class RegisteredCache : public ContentProvider {
129 friend class PlaceholderCache;
130
106public: 131public:
107 // Parsing function defines the conversion from raw file to NCA. If there are other steps 132 // Parsing function defines the conversion from raw file to NCA. If there are other steps
108 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 133 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index b2ccb2926..84cd4684c 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -7,6 +7,7 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/card_image.h"
10#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
@@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
34 this->update_raw = std::move(update_raw); 35 this->update_raw = std::move(update_raw);
35} 36}
36 37
37ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 38ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
38 if (!updatable) 39 if (!updatable)
39 return MakeResult<VirtualFile>(file); 40 return MakeResult<VirtualFile>(file);
40 41
@@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
43 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); 44 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
44} 45}
45 46
46ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { 47ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
48 ContentRecordType type) const {
47 std::shared_ptr<NCA> res; 49 std::shared_ptr<NCA> res;
48 50
49 switch (storage) { 51 switch (storage) {
@@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
51 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); 53 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
52 break; 54 break;
53 case StorageId::NandSystem: 55 case StorageId::NandSystem:
54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); 56 res =
57 Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
58 title_id, type);
55 break; 59 break;
56 case StorageId::NandUser: 60 case StorageId::NandUser:
57 res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); 61 res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
62 title_id, type);
58 break; 63 break;
59 case StorageId::SdCard: 64 case StorageId::SdCard:
60 res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); 65 res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
66 title_id, type);
61 break; 67 break;
62 default: 68 default:
63 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); 69 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 7724c0b23..da63a313a 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -33,8 +33,8 @@ public:
33 ~RomFSFactory(); 33 ~RomFSFactory();
34 34
35 void SetPackedUpdate(VirtualFile update_raw); 35 void SetPackedUpdate(VirtualFile update_raw);
36 ResultVal<VirtualFile> OpenCurrentProcess(); 36 ResultVal<VirtualFile> OpenCurrentProcess() const;
37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); 37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
38 38
39private: 39private:
40 VirtualFile file; 40 VirtualFile file;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 7974b031d..f77cc02ac 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -15,22 +15,8 @@ namespace FileSys {
15 15
16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; 16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
17 17
18std::string SaveDataDescriptor::DebugInfo() const { 18namespace {
19 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " 19void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
20 "rank={}, index={}]",
21 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
22 static_cast<u8>(rank), index);
23}
24
25SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
26 // Delete all temporary storages
27 // On hardware, it is expected that temporary storage be empty at first use.
28 dir->DeleteSubdirectoryRecursive("temp");
29}
30
31SaveDataFactory::~SaveDataFactory() = default;
32
33ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
34 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 20 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
35 if (meta.zero_1 != 0) { 21 if (meta.zero_1 != 0) {
36 LOG_WARNING(Service_FS, 22 LOG_WARNING(Service_FS,
@@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
65 "non-zero ({:016X}{:016X})", 51 "non-zero ({:016X}{:016X})",
66 meta.user_id[1], meta.user_id[0]); 52 meta.user_id[1], meta.user_id[0]);
67 } 53 }
54}
55} // Anonymous namespace
68 56
69 std::string save_directory = 57std::string SaveDataDescriptor::DebugInfo() const {
70 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 58 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
59 "save_id={:016X}, "
60 "rank={}, index={}]",
61 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
62 static_cast<u8>(rank), index);
63}
71 64
72 // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. 65SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
73 // But, user_ids don't match so this works for now. 66 // Delete all temporary storages
67 // On hardware, it is expected that temporary storage be empty at first use.
68 dir->DeleteSubdirectoryRecursive("temp");
69}
74 70
75 auto out = dir->GetDirectoryRelative(save_directory); 71SaveDataFactory::~SaveDataFactory() = default;
72
73ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
74 const SaveDataDescriptor& meta) const {
75 PrintSaveDataDescriptorWarnings(meta);
76
77 const auto save_directory =
78 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
79
80 auto out = dir->CreateDirectoryRelative(save_directory);
76 81
82 // Return an error if the save data doesn't actually exist.
77 if (out == nullptr) { 83 if (out == nullptr) {
78 // TODO(bunnei): This is a work-around to always create a save data directory if it does not 84 // TODO(DarkLordZach): Find out correct error code.
79 // already exist. This is a hack, as we do not understand yet how this works on hardware. 85 return ResultCode(-1);
80 // Without a save data directory, many games will assert on boot. This should not have any
81 // bad side-effects.
82 out = dir->CreateDirectoryRelative(save_directory);
83 } 86 }
84 87
88 return MakeResult<VirtualDir>(std::move(out));
89}
90
91ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
92 const SaveDataDescriptor& meta) const {
93
94 const auto save_directory =
95 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
96
97 auto out = dir->GetDirectoryRelative(save_directory);
98
85 // Return an error if the save data doesn't actually exist. 99 // Return an error if the save data doesn't actually exist.
86 if (out == nullptr) { 100 if (out == nullptr) {
87 // TODO(Subv): Find out correct error code. 101 // TODO(Subv): Find out correct error code.
@@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
152} 166}
153 167
154void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 168void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
155 SaveDataSize new_value) { 169 SaveDataSize new_value) const {
156 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 170 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
157 const auto dir = GetOrCreateDirectoryRelative(this->dir, path); 171 const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
158 172
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index b73654571..991e57aa1 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -64,7 +64,8 @@ public:
64 explicit SaveDataFactory(VirtualDir dir); 64 explicit SaveDataFactory(VirtualDir dir);
65 ~SaveDataFactory(); 65 ~SaveDataFactory();
66 66
67 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); 67 ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
68 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
68 69
69 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 70 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
70 71
@@ -73,7 +74,8 @@ public:
73 u128 user_id, u64 save_id); 74 u128 user_id, u64 save_id);
74 75
75 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; 76 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
76 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); 77 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
78 SaveDataSize new_value) const;
77 79
78private: 80private:
79 VirtualDir dir; 81 VirtualDir dir;
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index bd3a57058..5113a1ca6 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -6,6 +6,7 @@
6#include "core/file_sys/registered_cache.h" 6#include "core/file_sys/registered_cache.h"
7#include "core/file_sys/sdmc_factory.h" 7#include "core/file_sys/sdmc_factory.h"
8#include "core/file_sys/xts_archive.h" 8#include "core/file_sys/xts_archive.h"
9#include "core/settings.h"
9 10
10namespace FileSys { 11namespace FileSys {
11 12
@@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 15 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 16 [](const VirtualFile& file, const NcaID& id) {
16 return NAX{file, id}.GetDecrypted(); 17 return NAX{file, id}.GetDecrypted();
17 })) {} 18 })),
19 placeholder(std::make_unique<PlaceholderCache>(
20 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
18 21
19SDMCFactory::~SDMCFactory() = default; 22SDMCFactory::~SDMCFactory() = default;
20 23
21ResultVal<VirtualDir> SDMCFactory::Open() { 24ResultVal<VirtualDir> SDMCFactory::Open() const {
22 return MakeResult<VirtualDir>(dir); 25 return MakeResult<VirtualDir>(dir);
23} 26}
24 27
28VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
29 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
30}
31
25RegisteredCache* SDMCFactory::GetSDMCContents() const { 32RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents.get(); 33 return contents.get();
27} 34}
28 35
36PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
37 return placeholder.get();
38}
39
40VirtualDir SDMCFactory::GetImageDirectory() const {
41 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
42}
43
44u64 SDMCFactory::GetSDMCFreeSpace() const {
45 return GetSDMCTotalSpace() - dir->GetSize();
46}
47
48u64 SDMCFactory::GetSDMCTotalSpace() const {
49 return static_cast<u64>(Settings::values.sdmc_size);
50}
51
29} // namespace FileSys 52} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 42794ba5b..42dc4e08a 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -11,6 +11,7 @@
11namespace FileSys { 11namespace FileSys {
12 12
13class RegisteredCache; 13class RegisteredCache;
14class PlaceholderCache;
14 15
15/// File system interface to the SDCard archive 16/// File system interface to the SDCard archive
16class SDMCFactory { 17class SDMCFactory {
@@ -18,13 +19,23 @@ public:
18 explicit SDMCFactory(VirtualDir dir); 19 explicit SDMCFactory(VirtualDir dir);
19 ~SDMCFactory(); 20 ~SDMCFactory();
20 21
21 ResultVal<VirtualDir> Open(); 22 ResultVal<VirtualDir> Open() const;
23
24 VirtualDir GetSDMCContentDirectory() const;
25
22 RegisteredCache* GetSDMCContents() const; 26 RegisteredCache* GetSDMCContents() const;
27 PlaceholderCache* GetSDMCPlaceholder() const;
28
29 VirtualDir GetImageDirectory() const;
30
31 u64 GetSDMCFreeSpace() const;
32 u64 GetSDMCTotalSpace() const;
23 33
24private: 34private:
25 VirtualDir dir; 35 VirtualDir dir;
26 36
27 std::unique_ptr<RegisteredCache> contents; 37 std::unique_ptr<RegisteredCache> contents;
38 std::unique_ptr<PlaceholderCache> placeholder;
28}; 39};
29 40
30} // namespace FileSys 41} // namespace FileSys
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 8b3b14e25..ef3084681 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -14,6 +14,7 @@
14#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/nca_metadata.h" 15#include "core/file_sys/nca_metadata.h"
16#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
17#include "core/file_sys/program_metadata.h"
17#include "core/file_sys/submission_package.h" 18#include "core/file_sys/submission_package.h"
18#include "core/loader/loader.h" 19#include "core/loader/loader.h"
19 20
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
78} 79}
79 80
80Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { 81Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
82 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
83 return Loader::ResultStatus::Success;
84 }
85
81 const auto iter = program_status.find(title_id); 86 const auto iter = program_status.find(title_id);
82 if (iter == program_status.end()) 87 if (iter == program_status.end())
83 return Loader::ResultStatus::ErrorNSPMissingProgramNCA; 88 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
85} 90}
86 91
87u64 NSP::GetFirstTitleID() const { 92u64 NSP::GetFirstTitleID() const {
93 if (IsExtractedType()) {
94 return GetProgramTitleID();
95 }
96
88 if (program_status.empty()) 97 if (program_status.empty())
89 return 0; 98 return 0;
90 return program_status.begin()->first; 99 return program_status.begin()->first;
91} 100}
92 101
93u64 NSP::GetProgramTitleID() const { 102u64 NSP::GetProgramTitleID() const {
103 if (IsExtractedType()) {
104 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
105 return 0;
106 }
107
108 ProgramMetadata meta;
109 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
110 return meta.GetTitleID();
111 } else {
112 return 0;
113 }
114 }
115
94 const auto out = GetFirstTitleID(); 116 const auto out = GetFirstTitleID();
95 if ((out & 0x800) == 0) 117 if ((out & 0x800) == 0)
96 return out; 118 return out;
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
102} 124}
103 125
104std::vector<u64> NSP::GetTitleIDs() const { 126std::vector<u64> NSP::GetTitleIDs() const {
127 if (IsExtractedType()) {
128 return {GetProgramTitleID()};
129 }
130
105 std::vector<u64> out; 131 std::vector<u64> out;
106 out.reserve(ncas.size()); 132 out.reserve(ncas.size());
107 for (const auto& kv : ncas) 133 for (const auto& kv : ncas)
@@ -222,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
222 248
223void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { 249void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
224 for (const auto& outer_file : files) { 250 for (const auto& outer_file : files) {
225 if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { 251 if (outer_file->GetName().size() < 9 ||
252 outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
226 continue; 253 continue;
227 } 254 }
228 255
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 aa2c83937..797c9a06f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -232,12 +232,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
232 232
233IDebugFunctions::~IDebugFunctions() = default; 233IDebugFunctions::~IDebugFunctions() = default;
234 234
235ISelfController::ISelfController(Core::System& system_, 235ISelfController::ISelfController(Core::System& system,
236 std::shared_ptr<NVFlinger::NVFlinger> nvflinger_) 236 std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
237 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { 237 : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
238 // clang-format off 238 // clang-format off
239 static const FunctionInfo functions[] = { 239 static const FunctionInfo functions[] = {
240 {0, nullptr, "Exit"}, 240 {0, &ISelfController::Exit, "Exit"},
241 {1, &ISelfController::LockExit, "LockExit"}, 241 {1, &ISelfController::LockExit, "LockExit"},
242 {2, &ISelfController::UnlockExit, "UnlockExit"}, 242 {2, &ISelfController::UnlockExit, "UnlockExit"},
243 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, 243 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
@@ -282,7 +282,7 @@ ISelfController::ISelfController(Core::System& system_,
282 282
283 RegisterHandlers(functions); 283 RegisterHandlers(functions);
284 284
285 auto& kernel = system_.Kernel(); 285 auto& kernel = system.Kernel();
286 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 286 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
287 "ISelfController:LaunchableEvent"); 287 "ISelfController:LaunchableEvent");
288 288
@@ -298,15 +298,28 @@ ISelfController::ISelfController(Core::System& system_,
298 298
299ISelfController::~ISelfController() = default; 299ISelfController::~ISelfController() = default;
300 300
301void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
302 LOG_DEBUG(Service_AM, "called");
303
304 system.Shutdown();
305
306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(RESULT_SUCCESS);
308}
309
301void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { 310void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
302 LOG_WARNING(Service_AM, "(STUBBED) called"); 311 LOG_DEBUG(Service_AM, "called");
312
313 system.SetExitLock(true);
303 314
304 IPC::ResponseBuilder rb{ctx, 2}; 315 IPC::ResponseBuilder rb{ctx, 2};
305 rb.Push(RESULT_SUCCESS); 316 rb.Push(RESULT_SUCCESS);
306} 317}
307 318
308void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { 319void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
309 LOG_WARNING(Service_AM, "(STUBBED) called"); 320 LOG_DEBUG(Service_AM, "called");
321
322 system.SetExitLock(false);
310 323
311 IPC::ResponseBuilder rb{ctx, 2}; 324 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(RESULT_SUCCESS); 325 rb.Push(RESULT_SUCCESS);
@@ -550,6 +563,10 @@ void AppletMessageQueue::OperationModeChanged() {
550 on_operation_mode_changed.writable->Signal(); 563 on_operation_mode_changed.writable->Signal();
551} 564}
552 565
566void AppletMessageQueue::RequestExit() {
567 PushMessage(AppletMessage::ExitRequested);
568}
569
553ICommonStateGetter::ICommonStateGetter(Core::System& system, 570ICommonStateGetter::ICommonStateGetter(Core::System& system,
554 std::shared_ptr<AppletMessageQueue> msg_queue) 571 std::shared_ptr<AppletMessageQueue> msg_queue)
555 : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { 572 : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) {
@@ -1066,7 +1083,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1066 1083
1067 RegisterHandlers(functions); 1084 RegisterHandlers(functions);
1068 1085
1069 auto& kernel = Core::System::GetInstance().Kernel(); 1086 auto& kernel = system.Kernel();
1070 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( 1087 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
1071 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); 1088 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
1072} 1089}
@@ -1143,13 +1160,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
1143 1160
1144void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { 1161void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
1145 IPC::RequestParser rp{ctx}; 1162 IPC::RequestParser rp{ctx};
1146 u128 uid = rp.PopRaw<u128>(); // What does this do? 1163 u128 user_id = rp.PopRaw<u128>();
1147 LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); 1164
1165 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
1166
1167 FileSys::SaveDataDescriptor descriptor{};
1168 descriptor.title_id = Core::CurrentProcess()->GetTitleID();
1169 descriptor.user_id = user_id;
1170 descriptor.type = FileSys::SaveDataType::SaveData;
1171 const auto res = system.GetFileSystemController().CreateSaveData(
1172 FileSys::SaveDataSpaceId::NandUser, descriptor);
1148 1173
1149 IPC::ResponseBuilder rb{ctx, 4}; 1174 IPC::ResponseBuilder rb{ctx, 4};
1150 rb.Push(RESULT_SUCCESS); 1175 rb.Push(res.Code());
1151 rb.Push<u64>(0); 1176 rb.Push<u64>(0);
1152} // namespace Service::AM 1177}
1153 1178
1154void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 1179void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
1155 // Takes an input u32 Result, no output. 1180 // Takes an input u32 Result, no output.
@@ -1261,8 +1286,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
1261 "new_journal={:016X}", 1286 "new_journal={:016X}",
1262 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); 1287 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
1263 1288
1264 const auto title_id = system.CurrentProcess()->GetTitleID(); 1289 system.GetFileSystemController().WriteSaveDataSize(
1265 FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); 1290 type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
1266 1291
1267 IPC::ResponseBuilder rb{ctx, 4}; 1292 IPC::ResponseBuilder rb{ctx, 4};
1268 rb.Push(RESULT_SUCCESS); 1293 rb.Push(RESULT_SUCCESS);
@@ -1281,8 +1306,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
1281 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), 1306 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
1282 user_id[1], user_id[0]); 1307 user_id[1], user_id[0]);
1283 1308
1284 const auto title_id = system.CurrentProcess()->GetTitleID(); 1309 const auto size = system.GetFileSystemController().ReadSaveDataSize(
1285 const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); 1310 type, system.CurrentProcess()->GetTitleID(), user_id);
1286 1311
1287 IPC::ResponseBuilder rb{ctx, 6}; 1312 IPC::ResponseBuilder rb{ctx, 6};
1288 rb.Push(RESULT_SUCCESS); 1313 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 28f870302..a3baeb673 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -45,6 +45,7 @@ class AppletMessageQueue {
45public: 45public:
46 enum class AppletMessage : u32 { 46 enum class AppletMessage : u32 {
47 NoMessage = 0, 47 NoMessage = 0,
48 ExitRequested = 4,
48 FocusStateChanged = 15, 49 FocusStateChanged = 15,
49 OperationModeChanged = 30, 50 OperationModeChanged = 30,
50 PerformanceModeChanged = 31, 51 PerformanceModeChanged = 31,
@@ -59,6 +60,7 @@ public:
59 AppletMessage PopMessage(); 60 AppletMessage PopMessage();
60 std::size_t GetMessageCount() const; 61 std::size_t GetMessageCount() const;
61 void OperationModeChanged(); 62 void OperationModeChanged();
63 void RequestExit();
62 64
63private: 65private:
64 std::queue<AppletMessage> messages; 66 std::queue<AppletMessage> messages;
@@ -123,6 +125,7 @@ public:
123 ~ISelfController() override; 125 ~ISelfController() override;
124 126
125private: 127private:
128 void Exit(Kernel::HLERequestContext& ctx);
126 void LockExit(Kernel::HLERequestContext& ctx); 129 void LockExit(Kernel::HLERequestContext& ctx);
127 void UnlockExit(Kernel::HLERequestContext& ctx); 130 void UnlockExit(Kernel::HLERequestContext& ctx);
128 void EnterFatalSection(Kernel::HLERequestContext& ctx); 131 void EnterFatalSection(Kernel::HLERequestContext& ctx);
@@ -151,6 +154,8 @@ private:
151 u32 idle_time_detection_extension = 0; 154 u32 idle_time_detection_extension = 0;
152 u64 num_fatal_sections_entered = 0; 155 u64 num_fatal_sections_entered = 0;
153 bool is_auto_sleep_disabled = false; 156 bool is_auto_sleep_disabled = false;
157
158 Core::System& system;
154}; 159};
155 160
156class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 161class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 9e006cd9d..2e3e45915 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -9,12 +9,18 @@
9#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10 10
11namespace Service { 11namespace Service {
12namespace FileSystem {
13class FileSystemController;
14}
15
12namespace NVFlinger { 16namespace NVFlinger {
13class NVFlinger; 17class NVFlinger;
14} 18}
15 19
16namespace AM { 20namespace AM {
17 21
22class AppletMessageQueue;
23
18class AppletAE final : public ServiceFramework<AppletAE> { 24class AppletAE final : public ServiceFramework<AppletAE> {
19public: 25public:
20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 22c05419d..758da792d 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -9,12 +9,18 @@
9#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10 10
11namespace Service { 11namespace Service {
12namespace FileSystem {
13class FileSystemController;
14}
15
12namespace NVFlinger { 16namespace NVFlinger {
13class NVFlinger; 17class NVFlinger;
14} 18}
15 19
16namespace AM { 20namespace AM {
17 21
22class AppletMessageQueue;
23
18class AppletOE final : public ServiceFramework<AppletOE> { 24class AppletOE final : public ServiceFramework<AppletOE> {
19public: 25public:
20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
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/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 8ce110dd1..14cd0e322 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -8,6 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/bis_factory.h" 10#include "core/file_sys/bis_factory.h"
11#include "core/file_sys/card_image.h"
11#include "core/file_sys/control_metadata.h" 12#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/errors.h" 13#include "core/file_sys/errors.h"
13#include "core/file_sys/mode.h" 14#include "core/file_sys/mode.h"
@@ -25,14 +26,10 @@
25#include "core/hle/service/filesystem/fsp_pr.h" 26#include "core/hle/service/filesystem/fsp_pr.h"
26#include "core/hle/service/filesystem/fsp_srv.h" 27#include "core/hle/service/filesystem/fsp_srv.h"
27#include "core/loader/loader.h" 28#include "core/loader/loader.h"
29#include "core/settings.h"
28 30
29namespace Service::FileSystem { 31namespace Service::FileSystem {
30 32
31// Size of emulated sd card free space, reported in bytes.
32// Just using 32GB because thats reasonable
33// TODO(DarkLordZach): Eventually make this configurable in settings.
34constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
35
36// A default size for normal/journal save data size if application control metadata cannot be found. 33// A default size for normal/journal save data size if application control metadata cannot be found.
37// This should be large enough to satisfy even the most extreme requirements (~4.2GB) 34// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
38constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; 35constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
@@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
226 return MakeResult(dir); 223 return MakeResult(dir);
227} 224}
228 225
229u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
230 if (backing->IsWritable())
231 return EMULATED_SD_REPORTED_SIZE;
232
233 return 0;
234}
235
236ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 226ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
237 const std::string& path_) const { 227 const std::string& path_) const {
238 std::string path(FileUtil::SanitizePath(path_)); 228 std::string path(FileUtil::SanitizePath(path_));
@@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
251 return FileSys::ERROR_PATH_NOT_FOUND; 241 return FileSys::ERROR_PATH_NOT_FOUND;
252} 242}
253 243
254/** 244FileSystemController::FileSystemController() = default;
255 * Map of registered file systems, identified by type. Once an file system is registered here, it 245
256 * is never removed until UnregisterFileSystems is called. 246FileSystemController::~FileSystemController() = default;
257 */
258static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
259static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
260static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
261static std::unique_ptr<FileSys::BISFactory> bis_factory;
262 247
263ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { 248ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
264 ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
265 romfs_factory = std::move(factory); 249 romfs_factory = std::move(factory);
266 LOG_DEBUG(Service_FS, "Registered RomFS"); 250 LOG_DEBUG(Service_FS, "Registered RomFS");
267 return RESULT_SUCCESS; 251 return RESULT_SUCCESS;
268} 252}
269 253
270ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { 254ResultCode FileSystemController::RegisterSaveData(
271 ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data"); 255 std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
256 ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
272 save_data_factory = std::move(factory); 257 save_data_factory = std::move(factory);
273 LOG_DEBUG(Service_FS, "Registered save data"); 258 LOG_DEBUG(Service_FS, "Registered save data");
274 return RESULT_SUCCESS; 259 return RESULT_SUCCESS;
275} 260}
276 261
277ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { 262ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
278 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); 263 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
279 sdmc_factory = std::move(factory); 264 sdmc_factory = std::move(factory);
280 LOG_DEBUG(Service_FS, "Registered SDMC"); 265 LOG_DEBUG(Service_FS, "Registered SDMC");
281 return RESULT_SUCCESS; 266 return RESULT_SUCCESS;
282} 267}
283 268
284ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { 269ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
285 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); 270 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
286 bis_factory = std::move(factory); 271 bis_factory = std::move(factory);
287 LOG_DEBUG(Service_FS, "Registered BIS"); 272 LOG_DEBUG(Service_FS, "Registered BIS");
288 return RESULT_SUCCESS; 273 return RESULT_SUCCESS;
289} 274}
290 275
291void SetPackedUpdate(FileSys::VirtualFile update_raw) { 276void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
292 LOG_TRACE(Service_FS, "Setting packed update for romfs"); 277 LOG_TRACE(Service_FS, "Setting packed update for romfs");
293 278
294 if (romfs_factory == nullptr) 279 if (romfs_factory == nullptr)
@@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) {
297 romfs_factory->SetPackedUpdate(std::move(update_raw)); 282 romfs_factory->SetPackedUpdate(std::move(update_raw));
298} 283}
299 284
300ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { 285ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
301 LOG_TRACE(Service_FS, "Opening RomFS for current process"); 286 LOG_TRACE(Service_FS, "Opening RomFS for current process");
302 287
303 if (romfs_factory == nullptr) { 288 if (romfs_factory == nullptr) {
@@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
308 return romfs_factory->OpenCurrentProcess(); 293 return romfs_factory->OpenCurrentProcess();
309} 294}
310 295
311ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, 296ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
312 FileSys::ContentRecordType type) { 297 u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
313 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", 298 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
314 title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); 299 title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
315 300
@@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
321 return romfs_factory->Open(title_id, storage_id, type); 306 return romfs_factory->Open(title_id, storage_id, type);
322} 307}
323 308
324ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 309ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
325 const FileSys::SaveDataDescriptor& descriptor) { 310 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
311 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
312 static_cast<u8>(space), save_struct.DebugInfo());
313
314 if (save_data_factory == nullptr) {
315 return FileSys::ERROR_ENTITY_NOT_FOUND;
316 }
317
318 return save_data_factory->Create(space, save_struct);
319}
320
321ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
322 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
326 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", 323 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
327 static_cast<u8>(space), descriptor.DebugInfo()); 324 static_cast<u8>(space), descriptor.DebugInfo());
328 325
@@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
333 return save_data_factory->Open(space, descriptor); 330 return save_data_factory->Open(space, descriptor);
334} 331}
335 332
336ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { 333ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
334 FileSys::SaveDataSpaceId space) const {
337 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); 335 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
338 336
339 if (save_data_factory == nullptr) { 337 if (save_data_factory == nullptr) {
@@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
343 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); 341 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
344} 342}
345 343
346ResultVal<FileSys::VirtualDir> OpenSDMC() { 344ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
347 LOG_TRACE(Service_FS, "Opening SDMC"); 345 LOG_TRACE(Service_FS, "Opening SDMC");
348 346
349 if (sdmc_factory == nullptr) { 347 if (sdmc_factory == nullptr) {
@@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
353 return sdmc_factory->Open(); 351 return sdmc_factory->Open();
354} 352}
355 353
356FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { 354ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
355 FileSys::BisPartitionId id) const {
356 LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
357
358 if (bis_factory == nullptr) {
359 return FileSys::ERROR_ENTITY_NOT_FOUND;
360 }
361
362 auto part = bis_factory->OpenPartition(id);
363 if (part == nullptr) {
364 return FileSys::ERROR_INVALID_ARGUMENT;
365 }
366
367 return MakeResult<FileSys::VirtualDir>(std::move(part));
368}
369
370ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
371 FileSys::BisPartitionId id) const {
372 LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
373
374 if (bis_factory == nullptr) {
375 return FileSys::ERROR_ENTITY_NOT_FOUND;
376 }
377
378 auto part = bis_factory->OpenPartitionStorage(id);
379 if (part == nullptr) {
380 return FileSys::ERROR_INVALID_ARGUMENT;
381 }
382
383 return MakeResult<FileSys::VirtualFile>(std::move(part));
384}
385
386u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
387 switch (id) {
388 case FileSys::StorageId::None:
389 case FileSys::StorageId::GameCard:
390 return 0;
391 case FileSys::StorageId::SdCard:
392 if (sdmc_factory == nullptr)
393 return 0;
394 return sdmc_factory->GetSDMCFreeSpace();
395 case FileSys::StorageId::Host:
396 if (bis_factory == nullptr)
397 return 0;
398 return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
399 case FileSys::StorageId::NandSystem:
400 if (bis_factory == nullptr)
401 return 0;
402 return bis_factory->GetSystemNANDFreeSpace();
403 case FileSys::StorageId::NandUser:
404 if (bis_factory == nullptr)
405 return 0;
406 return bis_factory->GetUserNANDFreeSpace();
407 }
408
409 return 0;
410}
411
412u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
413 switch (id) {
414 case FileSys::StorageId::None:
415 case FileSys::StorageId::GameCard:
416 return 0;
417 case FileSys::StorageId::SdCard:
418 if (sdmc_factory == nullptr)
419 return 0;
420 return sdmc_factory->GetSDMCTotalSpace();
421 case FileSys::StorageId::Host:
422 if (bis_factory == nullptr)
423 return 0;
424 return bis_factory->GetFullNANDTotalSpace();
425 case FileSys::StorageId::NandSystem:
426 if (bis_factory == nullptr)
427 return 0;
428 return bis_factory->GetSystemNANDTotalSpace();
429 case FileSys::StorageId::NandUser:
430 if (bis_factory == nullptr)
431 return 0;
432 return bis_factory->GetUserNANDTotalSpace();
433 }
434
435 return 0;
436}
437
438FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
439 u64 title_id, u128 user_id) const {
357 if (save_data_factory == nullptr) { 440 if (save_data_factory == nullptr) {
358 return {0, 0}; 441 return {0, 0};
359 } 442 }
@@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
385 return value; 468 return value;
386} 469}
387 470
388void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, 471void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
389 FileSys::SaveDataSize new_value) { 472 FileSys::SaveDataSize new_value) const {
390 if (save_data_factory != nullptr) 473 if (save_data_factory != nullptr)
391 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); 474 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
392} 475}
393 476
394FileSys::RegisteredCache* GetSystemNANDContents() { 477void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
478 gamecard = std::make_unique<FileSys::XCI>(file);
479 const auto dir = gamecard->ConcatenatedPseudoDirectory();
480 gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
481 gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
482}
483
484FileSys::XCI* FileSystemController::GetGameCard() const {
485 return gamecard.get();
486}
487
488FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
489 return gamecard_registered.get();
490}
491
492FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
493 return gamecard_placeholder.get();
494}
495
496FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
395 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 497 LOG_TRACE(Service_FS, "Opening System NAND Contents");
396 498
397 if (bis_factory == nullptr) 499 if (bis_factory == nullptr)
@@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() {
400 return bis_factory->GetSystemNANDContents(); 502 return bis_factory->GetSystemNANDContents();
401} 503}
402 504
403FileSys::RegisteredCache* GetUserNANDContents() { 505FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
404 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 506 LOG_TRACE(Service_FS, "Opening User NAND Contents");
405 507
406 if (bis_factory == nullptr) 508 if (bis_factory == nullptr)
@@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() {
409 return bis_factory->GetUserNANDContents(); 511 return bis_factory->GetUserNANDContents();
410} 512}
411 513
412FileSys::RegisteredCache* GetSDMCContents() { 514FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
413 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 515 LOG_TRACE(Service_FS, "Opening SDMC Contents");
414 516
415 if (sdmc_factory == nullptr) 517 if (sdmc_factory == nullptr)
@@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() {
418 return sdmc_factory->GetSDMCContents(); 520 return sdmc_factory->GetSDMCContents();
419} 521}
420 522
421FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { 523FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
524 LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
525
526 if (bis_factory == nullptr)
527 return nullptr;
528
529 return bis_factory->GetSystemNANDPlaceholder();
530}
531
532FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
533 LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
534
535 if (bis_factory == nullptr)
536 return nullptr;
537
538 return bis_factory->GetUserNANDPlaceholder();
539}
540
541FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
542 LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
543
544 if (sdmc_factory == nullptr)
545 return nullptr;
546
547 return sdmc_factory->GetSDMCPlaceholder();
548}
549
550FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
551 FileSys::StorageId id) const {
552 switch (id) {
553 case FileSys::StorageId::None:
554 case FileSys::StorageId::Host:
555 UNIMPLEMENTED();
556 return nullptr;
557 case FileSys::StorageId::GameCard:
558 return GetGameCardContents();
559 case FileSys::StorageId::NandSystem:
560 return GetSystemNANDContents();
561 case FileSys::StorageId::NandUser:
562 return GetUserNANDContents();
563 case FileSys::StorageId::SdCard:
564 return GetSDMCContents();
565 }
566
567 return nullptr;
568}
569
570FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
571 FileSys::StorageId id) const {
572 switch (id) {
573 case FileSys::StorageId::None:
574 case FileSys::StorageId::Host:
575 UNIMPLEMENTED();
576 return nullptr;
577 case FileSys::StorageId::GameCard:
578 return GetGameCardPlaceholder();
579 case FileSys::StorageId::NandSystem:
580 return GetSystemNANDPlaceholder();
581 case FileSys::StorageId::NandUser:
582 return GetUserNANDPlaceholder();
583 case FileSys::StorageId::SdCard:
584 return GetSDMCPlaceholder();
585 }
586
587 return nullptr;
588}
589
590FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
591 LOG_TRACE(Service_FS, "Opening system NAND content directory");
592
593 if (bis_factory == nullptr)
594 return nullptr;
595
596 return bis_factory->GetSystemNANDContentDirectory();
597}
598
599FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
600 LOG_TRACE(Service_FS, "Opening user NAND content directory");
601
602 if (bis_factory == nullptr)
603 return nullptr;
604
605 return bis_factory->GetUserNANDContentDirectory();
606}
607
608FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
609 LOG_TRACE(Service_FS, "Opening SDMC content directory");
610
611 if (sdmc_factory == nullptr)
612 return nullptr;
613
614 return sdmc_factory->GetSDMCContentDirectory();
615}
616
617FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
618 LOG_TRACE(Service_FS, "Opening NAND image directory");
619
620 if (bis_factory == nullptr)
621 return nullptr;
622
623 return bis_factory->GetImageDirectory();
624}
625
626FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
627 LOG_TRACE(Service_FS, "Opening SDMC image directory");
628
629 if (sdmc_factory == nullptr)
630 return nullptr;
631
632 return sdmc_factory->GetImageDirectory();
633}
634
635FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
636 switch (id) {
637 case ContentStorageId::System:
638 return GetSystemNANDContentDirectory();
639 case ContentStorageId::User:
640 return GetUserNANDContentDirectory();
641 case ContentStorageId::SdCard:
642 return GetSDMCContentDirectory();
643 }
644
645 return nullptr;
646}
647
648FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
649 switch (id) {
650 case ImageDirectoryId::NAND:
651 return GetNANDImageDirectory();
652 case ImageDirectoryId::SdCard:
653 return GetSDMCImageDirectory();
654 }
655
656 return nullptr;
657}
658
659FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
422 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); 660 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
423 661
424 if (bis_factory == nullptr) 662 if (bis_factory == nullptr)
@@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
427 return bis_factory->GetModificationLoadRoot(title_id); 665 return bis_factory->GetModificationLoadRoot(title_id);
428} 666}
429 667
430FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { 668FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
431 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); 669 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
432 670
433 if (bis_factory == nullptr) 671 if (bis_factory == nullptr)
@@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
436 return bis_factory->GetModificationDumpRoot(title_id); 674 return bis_factory->GetModificationDumpRoot(title_id);
437} 675}
438 676
439void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 677void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
440 if (overwrite) { 678 if (overwrite) {
441 bis_factory = nullptr; 679 bis_factory = nullptr;
442 save_data_factory = nullptr; 680 save_data_factory = nullptr;
@@ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
473} 711}
474 712
475void InstallInterfaces(Core::System& system) { 713void InstallInterfaces(Core::System& system) {
476 romfs_factory = nullptr;
477 CreateFactories(*system.GetFilesystem(), false);
478 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); 714 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
479 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); 715 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
480 std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager()); 716 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
717 ->InstallAsService(system.ServiceManager());
481} 718}
482 719
483} // namespace Service::FileSystem 720} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3849dd89e..3e0c03ec0 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -14,10 +14,13 @@ namespace FileSys {
14class BISFactory; 14class BISFactory;
15class RegisteredCache; 15class RegisteredCache;
16class RegisteredCacheUnion; 16class RegisteredCacheUnion;
17class PlaceholderCache;
17class RomFSFactory; 18class RomFSFactory;
18class SaveDataFactory; 19class SaveDataFactory;
19class SDMCFactory; 20class SDMCFactory;
21class XCI;
20 22
23enum class BisPartitionId : u32;
21enum class ContentRecordType : u8; 24enum class ContentRecordType : u8;
22enum class Mode : u32; 25enum class Mode : u32;
23enum class SaveDataSpaceId : u8; 26enum class SaveDataSpaceId : u8;
@@ -36,34 +39,91 @@ class ServiceManager;
36 39
37namespace FileSystem { 40namespace FileSystem {
38 41
39ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); 42enum class ContentStorageId : u32 {
40ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); 43 System,
41ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); 44 User,
42ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 45 SdCard,
43 46};
44void SetPackedUpdate(FileSys::VirtualFile update_raw);
45ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
46ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
47 FileSys::ContentRecordType type);
48ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
49 const FileSys::SaveDataDescriptor& descriptor);
50ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
51ResultVal<FileSys::VirtualDir> OpenSDMC();
52
53FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
55 FileSys::SaveDataSize new_value);
56 47
57FileSys::RegisteredCache* GetSystemNANDContents(); 48enum class ImageDirectoryId : u32 {
58FileSys::RegisteredCache* GetUserNANDContents(); 49 NAND,
59FileSys::RegisteredCache* GetSDMCContents(); 50 SdCard,
51};
60 52
61FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 53class FileSystemController {
62FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); 54public:
55 FileSystemController();
56 ~FileSystemController();
57
58 ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
59 ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
60 ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
61 ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
62
63 void SetPackedUpdate(FileSys::VirtualFile update_raw);
64 ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
65 ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
66 FileSys::ContentRecordType type) const;
67 ResultVal<FileSys::VirtualDir> CreateSaveData(
68 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
69 ResultVal<FileSys::VirtualDir> OpenSaveData(
70 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
71 ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
72 ResultVal<FileSys::VirtualDir> OpenSDMC() const;
73 ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
74 ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
75
76 u64 GetFreeSpaceSize(FileSys::StorageId id) const;
77 u64 GetTotalSpaceSize(FileSys::StorageId id) const;
78
79 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
80 u128 user_id) const;
81 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
82 FileSys::SaveDataSize new_value) const;
83
84 void SetGameCard(FileSys::VirtualFile file);
85 FileSys::XCI* GetGameCard() const;
86
87 FileSys::RegisteredCache* GetSystemNANDContents() const;
88 FileSys::RegisteredCache* GetUserNANDContents() const;
89 FileSys::RegisteredCache* GetSDMCContents() const;
90 FileSys::RegisteredCache* GetGameCardContents() const;
91
92 FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
93 FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
94 FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
95 FileSys::PlaceholderCache* GetGameCardPlaceholder() const;
96
97 FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
98 FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;
99
100 FileSys::VirtualDir GetSystemNANDContentDirectory() const;
101 FileSys::VirtualDir GetUserNANDContentDirectory() const;
102 FileSys::VirtualDir GetSDMCContentDirectory() const;
103
104 FileSys::VirtualDir GetNANDImageDirectory() const;
105 FileSys::VirtualDir GetSDMCImageDirectory() const;
106
107 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
108 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
109
110 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
111 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
112
113 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
114 // above is called.
115 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
63 116
64// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 117private:
65// above is called. 118 std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
66void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 119 std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
120 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
121 std::unique_ptr<FileSys::BISFactory> bis_factory;
122
123 std::unique_ptr<FileSys::XCI> gamecard;
124 std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
125 std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
126};
67 127
68void InstallInterfaces(Core::System& system); 128void InstallInterfaces(Core::System& system);
69 129
@@ -160,12 +220,6 @@ public:
160 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); 220 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
161 221
162 /** 222 /**
163 * Get the free space
164 * @return The number of free bytes in the archive
165 */
166 u64 GetFreeSpaceSize() const;
167
168 /**
169 * Get the type of the specified path 223 * Get the type of the specified path
170 * @return The type of the specified path or error code 224 * @return The type of the specified path or error code
171 */ 225 */
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d3cd46a9b..eb982ad49 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -19,6 +19,7 @@
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/nca_metadata.h" 20#include "core/file_sys/nca_metadata.h"
21#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.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"
23#include "core/file_sys/system_archive/system_archive.h" 24#include "core/file_sys/system_archive/system_archive.h"
24#include "core/file_sys/vfs.h" 25#include "core/file_sys/vfs.h"
@@ -30,6 +31,18 @@
30 31
31namespace Service::FileSystem { 32namespace Service::FileSystem {
32 33
34struct SizeGetter {
35 std::function<u64()> get_free_size;
36 std::function<u64()> get_total_size;
37
38 static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
39 return {
40 [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
41 [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
42 };
43 }
44};
45
33enum class FileSystemType : u8 { 46enum class FileSystemType : u8 {
34 Invalid0 = 0, 47 Invalid0 = 0,
35 Invalid1 = 1, 48 Invalid1 = 1,
@@ -289,8 +302,8 @@ private:
289 302
290class IFileSystem final : public ServiceFramework<IFileSystem> { 303class IFileSystem final : public ServiceFramework<IFileSystem> {
291public: 304public:
292 explicit IFileSystem(FileSys::VirtualDir backend) 305 explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
293 : ServiceFramework("IFileSystem"), backend(std::move(backend)) { 306 : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
294 static const FunctionInfo functions[] = { 307 static const FunctionInfo functions[] = {
295 {0, &IFileSystem::CreateFile, "CreateFile"}, 308 {0, &IFileSystem::CreateFile, "CreateFile"},
296 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 309 {1, &IFileSystem::DeleteFile, "DeleteFile"},
@@ -467,14 +480,31 @@ public:
467 rb.Push(RESULT_SUCCESS); 480 rb.Push(RESULT_SUCCESS);
468 } 481 }
469 482
483 void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
484 LOG_DEBUG(Service_FS, "called");
485
486 IPC::ResponseBuilder rb{ctx, 4};
487 rb.Push(RESULT_SUCCESS);
488 rb.Push(size.get_free_size());
489 }
490
491 void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
492 LOG_DEBUG(Service_FS, "called");
493
494 IPC::ResponseBuilder rb{ctx, 4};
495 rb.Push(RESULT_SUCCESS);
496 rb.Push(size.get_total_size());
497 }
498
470private: 499private:
471 VfsDirectoryServiceWrapper backend; 500 VfsDirectoryServiceWrapper backend;
501 SizeGetter size;
472}; 502};
473 503
474class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 504class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
475public: 505public:
476 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) 506 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
477 : ServiceFramework("ISaveDataInfoReader") { 507 : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
478 static const FunctionInfo functions[] = { 508 static const FunctionInfo functions[] = {
479 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, 509 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
480 }; 510 };
@@ -520,8 +550,13 @@ private:
520 } 550 }
521 551
522 void FindAllSaves(FileSys::SaveDataSpaceId space) { 552 void FindAllSaves(FileSys::SaveDataSpaceId space) {
523 const auto save_root = OpenSaveDataSpace(space); 553 const auto save_root = fsc.OpenSaveDataSpace(space);
524 ASSERT(save_root.Succeeded()); 554
555 if (save_root.Failed() || *save_root == nullptr) {
556 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
557 static_cast<u8>(space));
558 return;
559 }
525 560
526 for (const auto& type : (*save_root)->GetSubdirectories()) { 561 for (const auto& type : (*save_root)->GetSubdirectories()) {
527 if (type->GetName() == "save") { 562 if (type->GetName() == "save") {
@@ -610,11 +645,13 @@ private:
610 }; 645 };
611 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 646 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
612 647
648 FileSystemController& fsc;
613 std::vector<SaveDataInfo> info; 649 std::vector<SaveDataInfo> info;
614 u64 next_entry_index = 0; 650 u64 next_entry_index = 0;
615}; 651};
616 652
617FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) { 653FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
654 : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
618 // clang-format off 655 // clang-format off
619 static const FunctionInfo functions[] = { 656 static const FunctionInfo functions[] = {
620 {0, nullptr, "OpenFileSystem"}, 657 {0, nullptr, "OpenFileSystem"},
@@ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
754void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { 791void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
755 LOG_DEBUG(Service_FS, "called"); 792 LOG_DEBUG(Service_FS, "called");
756 793
757 IFileSystem filesystem(OpenSDMC().Unwrap()); 794 IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
795 SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
758 796
759 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 797 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
760 rb.Push(RESULT_SUCCESS); 798 rb.Push(RESULT_SUCCESS);
@@ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
768 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); 806 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
769 u128 uid = rp.PopRaw<u128>(); 807 u128 uid = rp.PopRaw<u128>();
770 808
771 LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}", 809 LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
772 save_struct.DebugInfo(), uid[1], uid[0]); 810 uid[1], uid[0]);
811
812 fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
773 813
774 IPC::ResponseBuilder rb{ctx, 2}; 814 IPC::ResponseBuilder rb{ctx, 2};
775 rb.Push(RESULT_SUCCESS); 815 rb.Push(RESULT_SUCCESS);
@@ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
786 IPC::RequestParser rp{ctx}; 826 IPC::RequestParser rp{ctx};
787 const auto parameters = rp.PopRaw<Parameters>(); 827 const auto parameters = rp.PopRaw<Parameters>();
788 828
789 auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); 829 auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
790 if (dir.Failed()) { 830 if (dir.Failed()) {
791 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 831 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
792 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 832 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
793 return; 833 return;
794 } 834 }
795 835
796 IFileSystem filesystem(std::move(dir.Unwrap())); 836 FileSys::StorageId id;
837 if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
838 id = FileSys::StorageId::NandUser;
839 } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
840 parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
841 id = FileSys::StorageId::SdCard;
842 } else {
843 id = FileSys::StorageId::NandSystem;
844 }
845
846 IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
797 847
798 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 848 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
799 rb.Push(RESULT_SUCCESS); 849 rb.Push(RESULT_SUCCESS);
@@ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
812 862
813 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 863 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
814 rb.Push(RESULT_SUCCESS); 864 rb.Push(RESULT_SUCCESS);
815 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); 865 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
816} 866}
817 867
818void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 868void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
836void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { 886void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
837 LOG_DEBUG(Service_FS, "called"); 887 LOG_DEBUG(Service_FS, "called");
838 888
839 auto romfs = OpenRomFSCurrentProcess(); 889 auto romfs = fsc.OpenRomFSCurrentProcess();
840 if (romfs.Failed()) { 890 if (romfs.Failed()) {
841 // TODO (bunnei): Find the right error code to use here 891 // TODO (bunnei): Find the right error code to use here
842 LOG_CRITICAL(Service_FS, "no file system interface available!"); 892 LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
861 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", 911 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
862 static_cast<u8>(storage_id), unknown, title_id); 912 static_cast<u8>(storage_id), unknown, title_id);
863 913
864 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 914 auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
865 915
866 if (data.Failed()) { 916 if (data.Failed()) {
867 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); 917 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index b5486a193..d52b55999 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,7 +32,7 @@ enum class LogMode : u32 {
32 32
33class FSP_SRV final : public ServiceFramework<FSP_SRV> { 33class FSP_SRV final : public ServiceFramework<FSP_SRV> {
34public: 34public:
35 explicit FSP_SRV(const Core::Reporter& reporter); 35 explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
36 ~FSP_SRV() override; 36 ~FSP_SRV() override;
37 37
38private: 38private:
@@ -51,6 +51,8 @@ private:
51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); 51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); 52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
53 53
54 FileSystemController& fsc;
55
54 FileSys::VirtualFile romfs; 56 FileSys::VirtualFile romfs;
55 u64 current_process_id = 0; 57 u64 current_process_id = 0;
56 u32 access_log_program_index = 0; 58 u32 access_log_program_index = 0;
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 ce88a2941..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) { 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) {
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>()->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 0e8256cb4..13a64ad88 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,7 +6,13 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Service::NS { 9namespace Service {
10
11namespace FileSystem {
12class FileSystemController;
13} // namespace FileSystem
14
15namespace NS {
10 16
11class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { 17class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
12public: 18public:
@@ -91,6 +97,7 @@ private:
91}; 97};
92 98
93/// Registers all NS services with the specified service manager. 99/// Registers all NS services with the specified service manager.
94void InstallInterfaces(SM::ServiceManager& service_manager); 100void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
95 101
96} // namespace Service::NS 102} // namespace NS
103} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 2a522136d..7dcdb4a07 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -150,7 +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() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { 153PL_U::PL_U(Core::System& system)
154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) {
155
154 static const FunctionInfo functions[] = { 156 static const FunctionInfo functions[] = {
155 {0, &PL_U::RequestLoad, "RequestLoad"}, 157 {0, &PL_U::RequestLoad, "RequestLoad"},
156 {1, &PL_U::GetLoadState, "GetLoadState"}, 158 {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -160,8 +162,11 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
160 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, 162 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
161 }; 163 };
162 RegisterHandlers(functions); 164 RegisterHandlers(functions);
165
166 auto& fsc = system.GetFileSystemController();
167
163 // Attempt to load shared font data from disk 168 // Attempt to load shared font data from disk
164 const auto* nand = FileSystem::GetSystemNANDContents(); 169 const auto* nand = fsc.GetSystemNANDContents();
165 std::size_t offset = 0; 170 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 171 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 172 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
@@ -324,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
324 Kernel::MemoryState::Shared); 329 Kernel::MemoryState::Shared);
325 330
326 // Create shared font memory object 331 // Create shared font memory object
327 auto& kernel = Core::System::GetInstance().Kernel(); 332 auto& kernel = system.Kernel();
328 impl->shared_font_mem = Kernel::SharedMemory::Create( 333 impl->shared_font_mem = Kernel::SharedMemory::Create(
329 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, 334 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
330 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 253f26a2a..1063f4204 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -7,11 +7,17 @@
7#include <memory> 7#include <memory>
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9 9
10namespace Service::NS { 10namespace Service {
11
12namespace FileSystem {
13class FileSystemController;
14} // namespace FileSystem
15
16namespace NS {
11 17
12class PL_U final : public ServiceFramework<PL_U> { 18class PL_U final : public ServiceFramework<PL_U> {
13public: 19public:
14 PL_U(); 20 explicit PL_U(Core::System& system);
15 ~PL_U() override; 21 ~PL_U() override;
16 22
17private: 23private:
@@ -24,6 +30,9 @@ private:
24 30
25 struct Impl; 31 struct Impl;
26 std::unique_ptr<Impl> impl; 32 std::unique_ptr<Impl> impl;
33 Core::System& system;
27}; 34};
28 35
29} // namespace Service::NS 36} // namespace NS
37
38} // namespace Service
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 241dac881..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
@@ -146,8 +147,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
146 } 147 }
147 IoctlSubmitGpfifo params{}; 148 IoctlSubmitGpfifo params{};
148 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 149 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
149 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 150 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
150 params.address, params.num_entries, params.flags.raw); 151 params.num_entries, params.flags.raw);
151 152
152 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + 153 ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
153 params.num_entries * sizeof(Tegra::CommandListHeader), 154 params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -173,19 +174,24 @@ 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 }
180 IoctlSubmitGpfifo params{}; 182 IoctlSubmitGpfifo params{};
181 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 183 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
182 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 184 LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
183 params.address, 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 7e134f5c1..18d895263 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -2,34 +2,31 @@
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 <json.hpp>
6#include "common/file_util.h"
7#include "common/hex_util.h" 5#include "common/hex_util.h"
8#include "common/logging/log.h" 6#include "common/logging/log.h"
9#include "common/scm_rev.h"
10#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/process.h" 8#include "core/hle/kernel/process.h"
12#include "core/hle/service/acc/profile_manager.h" 9#include "core/hle/service/acc/profile_manager.h"
13#include "core/hle/service/prepo/prepo.h" 10#include "core/hle/service/prepo/prepo.h"
14#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
15#include "core/reporter.h" 12#include "core/reporter.h"
16#include "core/settings.h"
17 13
18namespace Service::PlayReport { 14namespace Service::PlayReport {
19 15
20class PlayReport final : public ServiceFramework<PlayReport> { 16class PlayReport final : public ServiceFramework<PlayReport> {
21public: 17public:
22 explicit PlayReport(const char* name) : ServiceFramework{name} { 18 explicit PlayReport(const char* name, Core::System& system)
19 : ServiceFramework{name}, system(system) {
23 // clang-format off 20 // clang-format off
24 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
25 {10100, nullptr, "SaveReportOld"}, 22 {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"},
26 {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, 23 {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
27 {10102, nullptr, "SaveReport"}, 24 {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
28 {10103, nullptr, "SaveReportWithUser"}, 25 {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
29 {10200, nullptr, "RequestImmediateTransmission"}, 26 {10200, nullptr, "RequestImmediateTransmission"},
30 {10300, nullptr, "GetTransmissionStatus"}, 27 {10300, nullptr, "GetTransmissionStatus"},
31 {20100, nullptr, "SaveSystemReport"}, 28 {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
32 {20101, nullptr, "SaveSystemReportWithUser"}, 29 {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
33 {20200, nullptr, "SetOperationMode"}, 30 {20200, nullptr, "SetOperationMode"},
34 {30100, nullptr, "ClearStorage"}, 31 {30100, nullptr, "ClearStorage"},
35 {30200, nullptr, "ClearStatistics"}, 32 {30200, nullptr, "ClearStatistics"},
@@ -47,7 +44,28 @@ public:
47 } 44 }
48 45
49private: 46private:
50 void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { 47 template <Core::Reporter::PlayReportType Type>
48 void SaveReport(Kernel::HLERequestContext& ctx) {
49 IPC::RequestParser rp{ctx};
50 const auto process_id = rp.PopRaw<u64>();
51
52 const auto data1 = ctx.ReadBuffer(0);
53 const auto data2 = ctx.ReadBuffer(1);
54
55 LOG_DEBUG(Service_PREPO,
56 "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
57 static_cast<u8>(Type), process_id, data1.size(), data2.size());
58
59 const auto& reporter{system.GetReporter()};
60 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
61 process_id);
62
63 IPC::ResponseBuilder rb{ctx, 2};
64 rb.Push(RESULT_SUCCESS);
65 }
66
67 template <Core::Reporter::PlayReportType Type>
68 void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
51 IPC::RequestParser rp{ctx}; 69 IPC::RequestParser rp{ctx};
52 const auto user_id = rp.PopRaw<u128>(); 70 const auto user_id = rp.PopRaw<u128>();
53 const auto process_id = rp.PopRaw<u64>(); 71 const auto process_id = rp.PopRaw<u64>();
@@ -57,24 +75,65 @@ private:
57 75
58 LOG_DEBUG( 76 LOG_DEBUG(
59 Service_PREPO, 77 Service_PREPO,
60 "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}", 78 "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, "
61 user_id[1], user_id[0], process_id, data1.size(), data2.size()); 79 "data2_size={:016X}",
80 static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
81
82 const auto& reporter{system.GetReporter()};
83 reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
84 process_id, user_id);
85
86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(RESULT_SUCCESS);
88 }
89
90 void SaveSystemReport(Kernel::HLERequestContext& ctx) {
91 IPC::RequestParser rp{ctx};
92 const auto title_id = rp.PopRaw<u64>();
93
94 const auto data1 = ctx.ReadBuffer(0);
95 const auto data2 = ctx.ReadBuffer(1);
96
97 LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
98 title_id, data1.size(), data2.size());
99
100 const auto& reporter{system.GetReporter()};
101 reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2});
102
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(RESULT_SUCCESS);
105 }
62 106
63 const auto& reporter{Core::System::GetInstance().GetReporter()}; 107 void SaveSystemReportWithUser(Kernel::HLERequestContext& ctx) {
64 reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2}, 108 IPC::RequestParser rp{ctx};
65 user_id); 109 const auto user_id = rp.PopRaw<u128>();
110 const auto title_id = rp.PopRaw<u64>();
111
112 const auto data1 = ctx.ReadBuffer(0);
113 const auto data2 = ctx.ReadBuffer(1);
114
115 LOG_DEBUG(Service_PREPO,
116 "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
117 "data2_size={:016X}",
118 user_id[1], user_id[0], title_id, data1.size(), data2.size());
119
120 const auto& reporter{system.GetReporter()};
121 reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2},
122 std::nullopt, user_id);
66 123
67 IPC::ResponseBuilder rb{ctx, 2}; 124 IPC::ResponseBuilder rb{ctx, 2};
68 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
69 } 126 }
127
128 Core::System& system;
70}; 129};
71 130
72void InstallInterfaces(SM::ServiceManager& service_manager) { 131void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
73 std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); 132 std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager);
74 std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); 133 std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager);
75 std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); 134 std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager);
76 std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); 135 std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager);
77 std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); 136 std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager);
78} 137}
79 138
80} // 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 0e7b01331..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(SM::ServiceManager& service_manager); 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 3a0f8c3f6..831a427de 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -198,49 +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 203
203 SM::ServiceManager::InstallInterfaces(sm); 204 SM::ServiceManager::InstallInterfaces(sm);
204 205
205 Account::InstallInterfaces(system); 206 Account::InstallInterfaces(system);
206 AM::InstallInterfaces(*sm, nv_flinger, system); 207 AM::InstallInterfaces(*sm, nv_flinger, system);
207 AOC::InstallInterfaces(*sm); 208 AOC::InstallInterfaces(*sm, system);
208 APM::InstallInterfaces(system); 209 APM::InstallInterfaces(system);
209 Audio::InstallInterfaces(*sm, system); 210 Audio::InstallInterfaces(*sm, system);
210 BCAT::InstallInterfaces(*sm); 211 BCAT::InstallInterfaces(*sm);
211 BPC::InstallInterfaces(*sm); 212 BPC::InstallInterfaces(*sm);
212 BtDrv::InstallInterfaces(*sm); 213 BtDrv::InstallInterfaces(*sm, system);
213 BTM::InstallInterfaces(*sm); 214 BTM::InstallInterfaces(*sm, system);
214 Capture::InstallInterfaces(*sm); 215 Capture::InstallInterfaces(*sm);
215 ERPT::InstallInterfaces(*sm); 216 ERPT::InstallInterfaces(*sm);
216 ES::InstallInterfaces(*sm); 217 ES::InstallInterfaces(*sm);
217 EUPLD::InstallInterfaces(*sm); 218 EUPLD::InstallInterfaces(*sm);
218 Fatal::InstallInterfaces(*sm); 219 Fatal::InstallInterfaces(*sm, system);
219 FGM::InstallInterfaces(*sm); 220 FGM::InstallInterfaces(*sm);
220 FileSystem::InstallInterfaces(system); 221 FileSystem::InstallInterfaces(system);
221 Friend::InstallInterfaces(*sm); 222 Friend::InstallInterfaces(*sm, system);
222 Glue::InstallInterfaces(system); 223 Glue::InstallInterfaces(system);
223 GRC::InstallInterfaces(*sm); 224 GRC::InstallInterfaces(*sm);
224 HID::InstallInterfaces(*sm); 225 HID::InstallInterfaces(*sm, system);
225 LBL::InstallInterfaces(*sm); 226 LBL::InstallInterfaces(*sm);
226 LDN::InstallInterfaces(*sm); 227 LDN::InstallInterfaces(*sm);
227 LDR::InstallInterfaces(*sm); 228 LDR::InstallInterfaces(*sm, system);
228 LM::InstallInterfaces(*sm); 229 LM::InstallInterfaces(*sm);
229 Migration::InstallInterfaces(*sm); 230 Migration::InstallInterfaces(*sm);
230 Mii::InstallInterfaces(*sm); 231 Mii::InstallInterfaces(*sm);
231 MM::InstallInterfaces(*sm); 232 MM::InstallInterfaces(*sm);
232 NCM::InstallInterfaces(*sm); 233 NCM::InstallInterfaces(*sm);
233 NFC::InstallInterfaces(*sm); 234 NFC::InstallInterfaces(*sm);
234 NFP::InstallInterfaces(*sm); 235 NFP::InstallInterfaces(*sm, system);
235 NIFM::InstallInterfaces(*sm); 236 NIFM::InstallInterfaces(*sm, system);
236 NIM::InstallInterfaces(*sm); 237 NIM::InstallInterfaces(*sm, system);
237 NPNS::InstallInterfaces(*sm); 238 NPNS::InstallInterfaces(*sm);
238 NS::InstallInterfaces(*sm); 239 NS::InstallInterfaces(*sm, system);
239 Nvidia::InstallInterfaces(*sm, *nv_flinger, system); 240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
240 PCIe::InstallInterfaces(*sm); 241 PCIe::InstallInterfaces(*sm);
241 PCTL::InstallInterfaces(*sm); 242 PCTL::InstallInterfaces(*sm);
242 PCV::InstallInterfaces(*sm); 243 PCV::InstallInterfaces(*sm);
243 PlayReport::InstallInterfaces(*sm); 244 PlayReport::InstallInterfaces(*sm, system);
244 PM::InstallInterfaces(system); 245 PM::InstallInterfaces(system);
245 PSC::InstallInterfaces(*sm); 246 PSC::InstallInterfaces(*sm);
246 PSM::InstallInterfaces(*sm); 247 PSM::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c6c4bdae5..aef964861 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -18,10 +18,6 @@ namespace Core {
18class System; 18class System;
19} 19}
20 20
21namespace FileSys {
22class VfsFilesystem;
23}
24
25namespace Kernel { 21namespace Kernel {
26class ClientPort; 22class ClientPort;
27class ServerPort; 23class ServerPort;
@@ -31,6 +27,10 @@ class HLERequestContext;
31 27
32namespace Service { 28namespace Service {
33 29
30namespace FileSystem {
31class FileSystemController;
32} // namespace FileSystem
33
34namespace SM { 34namespace SM {
35class ServiceManager; 35class ServiceManager;
36} 36}
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/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index f9e88be2b..d19c3623c 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,7 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
11#include "core/file_sys/control_metadata.h" 12#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
@@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
176 // Register the RomFS if a ".romfs" file was found 177 // Register the RomFS if a ".romfs" file was found
177 if (romfs_iter != files.end() && *romfs_iter != nullptr) { 178 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
178 romfs = *romfs_iter; 179 romfs = *romfs_iter;
179 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 180 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
181 std::make_unique<FileSys::RomFSFactory>(*this));
180 } 182 }
181 183
182 is_loaded = true; 184 is_loaded = true;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 0f65fb637..5a0469978 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/file_util.h" 7#include "common/file_util.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/romfs_factory.h" 11#include "core/file_sys/romfs_factory.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
@@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
57 } 58 }
58 59
59 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { 60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
60 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 61 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
62 std::make_unique<FileSys::RomFSFactory>(*this));
61 } 63 }
62 64
63 is_loaded = true; 65 is_loaded = true;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 3a5361fdd..175898b91 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h"
13#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/romfs_factory.h" 15#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
@@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
214 } 215 }
215 216
216 if (romfs != nullptr) { 217 if (romfs != nullptr) {
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 218 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
219 std::make_unique<FileSys::RomFSFactory>(*this));
218 } 220 }
219 221
220 is_loaded = true; 222 is_loaded = true;
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/loader/nsp.cpp b/src/core/loader/nsp.cpp
index b1171ce65..13950fc08 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -5,6 +5,7 @@
5#include <vector> 5#include <vector>
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/core.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
@@ -26,20 +27,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
26 27
27 if (nsp->GetStatus() != ResultStatus::Success) 28 if (nsp->GetStatus() != ResultStatus::Success)
28 return; 29 return;
29 if (nsp->IsExtractedType())
30 return;
31
32 const auto control_nca =
33 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
34 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
35 return;
36
37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39 30
40 if (nsp->IsExtractedType()) { 31 if (nsp->IsExtractedType()) {
41 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 32 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
42 } else { 33 } else {
34 const auto control_nca =
35 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
36 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
37 return;
38
39 std::tie(nacp_file, icon_file) =
40 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
41
43 if (title_id == 0) 42 if (title_id == 0)
44 return; 43 return;
45 44
@@ -56,11 +55,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
56 if (nsp.GetStatus() == ResultStatus::Success) { 55 if (nsp.GetStatus() == ResultStatus::Success) {
57 // Extracted Type case 56 // Extracted Type case
58 if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && 57 if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
59 FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) { 58 FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
60 return FileType::NSP; 59 return FileType::NSP;
61 } 60 }
62 61
63 // Non-Ectracted Type case 62 // Non-Extracted Type case
64 if (!nsp.IsExtractedType() && 63 if (!nsp.IsExtractedType() &&
65 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && 64 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
66 AppLoader_NCA::IdentifyType(nsp.GetNCAFile( 65 AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
@@ -77,7 +76,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
77 return {ResultStatus::ErrorAlreadyLoaded, {}}; 76 return {ResultStatus::ErrorAlreadyLoaded, {}};
78 } 77 }
79 78
80 if (title_id == 0) { 79 if (!nsp->IsExtractedType() && title_id == 0) {
81 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 80 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
82 } 81 }
83 82
@@ -91,7 +90,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
91 return {nsp_program_status, {}}; 90 return {nsp_program_status, {}};
92 } 91 }
93 92
94 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 93 if (!nsp->IsExtractedType() &&
94 nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
95 if (!Core::Crypto::KeyManager::KeyFileExists(false)) { 95 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
96 return {ResultStatus::ErrorMissingProductionKeyFile, {}}; 96 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
97 } 97 }
@@ -106,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
106 106
107 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
109 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 109 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
110 std::move(update_raw));
110 } 111 }
111 112
112 is_loaded = true; 113 is_loaded = true;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 5e8553db9..7186ad1ff 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -5,6 +5,7 @@
5#include <vector> 5#include <vector>
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/core.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
@@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
72 73
73 FileSys::VirtualFile update_raw; 74 FileSys::VirtualFile update_raw;
74 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
75 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 76 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
77 std::move(update_raw));
76 } 78 }
77 79
78 is_loaded = true; 80 is_loaded = true;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 8555691c0..9e030789d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -43,8 +43,13 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me
43 43
44 // During boot, current_page_table might not be set yet, in which case we need not flush 44 // During boot, current_page_table might not be set yet, in which case we need not flush
45 if (Core::System::GetInstance().IsPoweredOn()) { 45 if (Core::System::GetInstance().IsPoweredOn()) {
46 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, 46 auto& gpu = Core::System::GetInstance().GPU();
47 size * PAGE_SIZE); 47 for (u64 i = 0; i < size; i++) {
48 const auto page = base + i;
49 if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
50 gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
51 }
52 }
48 } 53 }
49 54
50 VAddr end = base + size; 55 VAddr end = base + size;
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/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 4afd6c8a3..d2c69d1a0 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -4,8 +4,14 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono> 6#include <chrono>
7#include <iterator>
7#include <mutex> 8#include <mutex>
9#include <numeric>
10#include <sstream>
8#include <thread> 11#include <thread>
12#include <fmt/chrono.h>
13#include <fmt/format.h>
14#include "common/file_util.h"
9#include "common/math_util.h" 15#include "common/math_util.h"
10#include "core/perf_stats.h" 16#include "core/perf_stats.h"
11#include "core/settings.h" 17#include "core/settings.h"
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
15using std::chrono::duration_cast; 21using std::chrono::duration_cast;
16using std::chrono::microseconds; 22using std::chrono::microseconds;
17 23
24// Purposefully ignore the first five frames, as there's a significant amount of overhead in
25// booting that we shouldn't account for
26constexpr std::size_t IgnoreFrames = 5;
27
18namespace Core { 28namespace Core {
19 29
30PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
31
32PerfStats::~PerfStats() {
33 if (!Settings::values.record_frame_times || title_id == 0) {
34 return;
35 }
36
37 const std::time_t t = std::time(nullptr);
38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w");
46 file.WriteString(stream.str());
47}
48
20void PerfStats::BeginSystemFrame() { 49void PerfStats::BeginSystemFrame() {
21 std::lock_guard lock{object_mutex}; 50 std::lock_guard lock{object_mutex};
22 51
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
27 std::lock_guard lock{object_mutex}; 56 std::lock_guard lock{object_mutex};
28 57
29 auto frame_end = Clock::now(); 58 auto frame_end = Clock::now();
30 accumulated_frametime += frame_end - frame_begin; 59 const auto frame_time = frame_end - frame_begin;
60 if (current_index < perf_history.size()) {
61 perf_history[current_index++] =
62 std::chrono::duration<double, std::milli>(frame_time).count();
63 }
64 accumulated_frametime += frame_time;
31 system_frames += 1; 65 system_frames += 1;
32 66
33 previous_frame_length = frame_end - previous_frame_end; 67 previous_frame_length = frame_end - previous_frame_end;
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 74 game_frames += 1;
41} 75}
42 76
77double PerfStats::GetMeanFrametime() {
78 std::lock_guard lock{object_mutex};
79
80 if (current_index <= IgnoreFrames) {
81 return 0;
82 }
83 const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
84 perf_history.begin() + current_index, 0);
85 return sum / (current_index - IgnoreFrames);
86}
87
43PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 88PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard lock{object_mutex}; 89 std::lock_guard lock{object_mutex};
45 90
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 222ac1a63..d9a64f072 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <chrono> 8#include <chrono>
9#include <cstddef>
8#include <mutex> 10#include <mutex>
9#include "common/common_types.h" 11#include "common/common_types.h"
10 12
@@ -27,6 +29,10 @@ struct PerfStatsResults {
27 */ 29 */
28class PerfStats { 30class PerfStats {
29public: 31public:
32 explicit PerfStats(u64 title_id);
33
34 ~PerfStats();
35
30 using Clock = std::chrono::high_resolution_clock; 36 using Clock = std::chrono::high_resolution_clock;
31 37
32 void BeginSystemFrame(); 38 void BeginSystemFrame();
@@ -36,13 +42,26 @@ public:
36 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); 42 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 43
38 /** 44 /**
45 * Returns the Arthimetic Mean of all frametime values stored in the performance history.
46 */
47 double GetMeanFrametime();
48
49 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 50 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
40 * useful for scaling inputs or outputs moving between the two time domains. 51 * useful for scaling inputs or outputs moving between the two time domains.
41 */ 52 */
42 double GetLastFrameTimeScale(); 53 double GetLastFrameTimeScale();
43 54
44private: 55private:
45 std::mutex object_mutex; 56 std::mutex object_mutex{};
57
58 /// Title ID for the game that is running. 0 if there is no game running yet
59 u64 title_id{0};
60 /// Current index for writing to the perf_history array
61 std::size_t current_index{0};
62 /// Stores an hour of historical frametime data useful for processing and tracking performance
63 /// regressions with code changes.
64 std::array<double, 216000> perf_history = {};
46 65
47 /// Point when the cumulative counters were reset 66 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 67 Clock::time_point reset_point = Clock::now();
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index cfe0771e2..9c657929e 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -304,8 +304,8 @@ void Reporter::SaveUnimplementedAppletReport(
304 SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); 304 SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
305} 305}
306 306
307void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, 307void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
308 std::optional<u128> user_id) const { 308 std::optional<u64> process_id, std::optional<u128> user_id) const {
309 if (!IsReportingEnabled()) { 309 if (!IsReportingEnabled()) {
310 return; 310 return;
311 } 311 }
@@ -321,7 +321,11 @@ void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vec
321 data_out.push_back(Common::HexToString(d)); 321 data_out.push_back(Common::HexToString(d));
322 } 322 }
323 323
324 out["play_report_process_id"] = fmt::format("{:016X}", process_id); 324 if (process_id.has_value()) {
325 out["play_report_process_id"] = fmt::format("{:016X}", *process_id);
326 }
327
328 out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type));
325 out["play_report_data"] = std::move(data_out); 329 out["play_report_data"] = std::move(data_out);
326 330
327 SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); 331 SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 44256de50..f08aa11fb 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -46,8 +46,14 @@ public:
46 std::vector<std::vector<u8>> normal_channel, 46 std::vector<std::vector<u8>> normal_channel,
47 std::vector<std::vector<u8>> interactive_channel) const; 47 std::vector<std::vector<u8>> interactive_channel) const;
48 48
49 void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, 49 enum class PlayReportType {
50 std::optional<u128> user_id = {}) const; 50 Old,
51 New,
52 System,
53 };
54
55 void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
56 std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
51 57
52 void SaveErrorReport(u64 title_id, ResultCode result, 58 void SaveErrorReport(u64 title_id, ResultCode result,
53 std::optional<std::string> custom_text_main = {}, 59 std::optional<std::string> custom_text_main = {},
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0dd1632ac..7de3fd1e5 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/file_util.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/gdbstub/gdbstub.h" 7#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h" 8#include "core/hle/service/hid/hid.h"
@@ -97,8 +98,8 @@ void LogSettings() {
97 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 98 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
98 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 99 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
99 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); 100 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
100 LogSetting("DataStorage_NandDir", Settings::values.nand_dir); 101 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
101 LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir); 102 LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
102 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); 103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
103 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); 104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
104 LogSetting("Debugging_ProgramArgs", Settings::values.program_args); 105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index 6638ce8f9..47bddfb30 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -346,6 +346,31 @@ struct TouchscreenInput {
346 u32 rotation_angle; 346 u32 rotation_angle;
347}; 347};
348 348
349enum class NANDTotalSize : u64 {
350 S29_1GB = 0x747C00000ULL,
351};
352
353enum class NANDUserSize : u64 {
354 S26GB = 0x680000000ULL,
355};
356
357enum class NANDSystemSize : u64 {
358 S2_5GB = 0xA0000000,
359};
360
361enum class SDMCSize : u64 {
362 S1GB = 0x40000000,
363 S2GB = 0x80000000,
364 S4GB = 0x100000000ULL,
365 S8GB = 0x200000000ULL,
366 S16GB = 0x400000000ULL,
367 S32GB = 0x800000000ULL,
368 S64GB = 0x1000000000ULL,
369 S128GB = 0x2000000000ULL,
370 S256GB = 0x4000000000ULL,
371 S1TB = 0x10000000000ULL,
372};
373
349struct Values { 374struct Values {
350 // System 375 // System
351 bool use_docked_mode; 376 bool use_docked_mode;
@@ -382,8 +407,13 @@ struct Values {
382 407
383 // Data Storage 408 // Data Storage
384 bool use_virtual_sd; 409 bool use_virtual_sd;
385 std::string nand_dir; 410 bool gamecard_inserted;
386 std::string sdmc_dir; 411 bool gamecard_current_game;
412 std::string gamecard_path;
413 NANDTotalSize nand_total_size;
414 NANDSystemSize nand_system_size;
415 NANDUserSize nand_user_size;
416 SDMCSize sdmc_size;
387 417
388 // Renderer 418 // Renderer
389 float resolution_factor; 419 float resolution_factor;
@@ -409,6 +439,7 @@ struct Values {
409 float volume; 439 float volume;
410 440
411 // Debugging 441 // Debugging
442 bool record_frame_times;
412 bool use_gdbstub; 443 bool use_gdbstub;
413 u16 gdbstub_port; 444 u16 gdbstub_port;
414 std::string program_args; 445 std::string program_args;
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 c7a3c85a0..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!");
@@ -541,7 +639,7 @@ void Maxwell3D::ProcessSyncPoint() {
541} 639}
542 640
543void Maxwell3D::DrawArrays() { 641void Maxwell3D::DrawArrays() {
544 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), 642 LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
545 regs.vertex_buffer.count); 643 regs.vertex_buffer.count);
546 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); 644 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
547 645
@@ -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/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b46fcf03d..7a6355ce2 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -950,6 +950,11 @@ union Instruction {
950 } isetp; 950 } isetp;
951 951
952 union { 952 union {
953 BitField<48, 1, u64> is_signed;
954 BitField<49, 3, PredCondition> cond;
955 } icmp;
956
957 union {
953 BitField<0, 3, u64> pred0; 958 BitField<0, 3, u64> pred0;
954 BitField<3, 3, u64> pred3; 959 BitField<3, 3, u64> pred3;
955 BitField<12, 3, u64> pred12; 960 BitField<12, 3, u64> pred12;
@@ -1646,6 +1651,10 @@ public:
1646 SEL_C, 1651 SEL_C,
1647 SEL_R, 1652 SEL_R,
1648 SEL_IMM, 1653 SEL_IMM,
1654 ICMP_RC,
1655 ICMP_R,
1656 ICMP_CR,
1657 ICMP_IMM,
1649 MUFU, // Multi-Function Operator 1658 MUFU, // Multi-Function Operator
1650 RRO_C, // Range Reduction Operator 1659 RRO_C, // Range Reduction Operator
1651 RRO_R, 1660 RRO_R,
@@ -1912,6 +1921,10 @@ private:
1912 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), 1921 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
1913 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), 1922 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
1914 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), 1923 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
1924 INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"),
1925 INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
1926 INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
1927 INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
1915 INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"), 1928 INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
1916 INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"), 1929 INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
1917 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), 1930 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
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 0f3a35f74..e6b36a0f2 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -461,6 +461,14 @@ private:
461 code.AddLine("float gl_PointSize;"); 461 code.AddLine("float gl_PointSize;");
462 } 462 }
463 463
464 if (ir.UsesInstanceId()) {
465 code.AddLine("int gl_InstanceID;");
466 }
467
468 if (ir.UsesVertexId()) {
469 code.AddLine("int gl_VertexID;");
470 }
471
464 --code.scope; 472 --code.scope;
465 code.AddLine("}};"); 473 code.AddLine("}};");
466 code.AddNewLine(); 474 code.AddNewLine();
@@ -951,7 +959,7 @@ private:
951 switch (element) { 959 switch (element) {
952 case 2: 960 case 2:
953 // Config pack's first value is instance_id. 961 // Config pack's first value is instance_id.
954 return {"config_pack[0]", Type::Uint}; 962 return {"gl_InstanceID", Type::Int};
955 case 3: 963 case 3:
956 return {"gl_VertexID", Type::Int}; 964 return {"gl_VertexID", Type::Int};
957 } 965 }
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_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index ea77dd211..9ed738171 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -145,7 +145,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
145 case Tegra::Texture::TextureMipmapFilter::None: 145 case Tegra::Texture::TextureMipmapFilter::None:
146 return GL_LINEAR; 146 return GL_LINEAR;
147 case Tegra::Texture::TextureMipmapFilter::Nearest: 147 case Tegra::Texture::TextureMipmapFilter::Nearest:
148 return GL_NEAREST_MIPMAP_LINEAR; 148 return GL_LINEAR_MIPMAP_NEAREST;
149 case Tegra::Texture::TextureMipmapFilter::Linear: 149 case Tegra::Texture::TextureMipmapFilter::Linear:
150 return GL_LINEAR_MIPMAP_LINEAR; 150 return GL_LINEAR_MIPMAP_LINEAR;
151 } 151 }
@@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
157 case Tegra::Texture::TextureMipmapFilter::Nearest: 157 case Tegra::Texture::TextureMipmapFilter::Nearest:
158 return GL_NEAREST_MIPMAP_NEAREST; 158 return GL_NEAREST_MIPMAP_NEAREST;
159 case Tegra::Texture::TextureMipmapFilter::Linear: 159 case Tegra::Texture::TextureMipmapFilter::Linear:
160 return GL_LINEAR_MIPMAP_NEAREST; 160 return GL_NEAREST_MIPMAP_LINEAR;
161 } 161 }
162 } 162 }
163 } 163 }
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/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index c8c1a7f40..b73f6536e 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -138,6 +138,35 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
138 SetRegister(bb, instr.gpr0, value); 138 SetRegister(bb, instr.gpr0, value);
139 break; 139 break;
140 } 140 }
141 case OpCode::Id::ICMP_CR:
142 case OpCode::Id::ICMP_R:
143 case OpCode::Id::ICMP_RC:
144 case OpCode::Id::ICMP_IMM: {
145 const Node zero = Immediate(0);
146
147 const auto [op_b, test] = [&]() -> std::pair<Node, Node> {
148 switch (opcode->get().GetId()) {
149 case OpCode::Id::ICMP_CR:
150 return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset),
151 GetRegister(instr.gpr39)};
152 case OpCode::Id::ICMP_R:
153 return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
154 case OpCode::Id::ICMP_RC:
155 return {GetRegister(instr.gpr39),
156 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
157 case OpCode::Id::ICMP_IMM:
158 return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
159 default:
160 UNREACHABLE();
161 return {zero, zero};
162 }
163 }();
164 const Node op_a = GetRegister(instr.gpr8);
165 const Node comparison =
166 GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero);
167 SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_a, op_b));
168 break;
169 }
141 case OpCode::Id::LOP_C: 170 case OpCode::Id::LOP_C:
142 case OpCode::Id::LOP_R: 171 case OpCode::Id::LOP_R:
143 case OpCode::Id::LOP_IMM: { 172 case OpCode::Id::LOP_IMM: {
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 c3e147eea..6f666ee30 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 }
@@ -370,6 +378,8 @@ private:
370 bool uses_viewport_index{}; 378 bool uses_viewport_index{};
371 bool uses_point_size{}; 379 bool uses_point_size{};
372 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes 380 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
381 bool uses_instance_id{};
382 bool uses_vertex_id{};
373 383
374 Tegra::Shader::Header header; 384 Tegra::Shader::Header header;
375}; 385};
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/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f051e17b4..dc6fa07fc 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -33,6 +33,9 @@ add_executable(yuzu
33 configuration/configure_debug.ui 33 configuration/configure_debug.ui
34 configuration/configure_dialog.cpp 34 configuration/configure_dialog.cpp
35 configuration/configure_dialog.h 35 configuration/configure_dialog.h
36 configuration/configure_filesystem.cpp
37 configuration/configure_filesystem.h
38 configuration/configure_filesystem.ui
36 configuration/configure_gamelist.cpp 39 configuration/configure_gamelist.cpp
37 configuration/configure_gamelist.h 40 configuration/configure_gamelist.h
38 configuration/configure_gamelist.ui 41 configuration/configure_gamelist.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index f594106bf..92d9fb161 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -459,6 +459,49 @@ void Config::ReadDataStorageValues() {
459 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) 459 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
460 .toString() 460 .toString()
461 .toStdString()); 461 .toStdString());
462 FileUtil::GetUserPath(
463 FileUtil::UserPath::LoadDir,
464 qt_config
465 ->value(QStringLiteral("load_directory"),
466 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
467 .toString()
468 .toStdString());
469 FileUtil::GetUserPath(
470 FileUtil::UserPath::DumpDir,
471 qt_config
472 ->value(QStringLiteral("dump_directory"),
473 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
474 .toString()
475 .toStdString());
476 FileUtil::GetUserPath(
477 FileUtil::UserPath::CacheDir,
478 qt_config
479 ->value(QStringLiteral("cache_directory"),
480 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
481 .toString()
482 .toStdString());
483 Settings::values.gamecard_inserted =
484 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
485 Settings::values.gamecard_current_game =
486 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
487 Settings::values.gamecard_path =
488 ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
489 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
490 ReadSetting(QStringLiteral("nand_total_size"),
491 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
492 .toULongLong());
493 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
494 ReadSetting(QStringLiteral("nand_user_size"),
495 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
496 .toULongLong());
497 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
498 ReadSetting(QStringLiteral("nand_system_size"),
499 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
500 .toULongLong());
501 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
502 ReadSetting(QStringLiteral("sdmc_size"),
503 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
504 .toULongLong());
462 505
463 qt_config->endGroup(); 506 qt_config->endGroup();
464} 507}
@@ -466,6 +509,9 @@ void Config::ReadDataStorageValues() {
466void Config::ReadDebuggingValues() { 509void Config::ReadDebuggingValues() {
467 qt_config->beginGroup(QStringLiteral("Debugging")); 510 qt_config->beginGroup(QStringLiteral("Debugging"));
468 511
512 // Intentionally not using the QT default setting as this is intended to be changed in the ini
513 Settings::values.record_frame_times =
514 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
469 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 515 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
470 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 516 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
471 Settings::values.program_args = 517 Settings::values.program_args =
@@ -872,13 +918,40 @@ void Config::SaveDataStorageValues() {
872 WriteSetting(QStringLiteral("sdmc_directory"), 918 WriteSetting(QStringLiteral("sdmc_directory"),
873 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 919 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
874 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 920 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
875 921 WriteSetting(QStringLiteral("load_directory"),
922 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)),
923 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
924 WriteSetting(QStringLiteral("dump_directory"),
925 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)),
926 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
927 WriteSetting(QStringLiteral("cache_directory"),
928 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
929 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
930 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
931 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
932 false);
933 WriteSetting(QStringLiteral("gamecard_path"),
934 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
935 WriteSetting(QStringLiteral("nand_total_size"),
936 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
937 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
938 WriteSetting(QStringLiteral("nand_user_size"),
939 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
940 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
941 WriteSetting(QStringLiteral("nand_system_size"),
942 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
943 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
944 WriteSetting(QStringLiteral("sdmc_size"),
945 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
946 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
876 qt_config->endGroup(); 947 qt_config->endGroup();
877} 948}
878 949
879void Config::SaveDebuggingValues() { 950void Config::SaveDebuggingValues() {
880 qt_config->beginGroup(QStringLiteral("Debugging")); 951 qt_config->beginGroup(QStringLiteral("Debugging"));
881 952
953 // Intentionally not using the QT default setting as this is intended to be changed in the ini
954 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
882 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 955 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
883 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 956 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
884 WriteSetting(QStringLiteral("program_args"), 957 WriteSetting(QStringLiteral("program_args"),
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 267717bc9..49fadd0ef 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -63,6 +63,11 @@
63 <string>Profiles</string> 63 <string>Profiles</string>
64 </attribute> 64 </attribute>
65 </widget> 65 </widget>
66 <widget class="ConfigureFilesystem" name="filesystemTab">
67 <attribute name="title">
68 <string>Filesystem</string>
69 </attribute>
70 </widget>
66 <widget class="ConfigureInputSimple" name="inputTab"> 71 <widget class="ConfigureInputSimple" name="inputTab">
67 <attribute name="title"> 72 <attribute name="title">
68 <string>Input</string> 73 <string>Input</string>
@@ -126,6 +131,12 @@
126 <container>1</container> 131 <container>1</container>
127 </customwidget> 132 </customwidget>
128 <customwidget> 133 <customwidget>
134 <class>ConfigureFilesystem</class>
135 <extends>QWidget</extends>
136 <header>configuration/configure_filesystem.h</header>
137 <container>1</container>
138 </customwidget>
139 <customwidget>
129 <class>ConfigureAudio</class> 140 <class>ConfigureAudio</class>
130 <extends>QWidget</extends> 141 <extends>QWidget</extends>
131 <header>configuration/configure_audio.h</header> 142 <header>configuration/configure_audio.h</header>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 5b7e03056..90c1f9459 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,8 +34,6 @@ void ConfigureDebug::SetConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
39 ui->reporting_services->setChecked(Settings::values.reporting_services); 37 ui->reporting_services->setChecked(Settings::values.reporting_services);
40 ui->quest_flag->setChecked(Settings::values.quest_flag); 38 ui->quest_flag->setChecked(Settings::values.quest_flag);
41} 39}
@@ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() {
46 UISettings::values.show_console = ui->toggle_console->isChecked(); 44 UISettings::values.show_console = ui->toggle_console->isChecked();
47 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 45 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
48 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
49 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
50 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
51 Settings::values.reporting_services = ui->reporting_services->isChecked(); 47 Settings::values.reporting_services = ui->reporting_services->isChecked();
52 Settings::values.quest_flag = ui->quest_flag->isChecked(); 48 Settings::values.quest_flag = ui->quest_flag->isChecked();
53 Debugger::ToggleConsole(); 49 Debugger::ToggleConsole();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 7e109cef0..ce49569bb 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -103,58 +103,6 @@
103 </item> 103 </item>
104 </layout> 104 </layout>
105 </item> 105 </item>
106 </layout>
107 </widget>
108 </item>
109 <item>
110 <widget class="QGroupBox" name="groupBox_3">
111 <property name="title">
112 <string>Homebrew</string>
113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout_5">
115 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout_4">
117 <item>
118 <widget class="QLabel" name="label_3">
119 <property name="text">
120 <string>Arguments String</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLineEdit" name="homebrew_args_edit"/>
126 </item>
127 </layout>
128 </item>
129 </layout>
130 </widget>
131 </item>
132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Dump</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_6">
138 <item>
139 <widget class="QCheckBox" name="dump_decompressed_nso">
140 <property name="whatsThis">
141 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
142 </property>
143 <property name="text">
144 <string>Dump Decompressed NSOs</string>
145 </property>
146 </widget>
147 </item>
148 <item>
149 <widget class="QCheckBox" name="dump_exefs">
150 <property name="whatsThis">
151 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
152 </property>
153 <property name="text">
154 <string>Dump ExeFS</string>
155 </property>
156 </widget>
157 </item>
158 <item> 106 <item>
159 <widget class="QCheckBox" name="reporting_services"> 107 <widget class="QCheckBox" name="reporting_services">
160 <property name="text"> 108 <property name="text">
@@ -163,7 +111,7 @@
163 </widget> 111 </widget>
164 </item> 112 </item>
165 <item> 113 <item>
166 <widget class="QLabel" name="label_3"> 114 <widget class="QLabel" name="label">
167 <property name="font"> 115 <property name="font">
168 <font> 116 <font>
169 <italic>true</italic> 117 <italic>true</italic>
@@ -197,10 +145,36 @@
197 </widget> 145 </widget>
198 </item> 146 </item>
199 <item> 147 <item>
148 <widget class="QGroupBox" name="groupBox_3">
149 <property name="title">
150 <string>Homebrew</string>
151 </property>
152 <layout class="QVBoxLayout" name="verticalLayout_5">
153 <item>
154 <layout class="QHBoxLayout" name="horizontalLayout_4">
155 <item>
156 <widget class="QLabel" name="label_3">
157 <property name="text">
158 <string>Arguments String</string>
159 </property>
160 </widget>
161 </item>
162 <item>
163 <widget class="QLineEdit" name="homebrew_args_edit"/>
164 </item>
165 </layout>
166 </item>
167 </layout>
168 </widget>
169 </item>
170 <item>
200 <spacer name="verticalSpacer"> 171 <spacer name="verticalSpacer">
201 <property name="orientation"> 172 <property name="orientation">
202 <enum>Qt::Vertical</enum> 173 <enum>Qt::Vertical</enum>
203 </property> 174 </property>
175 <property name="sizeType">
176 <enum>QSizePolicy::Expanding</enum>
177 </property>
204 <property name="sizeHint" stdset="0"> 178 <property name="sizeHint" stdset="0">
205 <size> 179 <size>
206 <width>20</width> 180 <width>20</width>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 775e3f2ea..7c875ae87 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -37,6 +37,7 @@ void ConfigureDialog::ApplyConfiguration() {
37 ui->gameListTab->ApplyConfiguration(); 37 ui->gameListTab->ApplyConfiguration();
38 ui->systemTab->ApplyConfiguration(); 38 ui->systemTab->ApplyConfiguration();
39 ui->profileManagerTab->ApplyConfiguration(); 39 ui->profileManagerTab->ApplyConfiguration();
40 ui->filesystemTab->applyConfiguration();
40 ui->inputTab->ApplyConfiguration(); 41 ui->inputTab->ApplyConfiguration();
41 ui->hotkeysTab->ApplyConfiguration(registry); 42 ui->hotkeysTab->ApplyConfiguration(registry);
42 ui->graphicsTab->ApplyConfiguration(); 43 ui->graphicsTab->ApplyConfiguration();
@@ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
73void ConfigureDialog::PopulateSelectionList() { 74void ConfigureDialog::PopulateSelectionList() {
74 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ 75 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
75 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, 76 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
76 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}}, 77 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}},
77 {tr("Graphics"), {ui->graphicsTab}}, 78 {tr("Graphics"), {ui->graphicsTab}},
78 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 79 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
79 }; 80 };
@@ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
106 {ui->debugTab, tr("Debug")}, 107 {ui->debugTab, tr("Debug")},
107 {ui->webTab, tr("Web")}, 108 {ui->webTab, tr("Web")},
108 {ui->gameListTab, tr("Game List")}, 109 {ui->gameListTab, tr("Game List")},
110 {ui->filesystemTab, tr("Filesystem")},
109 }; 111 };
110 112
111 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); 113 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
new file mode 100644
index 000000000..29f540eb7
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -0,0 +1,177 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QFileDialog>
6#include <QMessageBox>
7#include "common/common_paths.h"
8#include "common/file_util.h"
9#include "core/settings.h"
10#include "ui_configure_filesystem.h"
11#include "yuzu/configuration/configure_filesystem.h"
12#include "yuzu/uisettings.h"
13
14namespace {
15
16template <typename T>
17void SetComboBoxFromData(QComboBox* combo_box, T data) {
18 const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
19 if (index >= combo_box->count() || index < 0)
20 return;
21
22 combo_box->setCurrentIndex(index);
23}
24
25} // Anonymous namespace
26
27ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
28 : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
29 ui->setupUi(this);
30 this->setConfiguration();
31
32 connect(ui->nand_directory_button, &QToolButton::pressed, this,
33 [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); });
34 connect(ui->sdmc_directory_button, &QToolButton::pressed, this,
35 [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); });
36 connect(ui->gamecard_path_button, &QToolButton::pressed, this,
37 [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); });
38 connect(ui->dump_path_button, &QToolButton::pressed, this,
39 [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); });
40 connect(ui->load_path_button, &QToolButton::pressed, this,
41 [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); });
42 connect(ui->cache_directory_button, &QToolButton::pressed, this,
43 [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); });
44
45 connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
46 &ConfigureFilesystem::ResetMetadata);
47
48 connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this,
49 &ConfigureFilesystem::UpdateEnabledControls);
50 connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this,
51 &ConfigureFilesystem::UpdateEnabledControls);
52}
53
54ConfigureFilesystem::~ConfigureFilesystem() = default;
55
56void ConfigureFilesystem::setConfiguration() {
57 ui->nand_directory_edit->setText(
58 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
59 ui->sdmc_directory_edit->setText(
60 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
61 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
62 ui->dump_path_edit->setText(
63 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
64 ui->load_path_edit->setText(
65 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
66 ui->cache_directory_edit->setText(
67 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
68
69 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
70 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
71 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
72 ui->dump_nso->setChecked(Settings::values.dump_nso);
73
74 ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
75
76 SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
77 SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
78 SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
79 SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
80
81 UpdateEnabledControls();
82}
83
84void ConfigureFilesystem::applyConfiguration() {
85 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
86 ui->nand_directory_edit->text().toStdString());
87 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
88 ui->sdmc_directory_edit->text().toStdString());
89 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString());
90 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString());
91 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
92 ui->cache_directory_edit->text().toStdString());
93 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
94
95 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
96 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
97 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
98 Settings::values.dump_nso = ui->dump_nso->isChecked();
99
100 UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
101
102 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
103 ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
104 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
105 ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
106 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
107 ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
108 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
109 ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
110}
111
112void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
113 QString caption;
114
115 switch (target) {
116 case DirectoryTarget::NAND:
117 caption = tr("Select Emulated NAND Directory...");
118 break;
119 case DirectoryTarget::SD:
120 caption = tr("Select Emulated SD Directory...");
121 break;
122 case DirectoryTarget::Gamecard:
123 caption = tr("Select Gamecard Path...");
124 break;
125 case DirectoryTarget::Dump:
126 caption = tr("Select Dump Directory...");
127 break;
128 case DirectoryTarget::Load:
129 caption = tr("Select Mod Load Directory...");
130 break;
131 case DirectoryTarget::Cache:
132 caption = tr("Select Cache Directory...");
133 break;
134 }
135
136 QString str;
137 if (target == DirectoryTarget::Gamecard) {
138 str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
139 QStringLiteral("NX Gamecard;*.xci"));
140 } else {
141 str = QFileDialog::getExistingDirectory(this, caption, edit->text());
142 }
143
144 if (str.isEmpty())
145 return;
146
147 edit->setText(str);
148}
149
150void ConfigureFilesystem::ResetMetadata() {
151 if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
152 "game_list")) {
153 QMessageBox::information(this, tr("Reset Metadata Cache"),
154 tr("The metadata cache is already empty."));
155 } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
156 DIR_SEP + "game_list")) {
157 QMessageBox::information(this, tr("Reset Metadata Cache"),
158 tr("The operation completed successfully."));
159 UISettings::values.is_game_list_reload_pending.exchange(true);
160 } else {
161 QMessageBox::warning(
162 this, tr("Reset Metadata Cache"),
163 tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
164 }
165}
166
167void ConfigureFilesystem::UpdateEnabledControls() {
168 ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked());
169 ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() &&
170 !ui->gamecard_current_game->isChecked());
171 ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() &&
172 !ui->gamecard_current_game->isChecked());
173}
174
175void ConfigureFilesystem::retranslateUi() {
176 ui->retranslateUi(this);
177}
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h
new file mode 100644
index 000000000..a79303760
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.h
@@ -0,0 +1,43 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10class QLineEdit;
11
12namespace Ui {
13class ConfigureFilesystem;
14}
15
16class ConfigureFilesystem : public QWidget {
17 Q_OBJECT
18
19public:
20 explicit ConfigureFilesystem(QWidget* parent = nullptr);
21 ~ConfigureFilesystem() override;
22
23 void applyConfiguration();
24 void retranslateUi();
25
26private:
27 void setConfiguration();
28
29 enum class DirectoryTarget {
30 NAND,
31 SD,
32 Gamecard,
33 Dump,
34 Load,
35 Cache,
36 };
37
38 void SetDirectory(DirectoryTarget target, QLineEdit* edit);
39 void ResetMetadata();
40 void UpdateEnabledControls();
41
42 std::unique_ptr<Ui::ConfigureFilesystem> ui;
43};
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
new file mode 100644
index 000000000..58cd07f52
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -0,0 +1,395 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureFilesystem</class>
4 <widget class="QWidget" name="ConfigureFilesystem">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>453</width>
10 <height>561</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="title">
22 <string>Storage Directories</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="0" column="0">
26 <widget class="QLabel" name="label">
27 <property name="text">
28 <string>NAND</string>
29 </property>
30 </widget>
31 </item>
32 <item row="0" column="3">
33 <widget class="QToolButton" name="nand_directory_button">
34 <property name="text">
35 <string>...</string>
36 </property>
37 </widget>
38 </item>
39 <item row="0" column="2">
40 <widget class="QLineEdit" name="nand_directory_edit"/>
41 </item>
42 <item row="1" column="2">
43 <widget class="QLineEdit" name="sdmc_directory_edit"/>
44 </item>
45 <item row="1" column="0">
46 <widget class="QLabel" name="label_2">
47 <property name="text">
48 <string>SD Card</string>
49 </property>
50 </widget>
51 </item>
52 <item row="1" column="3">
53 <widget class="QToolButton" name="sdmc_directory_button">
54 <property name="text">
55 <string>...</string>
56 </property>
57 </widget>
58 </item>
59 <item row="0" column="1">
60 <spacer name="horizontalSpacer">
61 <property name="orientation">
62 <enum>Qt::Horizontal</enum>
63 </property>
64 <property name="sizeType">
65 <enum>QSizePolicy::Maximum</enum>
66 </property>
67 <property name="sizeHint" stdset="0">
68 <size>
69 <width>60</width>
70 <height>20</height>
71 </size>
72 </property>
73 </spacer>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 <item>
79 <widget class="QGroupBox" name="groupBox_2">
80 <property name="title">
81 <string>Gamecard</string>
82 </property>
83 <layout class="QGridLayout" name="gridLayout_2">
84 <item row="2" column="1">
85 <widget class="QLabel" name="label_3">
86 <property name="text">
87 <string>Path</string>
88 </property>
89 </widget>
90 </item>
91 <item row="2" column="2">
92 <widget class="QLineEdit" name="gamecard_path_edit"/>
93 </item>
94 <item row="0" column="1">
95 <widget class="QCheckBox" name="gamecard_inserted">
96 <property name="text">
97 <string>Inserted</string>
98 </property>
99 </widget>
100 </item>
101 <item row="1" column="1">
102 <widget class="QCheckBox" name="gamecard_current_game">
103 <property name="text">
104 <string>Current Game</string>
105 </property>
106 </widget>
107 </item>
108 <item row="2" column="3">
109 <widget class="QToolButton" name="gamecard_path_button">
110 <property name="text">
111 <string>...</string>
112 </property>
113 </widget>
114 </item>
115 </layout>
116 </widget>
117 </item>
118 <item>
119 <widget class="QGroupBox" name="groupBox_3">
120 <property name="title">
121 <string>Storage Sizes</string>
122 </property>
123 <layout class="QGridLayout" name="gridLayout_3">
124 <item row="3" column="0">
125 <widget class="QLabel" name="label_5">
126 <property name="text">
127 <string>SD Card</string>
128 </property>
129 </widget>
130 </item>
131 <item row="1" column="0">
132 <widget class="QLabel" name="label_4">
133 <property name="text">
134 <string>System NAND</string>
135 </property>
136 </widget>
137 </item>
138 <item row="1" column="1">
139 <widget class="QComboBox" name="sysnand_size">
140 <item>
141 <property name="text">
142 <string>2.5 GB</string>
143 </property>
144 </item>
145 </widget>
146 </item>
147 <item row="3" column="1">
148 <widget class="QComboBox" name="sdmc_size">
149 <property name="currentText">
150 <string>32 GB</string>
151 </property>
152 <item>
153 <property name="text">
154 <string>1 GB</string>
155 </property>
156 </item>
157 <item>
158 <property name="text">
159 <string>2 GB</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>4 GB</string>
165 </property>
166 </item>
167 <item>
168 <property name="text">
169 <string>8 GB</string>
170 </property>
171 </item>
172 <item>
173 <property name="text">
174 <string>16 GB</string>
175 </property>
176 </item>
177 <item>
178 <property name="text">
179 <string>32 GB</string>
180 </property>
181 </item>
182 <item>
183 <property name="text">
184 <string>64 GB</string>
185 </property>
186 </item>
187 <item>
188 <property name="text">
189 <string>128 GB</string>
190 </property>
191 </item>
192 <item>
193 <property name="text">
194 <string>256 GB</string>
195 </property>
196 </item>
197 <item>
198 <property name="text">
199 <string>1 TB</string>
200 </property>
201 </item>
202 </widget>
203 </item>
204 <item row="2" column="1">
205 <widget class="QComboBox" name="usrnand_size">
206 <item>
207 <property name="text">
208 <string>26 GB</string>
209 </property>
210 </item>
211 </widget>
212 </item>
213 <item row="2" column="0">
214 <widget class="QLabel" name="label_6">
215 <property name="text">
216 <string>User NAND</string>
217 </property>
218 </widget>
219 </item>
220 <item row="0" column="0">
221 <widget class="QLabel" name="label_7">
222 <property name="text">
223 <string>NAND</string>
224 </property>
225 </widget>
226 </item>
227 <item row="0" column="1">
228 <widget class="QComboBox" name="nand_size">
229 <item>
230 <property name="text">
231 <string>29.1 GB</string>
232 </property>
233 </item>
234 </widget>
235 </item>
236 </layout>
237 </widget>
238 </item>
239 <item>
240 <widget class="QGroupBox" name="groupBox_4">
241 <property name="title">
242 <string>Patch Manager</string>
243 </property>
244 <layout class="QGridLayout" name="gridLayout_4">
245 <item row="1" column="2">
246 <widget class="QLineEdit" name="load_path_edit"/>
247 </item>
248 <item row="0" column="2">
249 <widget class="QLineEdit" name="dump_path_edit"/>
250 </item>
251 <item row="0" column="3">
252 <widget class="QToolButton" name="dump_path_button">
253 <property name="text">
254 <string>...</string>
255 </property>
256 </widget>
257 </item>
258 <item row="1" column="3">
259 <widget class="QToolButton" name="load_path_button">
260 <property name="text">
261 <string>...</string>
262 </property>
263 </widget>
264 </item>
265 <item row="2" column="0" colspan="4">
266 <layout class="QHBoxLayout" name="horizontalLayout">
267 <item>
268 <widget class="QCheckBox" name="dump_nso">
269 <property name="text">
270 <string>Dump Decompressed NSOs</string>
271 </property>
272 </widget>
273 </item>
274 <item>
275 <widget class="QCheckBox" name="dump_exefs">
276 <property name="text">
277 <string>Dump ExeFS</string>
278 </property>
279 </widget>
280 </item>
281 </layout>
282 </item>
283 <item row="1" column="0">
284 <widget class="QLabel" name="label_9">
285 <property name="text">
286 <string>Mod Load Root</string>
287 </property>
288 </widget>
289 </item>
290 <item row="0" column="0">
291 <widget class="QLabel" name="label_8">
292 <property name="text">
293 <string>Dump Root</string>
294 </property>
295 </widget>
296 </item>
297 <item row="0" column="1">
298 <spacer name="horizontalSpacer_2">
299 <property name="orientation">
300 <enum>Qt::Horizontal</enum>
301 </property>
302 <property name="sizeType">
303 <enum>QSizePolicy::Fixed</enum>
304 </property>
305 <property name="sizeHint" stdset="0">
306 <size>
307 <width>40</width>
308 <height>20</height>
309 </size>
310 </property>
311 </spacer>
312 </item>
313 </layout>
314 </widget>
315 </item>
316 <item>
317 <widget class="QGroupBox" name="groupBox_5">
318 <property name="title">
319 <string>Caching</string>
320 </property>
321 <layout class="QGridLayout" name="gridLayout_5">
322 <item row="0" column="0">
323 <widget class="QLabel" name="label_10">
324 <property name="text">
325 <string>Cache Directory</string>
326 </property>
327 </widget>
328 </item>
329 <item row="0" column="1">
330 <spacer name="horizontalSpacer_3">
331 <property name="orientation">
332 <enum>Qt::Horizontal</enum>
333 </property>
334 <property name="sizeType">
335 <enum>QSizePolicy::Fixed</enum>
336 </property>
337 <property name="sizeHint" stdset="0">
338 <size>
339 <width>40</width>
340 <height>20</height>
341 </size>
342 </property>
343 </spacer>
344 </item>
345 <item row="0" column="2">
346 <widget class="QLineEdit" name="cache_directory_edit"/>
347 </item>
348 <item row="0" column="3">
349 <widget class="QToolButton" name="cache_directory_button">
350 <property name="text">
351 <string>...</string>
352 </property>
353 </widget>
354 </item>
355 <item row="1" column="0" colspan="4">
356 <layout class="QHBoxLayout" name="horizontalLayout_2">
357 <item>
358 <widget class="QCheckBox" name="cache_game_list">
359 <property name="text">
360 <string>Cache Game List Metadata</string>
361 </property>
362 </widget>
363 </item>
364 <item>
365 <widget class="QPushButton" name="reset_game_list_cache">
366 <property name="text">
367 <string>Reset Metadata Cache</string>
368 </property>
369 </widget>
370 </item>
371 </layout>
372 </item>
373 </layout>
374 </widget>
375 </item>
376 </layout>
377 </item>
378 <item>
379 <spacer name="verticalSpacer">
380 <property name="orientation">
381 <enum>Qt::Vertical</enum>
382 </property>
383 <property name="sizeHint" stdset="0">
384 <size>
385 <width>20</width>
386 <height>40</height>
387 </size>
388 </property>
389 </spacer>
390 </item>
391 </layout>
392 </widget>
393 <resources/>
394 <connections/>
395</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 10bcd650e..98bc9b391 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QCheckBox>
6#include <QSpinBox>
5#include "core/core.h" 7#include "core/core.h"
6#include "core/settings.h" 8#include "core/settings.h"
7#include "ui_configure_general.h" 9#include "ui_configure_general.h"
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7613197f2..f2977719c 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -182,6 +182,8 @@ void ConfigureInput::UpdateUIEnabled() {
182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); 182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
183 } 183 }
184 184
185 ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
186 !ui->use_docked_mode->isChecked());
185 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); 187 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
186 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && 188 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
187 !ui->use_docked_mode->isChecked()); 189 !ui->use_docked_mode->isChecked());
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 8304c6517..2d82df739 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -22,6 +22,8 @@
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/scope_acquire_window_context.h" 23#include "core/frontend/scope_acquire_window_context.h"
24#include "core/hle/service/acc/profile_manager.h" 24#include "core/hle/service/acc/profile_manager.h"
25#include "core/hle/service/am/applet_ae.h"
26#include "core/hle/service/am/applet_oe.h"
25#include "core/hle/service/am/applets/applets.h" 27#include "core/hle/service/am/applets/applets.h"
26#include "core/hle/service/hid/controllers/npad.h" 28#include "core/hle/service/hid/controllers/npad.h"
27#include "core/hle/service/hid/hid.h" 29#include "core/hle/service/hid/hid.h"
@@ -54,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
54#include <QProgressDialog> 56#include <QProgressDialog>
55#include <QShortcut> 57#include <QShortcut>
56#include <QStatusBar> 58#include <QStatusBar>
59#include <QSysInfo>
57#include <QtConcurrent/QtConcurrent> 60#include <QtConcurrent/QtConcurrent>
58 61
59#include <fmt/format.h> 62#include <fmt/format.h>
@@ -66,6 +69,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
66#include "common/microprofile.h" 69#include "common/microprofile.h"
67#include "common/scm_rev.h" 70#include "common/scm_rev.h"
68#include "common/scope_exit.h" 71#include "common/scope_exit.h"
72#ifdef ARCHITECTURE_x86_64
73#include "common/x64/cpu_detect.h"
74#endif
69#include "common/telemetry.h" 75#include "common/telemetry.h"
70#include "core/core.h" 76#include "core/core.h"
71#include "core/crypto/key_manager.h" 77#include "core/crypto/key_manager.h"
@@ -79,6 +85,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
79#include "core/file_sys/submission_package.h" 85#include "core/file_sys/submission_package.h"
80#include "core/frontend/applets/software_keyboard.h" 86#include "core/frontend/applets/software_keyboard.h"
81#include "core/hle/kernel/process.h" 87#include "core/hle/kernel/process.h"
88#include "core/hle/service/am/am.h"
82#include "core/hle/service/filesystem/filesystem.h" 89#include "core/hle/service/filesystem/filesystem.h"
83#include "core/hle/service/nfp/nfp.h" 90#include "core/hle/service/nfp/nfp.h"
84#include "core/hle/service/sm/sm.h" 91#include "core/hle/service/sm/sm.h"
@@ -205,6 +212,10 @@ GMainWindow::GMainWindow()
205 212
206 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, 213 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
207 Common::g_scm_desc); 214 Common::g_scm_desc);
215#ifdef ARCHITECTURE_x86_64
216 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
217#endif
218 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
208 UpdateWindowTitle(); 219 UpdateWindowTitle();
209 220
210 show(); 221 show();
@@ -213,7 +224,7 @@ GMainWindow::GMainWindow()
213 std::make_unique<FileSys::ContentProviderUnion>()); 224 std::make_unique<FileSys::ContentProviderUnion>());
214 Core::System::GetInstance().RegisterContentProvider( 225 Core::System::GetInstance().RegisterContentProvider(
215 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); 226 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
216 Service::FileSystem::CreateFactories(*vfs); 227 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
217 228
218 // Gen keys if necessary 229 // Gen keys if necessary
219 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 230 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
@@ -964,11 +975,11 @@ void GMainWindow::BootGame(const QString& filename) {
964 } 975 }
965 status_bar_update_timer.start(2000); 976 status_bar_update_timer.start(2000);
966 977
978 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
979
967 std::string title_name; 980 std::string title_name;
968 const auto res = Core::System::GetInstance().GetGameName(title_name); 981 const auto res = Core::System::GetInstance().GetGameName(title_name);
969 if (res != Loader::ResultStatus::Success) { 982 if (res != Loader::ResultStatus::Success) {
970 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
971
972 const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata(); 983 const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata();
973 if (nacp != nullptr) 984 if (nacp != nullptr)
974 title_name = nacp->GetApplicationName(); 985 title_name = nacp->GetApplicationName();
@@ -976,7 +987,7 @@ void GMainWindow::BootGame(const QString& filename) {
976 if (title_name.empty()) 987 if (title_name.empty())
977 title_name = FileUtil::GetFilename(filename.toStdString()); 988 title_name = FileUtil::GetFilename(filename.toStdString());
978 } 989 }
979 990 LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name);
980 UpdateWindowTitle(QString::fromStdString(title_name)); 991 UpdateWindowTitle(QString::fromStdString(title_name));
981 992
982 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); 993 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
@@ -1499,15 +1510,19 @@ void GMainWindow::OnMenuInstallToNAND() {
1499 failed(); 1510 failed();
1500 return; 1511 return;
1501 } 1512 }
1502 const auto res = 1513 const auto res = Core::System::GetInstance()
1503 Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy); 1514 .GetFileSystemController()
1515 .GetUserNANDContents()
1516 ->InstallEntry(*nsp, false, qt_raw_copy);
1504 if (res == FileSys::InstallResult::Success) { 1517 if (res == FileSys::InstallResult::Success) {
1505 success(); 1518 success();
1506 } else { 1519 } else {
1507 if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1520 if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1508 if (overwrite()) { 1521 if (overwrite()) {
1509 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1522 const auto res2 = Core::System::GetInstance()
1510 *nsp, true, qt_raw_copy); 1523 .GetFileSystemController()
1524 .GetUserNANDContents()
1525 ->InstallEntry(*nsp, true, qt_raw_copy);
1511 if (res2 == FileSys::InstallResult::Success) { 1526 if (res2 == FileSys::InstallResult::Success) {
1512 success(); 1527 success();
1513 } else { 1528 } else {
@@ -1561,19 +1576,28 @@ void GMainWindow::OnMenuInstallToNAND() {
1561 1576
1562 FileSys::InstallResult res; 1577 FileSys::InstallResult res;
1563 if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { 1578 if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
1564 res = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1579 res = Core::System::GetInstance()
1565 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); 1580 .GetFileSystemController()
1581 .GetUserNANDContents()
1582 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1583 qt_raw_copy);
1566 } else { 1584 } else {
1567 res = Service::FileSystem::GetSystemNANDContents()->InstallEntry( 1585 res = Core::System::GetInstance()
1568 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); 1586 .GetFileSystemController()
1587 .GetSystemNANDContents()
1588 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1589 qt_raw_copy);
1569 } 1590 }
1570 1591
1571 if (res == FileSys::InstallResult::Success) { 1592 if (res == FileSys::InstallResult::Success) {
1572 success(); 1593 success();
1573 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1594 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1574 if (overwrite()) { 1595 if (overwrite()) {
1575 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1596 const auto res2 = Core::System::GetInstance()
1576 *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); 1597 .GetFileSystemController()
1598 .GetUserNANDContents()
1599 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
1600 true, qt_raw_copy);
1577 if (res2 == FileSys::InstallResult::Success) { 1601 if (res2 == FileSys::InstallResult::Success) {
1578 success(); 1602 success();
1579 } else { 1603 } else {
@@ -1603,7 +1627,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1603 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1627 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1604 : FileUtil::UserPath::NANDDir, 1628 : FileUtil::UserPath::NANDDir,
1605 dir_path.toStdString()); 1629 dir_path.toStdString());
1606 Service::FileSystem::CreateFactories(*vfs); 1630 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
1607 game_list->PopulateAsync(UISettings::values.game_dirs); 1631 game_list->PopulateAsync(UISettings::values.game_dirs);
1608 } 1632 }
1609} 1633}
@@ -1653,6 +1677,11 @@ void GMainWindow::OnStartGame() {
1653} 1677}
1654 1678
1655void GMainWindow::OnPauseGame() { 1679void GMainWindow::OnPauseGame() {
1680 Core::System& system{Core::System::GetInstance()};
1681 if (system.GetExitLock() && !ConfirmForceLockedExit()) {
1682 return;
1683 }
1684
1656 emu_thread->SetRunning(false); 1685 emu_thread->SetRunning(false);
1657 1686
1658 ui.action_Start->setEnabled(true); 1687 ui.action_Start->setEnabled(true);
@@ -1664,6 +1693,11 @@ void GMainWindow::OnPauseGame() {
1664} 1693}
1665 1694
1666void GMainWindow::OnStopGame() { 1695void GMainWindow::OnStopGame() {
1696 Core::System& system{Core::System::GetInstance()};
1697 if (system.GetExitLock() && !ConfirmForceLockedExit()) {
1698 return;
1699 }
1700
1667 ShutdownGame(); 1701 ShutdownGame();
1668} 1702}
1669 1703
@@ -1988,7 +2022,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1988 2022
1989 const auto function = [this, &keys, &pdm] { 2023 const auto function = [this, &keys, &pdm] {
1990 keys.PopulateFromPartitionData(pdm); 2024 keys.PopulateFromPartitionData(pdm);
1991 Service::FileSystem::CreateFactories(*vfs); 2025 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
1992 keys.DeriveETicket(pdm); 2026 keys.DeriveETicket(pdm);
1993 }; 2027 };
1994 2028
@@ -2033,7 +2067,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2033 prog.close(); 2067 prog.close();
2034 } 2068 }
2035 2069
2036 Service::FileSystem::CreateFactories(*vfs); 2070 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
2037 2071
2038 if (behavior == ReinitializeKeyBehavior::Warning) { 2072 if (behavior == ReinitializeKeyBehavior::Warning) {
2039 game_list->PopulateAsync(UISettings::values.game_dirs); 2073 game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -2161,13 +2195,41 @@ bool GMainWindow::ConfirmChangeGame() {
2161 if (emu_thread == nullptr) 2195 if (emu_thread == nullptr)
2162 return true; 2196 return true;
2163 2197
2164 auto answer = QMessageBox::question( 2198 const auto answer = QMessageBox::question(
2165 this, tr("yuzu"), 2199 this, tr("yuzu"),
2166 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), 2200 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
2167 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 2201 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
2168 return answer != QMessageBox::No; 2202 return answer != QMessageBox::No;
2169} 2203}
2170 2204
2205bool GMainWindow::ConfirmForceLockedExit() {
2206 if (emu_thread == nullptr)
2207 return true;
2208
2209 const auto answer =
2210 QMessageBox::question(this, tr("yuzu"),
2211 tr("The currently running application has requested yuzu to not "
2212 "exit.\n\nWould you like to bypass this and exit anyway?"),
2213 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
2214 return answer != QMessageBox::No;
2215}
2216
2217void GMainWindow::RequestGameExit() {
2218 auto& sm{Core::System::GetInstance().ServiceManager()};
2219 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
2220 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
2221 bool has_signalled = false;
2222
2223 if (applet_oe != nullptr) {
2224 applet_oe->GetMessageQueue()->RequestExit();
2225 has_signalled = true;
2226 }
2227
2228 if (applet_ae != nullptr && !has_signalled) {
2229 applet_ae->GetMessageQueue()->RequestExit();
2230 }
2231}
2232
2171void GMainWindow::filterBarSetChecked(bool state) { 2233void GMainWindow::filterBarSetChecked(bool state) {
2172 ui.action_Show_Filter_Bar->setChecked(state); 2234 ui.action_Show_Filter_Bar->setChecked(state);
2173 emit(OnToggleFilterBar()); 2235 emit(OnToggleFilterBar());
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7d16188cb..e942d1248 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -172,6 +172,8 @@ private:
172 */ 172 */
173 bool ConfirmClose(); 173 bool ConfirmClose();
174 bool ConfirmChangeGame(); 174 bool ConfirmChangeGame();
175 bool ConfirmForceLockedExit();
176 void RequestGameExit();
175 void closeEvent(QCloseEvent* event) override; 177 void closeEvent(QCloseEvent* event) override;
176 178
177private slots: 179private slots:
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 067d58d80..d82438502 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -316,6 +316,29 @@ void Config::ReadValues() {
316 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 316 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
317 sdl2_config->Get("Data Storage", "sdmc_directory", 317 sdl2_config->Get("Data Storage", "sdmc_directory",
318 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 318 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
319 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir,
320 sdl2_config->Get("Data Storage", "load_directory",
321 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
322 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir,
323 sdl2_config->Get("Data Storage", "dump_directory",
324 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
325 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
326 sdl2_config->Get("Data Storage", "cache_directory",
327 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
328 Settings::values.gamecard_inserted =
329 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
330 Settings::values.gamecard_current_game =
331 sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
332 Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
333 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
334 "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
335 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
336 "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
337 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
338 sdl2_config->GetInteger("Data Storage", "nand_system_size",
339 static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
340 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
341 "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
319 342
320 // System 343 // System
321 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 344 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
@@ -374,6 +397,8 @@ void Config::ReadValues() {
374 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 397 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
375 398
376 // Debugging 399 // Debugging
400 Settings::values.record_frame_times =
401 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
377 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 402 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
378 Settings::values.gdbstub_port = 403 Settings::values.gdbstub_port =
379 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 404 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 0cfc111a6..a6171c3ed 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -173,6 +173,20 @@ volume =
173# 1 (default): Yes, 0: No 173# 1 (default): Yes, 0: No
174use_virtual_sd = 174use_virtual_sd =
175 175
176# Whether or not to enable gamecard emulation
177# 1: Yes, 0 (default): No
178gamecard_inserted =
179
180# Whether or not the gamecard should be emulated as the current game
181# If 'gamecard_inserted' is 0 this setting is irrelevant
182# 1: Yes, 0 (default): No
183gamecard_current_game =
184
185# Path to an XCI file to use as the gamecard
186# If 'gamecard_inserted' is 0 this setting is irrelevant
187# If 'gamecard_current_game' is 1 this setting is irrelevant
188gamecard_path =
189
176[System] 190[System]
177# Whether the system is docked 191# Whether the system is docked
178# 1: Yes, 0 (default): No 192# 1: Yes, 0 (default): No
@@ -213,6 +227,8 @@ region_value =
213log_filter = *:Trace 227log_filter = *:Trace
214 228
215[Debugging] 229[Debugging]
230# Record frame time data, can be found in the log directory. Boolean value
231record_frame_times =
216# Port for listening to GDB connections. 232# Port for listening to GDB connections.
217use_gdbstub=false 233use_gdbstub=false
218gdbstub_port=24689 234gdbstub_port=24689
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 129d8ca73..bac05b959 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -184,7 +184,7 @@ int main(int argc, char** argv) {
184 Core::System& system{Core::System::GetInstance()}; 184 Core::System& system{Core::System::GetInstance()};
185 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 185 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
186 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 186 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
187 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 187 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
188 188
189 SCOPE_EXIT({ system.Shutdown(); }); 189 SCOPE_EXIT({ system.Shutdown(); });
190 190
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 0ee97aa54..94ad50cb3 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -22,6 +22,7 @@
22#include "common/telemetry.h" 22#include "common/telemetry.h"
23#include "core/core.h" 23#include "core/core.h"
24#include "core/crypto/key_manager.h" 24#include "core/crypto/key_manager.h"
25#include "core/file_sys/registered_cache.h"
25#include "core/file_sys/vfs_real.h" 26#include "core/file_sys/vfs_real.h"
26#include "core/hle/service/filesystem/filesystem.h" 27#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h" 28#include "core/loader/loader.h"
@@ -216,8 +217,9 @@ int main(int argc, char** argv) {
216 }; 217 };
217 218
218 Core::System& system{Core::System::GetInstance()}; 219 Core::System& system{Core::System::GetInstance()};
220 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
219 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 221 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
220 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 222 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
221 223
222 SCOPE_EXIT({ system.Shutdown(); }); 224 SCOPE_EXIT({ system.Shutdown(); });
223 225