summaryrefslogtreecommitdiffstats
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 @@
+steps:
+ - script: mkdir artifacts || echo 'X' > artifacts/T1.txt
+ - publish: artifacts
+ artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
+ 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:
cache: 'false'
steps:
+- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
+ displayName: 'Determine Build Name'
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:
dockerVersion: '17.09.0-ce'
-- ${{ if eq(parameters.cache, 'true') }}:
- - task: CacheBeta@0
- displayName: 'Cache Build System'
- inputs:
- key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
- path: $(System.DefaultWorkingDirectory)/ccache
- cacheHitVar: CACHE_RESTORED
+- task: CacheBeta@0
+ displayName: 'Cache Build System'
+ inputs:
+ key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
+ path: $(System.DefaultWorkingDirectory)/ccache
+ cacheHitVar: CACHE_RESTORED
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
displayName: 'Build'
- 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 @@
+steps:
+ - task: DownloadPipelineArtifact@2
+ displayName: 'Download Windows Release'
+ inputs:
+ artifactName: 'yuzu-$(BuildName)-windows-mingw'
+ buildType: 'current'
+ targetPath: '$(Build.ArtifactStagingDirectory)'
+ - task: DownloadPipelineArtifact@2
+ displayName: 'Download Linux Release'
+ inputs:
+ artifactName: 'yuzu-$(BuildName)-linux'
+ buildType: 'current'
+ 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 @@
+steps:
+ - template: ./release-download.yml
+ - task: GitHubRelease@0
+ inputs:
+ action: 'create'
+ title: 'yuzu $(BuildName) #$(Build.BuildId)'
+ assets: '$(Build.ArtifactStagingDirectory)/*'
+ gitHubConnection: $(GitHubReleaseConnectionName)
+ repositoryName: '$(Build.Repository.Name)'
+ target: '$(Build.SourceVersion)'
+ 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 @@
+steps:
+ - template: ./release-download.yml
+ - task: UniversalPackages@0
+ displayName: Publish Artifacts
+ inputs:
+ command: publish
+ publishDirectory: '$(Build.ArtifactStagingDirectory)'
+ vstsFeedPublish: 'yuzu-$(BuildName)'
+ vstsFeedPackagePublish: 'main'
+ 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 @@
+trigger:
+- master
+
+stages:
+- stage: merge
+ displayName: 'merge'
+ jobs:
+ - 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:
- master
stages:
-- stage: merge
- displayName: 'merge'
- jobs:
- - template: ./templates/merge.yml
- stage: format
- dependsOn: merge
displayName: 'format'
jobs:
- job: format
@@ -17,9 +12,17 @@ stages:
steps:
- template: ./templates/format-check.yml
- stage: build
- displayName: 'build'
dependsOn: format
+ displayName: 'build'
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
+- stage: release
+ displayName: 'Release'
+ dependsOn: build
+ jobs:
+ - job: github
+ displayName: 'GitHub Release'
+ steps:
+ - 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
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
-It 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.
+It 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.
-yuzu 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.
+yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
+
+Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
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 {
return stream->GetState();
}
+static constexpr u32 VersionFromRevision(u32_le rev) {
+ // "REV7" -> 7
+ return ((rev >> 24) & 0xff) - 0x30;
+}
+
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
@@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
+ const auto audren_revision = VersionFromRevision(config.revision);
+ if (audren_revision >= 5) {
+ response_data.frame_count = 0x10;
+ response_data.total_size += 0x10;
+ }
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// 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 {
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
+ frame_count = 0;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
- u32_le revision;
- u32_le behavior_size;
- u32_le memory_pools_size;
- u32_le voices_size;
- u32_le voice_resource_size;
- u32_le effects_size;
- u32_le mixes_size;
- u32_le sinks_size;
- u32_le performance_manager_size;
- INSERT_PADDING_WORDS(6);
- u32_le total_size;
+ u32_le revision{};
+ u32_le behavior_size{};
+ u32_le memory_pools_size{};
+ u32_le voices_size{};
+ u32_le voice_resource_size{};
+ u32_le effects_size{};
+ u32_le mixes_size{};
+ u32_le sinks_size{};
+ u32_le performance_manager_size{};
+ INSERT_PADDING_WORDS(1);
+ u32_le frame_count{};
+ INSERT_PADDING_WORDS(4);
+ u32_le total_size{};
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
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})
elseif(DEFINED ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
+ elseif(DEFINED ENV{AZURE})
+ set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
+ set(BUILD_TAG $ENV{AZURE_REPO_TAG})
endif()
endif()
add_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) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
+ CLS(CheatEngine) \
CLS(Crypto) \
CLS(WebService)
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 {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
+ CheatEngine, ///< Memory manipulation and engine VM functions
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
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
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
- file_sys/cheat_engine.cpp
- file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -477,6 +475,11 @@ add_library(core STATIC
loader/nsp.h
loader/xci.cpp
loader/xci.h
+ memory/cheat_engine.cpp
+ memory/cheat_engine.h
+ memory/dmnt_cheat_types.h
+ memory/dmnt_cheat_vm.cpp
+ memory/dmnt_cheat_vm.h
memory.cpp
memory.h
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 @@
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
+#include "core/file_sys/bis_factory.h"
+#include "core/file_sys/card_image.h"
#include "core/file_sys/mode.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs_factory.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
@@ -27,17 +33,17 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
+#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
+#include "core/memory/cheat_engine.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
-#include "file_sys/cheat_engine.h"
-#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -157,13 +163,10 @@ struct System::Impl {
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
+ exit_lock = false;
LOG_DEBUG(Core, "Initialized OK");
- // Reset counters and set time origin to current frame
- GetAndResetPerfStats();
- perf_stats.BeginSystemFrame();
-
return ResultStatus::Success;
}
@@ -202,10 +205,34 @@ struct System::Impl {
gpu_core->Start();
cpu_core_manager.StartThreads();
+ // Initialize cheat engine
+ if (cheat_engine) {
+ cheat_engine->Initialize();
+ }
+
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
+ if (Settings::values.gamecard_inserted) {
+ if (Settings::values.gamecard_current_game) {
+ fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
+ } else if (!Settings::values.gamecard_path.empty()) {
+ fs_controller.SetGameCard(
+ GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
+ }
+ }
+
+ u64 title_id{0};
+ if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
+ LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
+ static_cast<u32>(load_result));
+ }
+ perf_stats = std::make_unique<PerfStats>(title_id);
+ // Reset counters and set time origin to current frame
+ GetAndResetPerfStats();
+ perf_stats->BeginSystemFrame();
+
status = ResultStatus::Success;
return status;
}
@@ -219,8 +246,11 @@ struct System::Impl {
perf_results.game_fps);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
+ telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
+ perf_stats->GetMeanFrametime());
is_powered_on = false;
+ exit_lock = false;
// Shutdown emulation session
renderer.reset();
@@ -229,6 +259,7 @@ struct System::Impl {
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
+ perf_stats.reset();
gpu_core.reset();
// Close all CPU/threading state
@@ -286,7 +317,7 @@ struct System::Impl {
}
PerfStatsResults GetAndResetPerfStats() {
- return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
+ return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
Timing::CoreTiming core_timing;
@@ -295,6 +326,7 @@ struct System::Impl {
FileSys::VirtualFilesystem virtual_filesystem;
/// ContentProviderUnion instance
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
+ Service::FileSystem::FileSystemController fs_controller;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -303,8 +335,9 @@ struct System::Impl {
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
+ bool exit_lock = false;
- std::unique_ptr<FileSys::CheatEngine> cheat_engine;
+ std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
/// Frontend applets
@@ -327,7 +360,7 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
- Core::PerfStats perf_stats;
+ std::unique_ptr<Core::PerfStats> perf_stats;
Core::FrameLimiter frame_limiter;
};
@@ -480,11 +513,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
}
Core::PerfStats& System::GetPerfStats() {
- return impl->perf_stats;
+ return *impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
- return impl->perf_stats;
+ return *impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
@@ -519,13 +552,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
-void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
- const std::string& build_id, VAddr code_region_start,
- VAddr code_region_end) {
- impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
- code_region_start, code_region_end);
-}
-
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
@@ -534,6 +560,13 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
+void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
+ const std::array<u8, 32>& build_id, VAddr main_region_begin,
+ u64 main_region_size) {
+ impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
+ impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
+}
+
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
}
@@ -562,6 +595,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
return *impl->content_provider;
}
+Service::FileSystem::FileSystemController& System::GetFileSystemController() {
+ return impl->fs_controller;
+}
+
+const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
+ return impl->fs_controller;
+}
+
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider) {
impl->content_provider->SetSlot(slot, provider);
@@ -591,6 +632,14 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
+void System::SetExitLock(bool locked) {
+ impl->exit_lock = locked;
+}
+
+bool System::GetExitLock() const {
+ return impl->exit_lock;
+}
+
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}
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;
} // namespace Core::Frontend
namespace FileSys {
-class CheatList;
class ContentProvider;
class ContentProviderUnion;
enum class ContentProviderUnionSlot;
@@ -36,6 +35,10 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
+namespace Memory {
+struct CheatEntry;
+} // namespace Memory
+
namespace Service {
namespace AM::Applets {
@@ -47,6 +50,10 @@ namespace APM {
class Controller;
}
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
namespace Glue {
class ARPManager;
}
@@ -282,8 +289,9 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
- void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
- VAddr code_region_start, VAddr code_region_end);
+ void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
+ const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
+ u64 main_region_size);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
@@ -299,6 +307,10 @@ public:
const FileSys::ContentProvider& GetContentProvider() const;
+ Service::FileSystem::FileSystemController& GetFileSystemController();
+
+ const Service::FileSystem::FileSystemController& GetFileSystemController() const;
+
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider);
@@ -314,6 +326,10 @@ public:
const Service::APM::Controller& GetAPMController() const;
+ void SetExitLock(bool locked);
+
+ bool GetExitLock() const;
+
private:
System();
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) {
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
}
+FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
+ return prodinfo_decrypted;
+}
+
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
std::array<u8, 0x240> out{};
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:
bool HasProdInfo() const;
FileSys::VirtualFile GetProdInfoRaw() const;
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
+ FileSys::VirtualFile GetDecryptedProdInfo() const;
std::array<u8, 0x240> GetETicketExtendedKek() const;
private:
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 @@
// Refer to the license.txt file included.
#include <fmt/format.h>
+#include "common/file_util.h"
+#include "core/core.h"
#include "core/file_sys/bis_factory.h"
+#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
+#include "core/settings.h"
namespace FileSys {
@@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_unique<RegisteredCache>(
- GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
+ GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
+ sysnand_placeholder(std::make_unique<PlaceholderCache>(
+ GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
+ usrnand_placeholder(std::make_unique<PlaceholderCache>(
+ GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
BISFactory::~BISFactory() = default;
+VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
+ return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
+}
+
+VirtualDir BISFactory::GetUserNANDContentDirectory() const {
+ return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
+}
+
RegisteredCache* BISFactory::GetSystemNANDContents() const {
return sysnand_cache.get();
}
@@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
return usrnand_cache.get();
}
+PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
+ return sysnand_placeholder.get();
+}
+
+PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
+ return usrnand_placeholder.get();
+}
+
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
// LayeredFS doesn't work on updates and title id-less homebrew
- if (title_id == 0 || (title_id & 0x800) > 0)
+ if (title_id == 0 || (title_id & 0xFFF) == 0x800)
return nullptr;
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
@@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
}
+VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
+ switch (id) {
+ case BisPartitionId::CalibrationFile:
+ return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
+ case BisPartitionId::SafeMode:
+ return GetOrCreateDirectoryRelative(nand_root, "/safe");
+ case BisPartitionId::System:
+ return GetOrCreateDirectoryRelative(nand_root, "/system");
+ case BisPartitionId::User:
+ return GetOrCreateDirectoryRelative(nand_root, "/user");
+ default:
+ return nullptr;
+ }
+}
+
+VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
+ Core::Crypto::KeyManager keys;
+ Core::Crypto::PartitionDataManager pdm{
+ Core::System::GetInstance().GetFilesystem()->OpenDirectory(
+ FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
+ keys.PopulateFromPartitionData(pdm);
+
+ switch (id) {
+ case BisPartitionId::CalibrationBinary:
+ return pdm.GetDecryptedProdInfo();
+ case BisPartitionId::BootConfigAndPackage2Part1:
+ case BisPartitionId::BootConfigAndPackage2Part2:
+ case BisPartitionId::BootConfigAndPackage2Part3:
+ case BisPartitionId::BootConfigAndPackage2Part4:
+ case BisPartitionId::BootConfigAndPackage2Part5:
+ case BisPartitionId::BootConfigAndPackage2Part6: {
+ const auto new_id = static_cast<u8>(id) -
+ static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
+ static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
+ return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
+ }
+ default:
+ return nullptr;
+ }
+}
+
+VirtualDir BISFactory::GetImageDirectory() const {
+ return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
+}
+
+u64 BISFactory::GetSystemNANDFreeSpace() const {
+ const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
+ if (sys_dir == nullptr)
+ return 0;
+
+ return GetSystemNANDTotalSpace() - sys_dir->GetSize();
+}
+
+u64 BISFactory::GetSystemNANDTotalSpace() const {
+ return static_cast<u64>(Settings::values.nand_system_size);
+}
+
+u64 BISFactory::GetUserNANDFreeSpace() const {
+ const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
+ if (usr_dir == nullptr)
+ return 0;
+
+ return GetUserNANDTotalSpace() - usr_dir->GetSize();
+}
+
+u64 BISFactory::GetUserNANDTotalSpace() const {
+ return static_cast<u64>(Settings::values.nand_user_size);
+}
+
+u64 BISFactory::GetFullNANDTotalSpace() const {
+ return static_cast<u64>(Settings::values.nand_total_size);
+}
+
} // 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 @@
namespace FileSys {
+enum class BisPartitionId : u32 {
+ UserDataRoot = 20,
+ CalibrationBinary = 27,
+ CalibrationFile = 28,
+ BootConfigAndPackage2Part1 = 21,
+ BootConfigAndPackage2Part2 = 22,
+ BootConfigAndPackage2Part3 = 23,
+ BootConfigAndPackage2Part4 = 24,
+ BootConfigAndPackage2Part5 = 25,
+ BootConfigAndPackage2Part6 = 26,
+ SafeMode = 29,
+ System = 31,
+ SystemProperEncryption = 32,
+ SystemProperPartition = 33,
+ User = 30,
+};
+
class RegisteredCache;
+class PlaceholderCache;
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -20,12 +38,29 @@ public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
~BISFactory();
+ VirtualDir GetSystemNANDContentDirectory() const;
+ VirtualDir GetUserNANDContentDirectory() const;
+
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
+ PlaceholderCache* GetSystemNANDPlaceholder() const;
+ PlaceholderCache* GetUserNANDPlaceholder() const;
+
VirtualDir GetModificationLoadRoot(u64 title_id) const;
VirtualDir GetModificationDumpRoot(u64 title_id) const;
+ VirtualDir OpenPartition(BisPartitionId id) const;
+ VirtualFile OpenPartitionStorage(BisPartitionId id) const;
+
+ VirtualDir GetImageDirectory() const;
+
+ u64 GetSystemNANDFreeSpace() const;
+ u64 GetSystemNANDTotalSpace() const;
+ u64 GetUserNANDFreeSpace() const;
+ u64 GetUserNANDTotalSpace() const;
+ u64 GetFullNANDTotalSpace() const;
+
private:
VirtualDir nand_root;
VirtualDir load_root;
@@ -33,6 +68,9 @@ private:
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;
+
+ std::unique_ptr<PlaceholderCache> sysnand_placeholder;
+ std::unique_ptr<PlaceholderCache> usrnand_placeholder;
};
} // 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 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
+#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs_vector.h"
#include "core/loader/loader.h"
namespace FileSys {
+constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
constexpr std::array partition_names{
"update",
"normal",
@@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
+VirtualDir XCI::ConcatenatedPseudoDirectory() {
+ const auto out = std::make_shared<VectorVfsDirectory>();
+ for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
+ const auto& part = GetPartition(part_id);
+ if (part == nullptr)
+ continue;
+
+ for (const auto& file : part->GetFiles())
+ out->AddFile(file);
+ }
+
+ return out;
+}
+
+std::array<u8, 0x200> XCI::GetCertificate() const {
+ std::array<u8, 0x200> out;
+ file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
+ return out;
+}
+
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
const auto partition_index = static_cast<std::size_t>(part);
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:
VirtualDir GetLogoPartition() const;
u64 GetProgramTitleID() const;
+ u32 GetSystemUpdateVersion();
+ u64 GetSystemUpdateTitleID() const;
bool HasProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
@@ -106,6 +108,11 @@ public:
VirtualDir GetParentDirectory() const override;
+ // Creates a directory that contains all the NCAs in the gamecard
+ VirtualDir ConcatenatedPseudoDirectory();
+
+ std::array<u8, 0x200> GetCertificate() const;
+
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
@@ -120,6 +127,8 @@ private:
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
+ u64 update_normal_partition_end;
+
Core::Crypto::KeyManager keys;
};
} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <locale>
-#include "common/hex_util.h"
-#include "common/microprofile.h"
-#include "common/swap.h"
-#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/core_timing_util.h"
-#include "core/file_sys/cheat_engine.h"
-#include "core/hle/kernel/process.h"
-#include "core/hle/service/hid/controllers/npad.h"
-#include "core/hle/service/hid/hid.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace FileSys {
-
-constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
-constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
-
-u64 Cheat::Address() const {
- u64 out;
- std::memcpy(&out, raw.data(), sizeof(u64));
- return Common::swap64(out) & 0xFFFFFFFFFF;
-}
-
-u64 Cheat::ValueWidth(u64 offset) const {
- return Value(offset, width);
-}
-
-u64 Cheat::Value(u64 offset, u64 width) const {
- u64 out;
- std::memcpy(&out, raw.data() + offset, sizeof(u64));
- out = Common::swap64(out);
- if (width == 8)
- return out;
- return out & ((1ull << (width * CHAR_BIT)) - 1);
-}
-
-u32 Cheat::KeypadValue() const {
- u32 out;
- std::memcpy(&out, raw.data(), sizeof(u32));
- return Common::swap32(out) & 0x0FFFFFFF;
-}
-
-void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
- VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
- this->main_region_begin = main_begin;
- this->main_region_end = main_end;
- this->heap_region_begin = heap_begin;
- this->heap_region_end = heap_end;
- this->writer = writer;
- this->reader = reader;
-}
-
-MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
-
-void CheatList::Execute() {
- MICROPROFILE_SCOPE(Cheat_Engine);
-
- std::fill(scratch.begin(), scratch.end(), 0);
- in_standard = false;
- for (std::size_t i = 0; i < master_list.size(); ++i) {
- LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
- current_block = i;
- ExecuteBlock(master_list[i].second);
- }
-
- in_standard = true;
- for (std::size_t i = 0; i < standard_list.size(); ++i) {
- LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
- current_block = i;
- ExecuteBlock(standard_list[i].second);
- }
-}
-
-CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
- : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
-
-bool CheatList::EvaluateConditional(const Cheat& cheat) const {
- using ComparisonFunction = bool (*)(u64, u64);
- constexpr std::array<ComparisonFunction, 6> comparison_functions{
- [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
- [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
- [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
- };
-
- if (cheat.type == CodeType::ConditionalInput) {
- const auto applet_resource =
- system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
- if (applet_resource == nullptr) {
- LOG_WARNING(
- Common_Filesystem,
- "Attempted to evaluate input conditional, but applet resource is not initialized!");
- return false;
- }
-
- const auto press_state =
- applet_resource
- ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
- .GetAndResetPressState();
- return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
- }
-
- ASSERT(cheat.type == CodeType::Conditional);
-
- const auto offset =
- cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
- ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
- auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
- const auto addr = cheat.Address() + offset;
-
- return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
-}
-
-void CheatList::ProcessBlockPairs(const Block& block) {
- block_pairs.clear();
-
- u64 scope = 0;
- std::map<u64, u64> pairs;
-
- for (std::size_t i = 0; i < block.size(); ++i) {
- const auto& cheat = block[i];
-
- switch (cheat.type) {
- case CodeType::Conditional:
- case CodeType::ConditionalInput:
- pairs.insert_or_assign(scope, i);
- ++scope;
- break;
- case CodeType::EndConditional: {
- --scope;
- const auto idx = pairs.at(scope);
- block_pairs.insert_or_assign(idx, i);
- break;
- }
- case CodeType::Loop: {
- if (cheat.end_of_loop) {
- --scope;
- const auto idx = pairs.at(scope);
- block_pairs.insert_or_assign(idx, i);
- } else {
- pairs.insert_or_assign(scope, i);
- ++scope;
- }
- break;
- }
- }
- }
-}
-
-void CheatList::WriteImmediate(const Cheat& cheat) {
- const auto offset =
- cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
- const auto& register_3 = scratch.at(cheat.register_3);
-
- const auto addr = cheat.Address() + offset + register_3;
- LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
- cheat.Value(8, cheat.width));
- writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
-}
-
-void CheatList::BeginConditional(const Cheat& cheat) {
- if (EvaluateConditional(cheat)) {
- return;
- }
-
- const auto iter = block_pairs.find(current_index);
- ASSERT(iter != block_pairs.end());
- current_index = iter->second - 1;
-}
-
-void CheatList::EndConditional(const Cheat& cheat) {
- LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
-}
-
-void CheatList::Loop(const Cheat& cheat) {
- if (cheat.end_of_loop.Value())
- ASSERT(!cheat.end_of_loop.Value());
-
- auto& register_3 = scratch.at(cheat.register_3);
- const auto iter = block_pairs.find(current_index);
- ASSERT(iter != block_pairs.end());
- ASSERT(iter->first < iter->second);
-
- const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
- for (s32 i = initial_value; i >= 0; --i) {
- register_3 = static_cast<u64>(i);
- for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
- current_index = c;
- ExecuteSingleCheat(
- (in_standard ? standard_list : master_list)[current_block].second[c]);
- }
- }
-
- current_index = iter->second;
-}
-
-void CheatList::LoadImmediate(const Cheat& cheat) {
- auto& register_3 = scratch.at(cheat.register_3);
-
- LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
- cheat.Value(4, 8));
- register_3 = cheat.Value(4, 8);
-}
-
-void CheatList::LoadIndexed(const Cheat& cheat) {
- const auto offset =
- cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
- auto& register_3 = scratch.at(cheat.register_3);
-
- const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
- LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
- cheat.register_3, addr);
- register_3 = reader(cheat.width, SanitizeAddress(addr));
-}
-
-void CheatList::StoreIndexed(const Cheat& cheat) {
- const auto& register_3 = scratch.at(cheat.register_3);
-
- const auto addr =
- register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
- LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
- cheat.Value(4, cheat.width), addr);
- writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
-}
-
-void CheatList::RegisterArithmetic(const Cheat& cheat) {
- using ArithmeticFunction = u64 (*)(u64, u64);
- constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
- [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
- [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
- [](u64 a, u64 b) { return a >> b; },
- };
-
- using ArithmeticOverflowCheck = bool (*)(u64, u64);
- constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
- [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
- [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
- [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
- [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
- [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
- };
-
- static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
- "Missing or have extra arithmetic overflow checks compared to functions!");
-
- auto& register_3 = scratch.at(cheat.register_3);
-
- ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
- auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
- auto* overflow_function =
- arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
- LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
- cheat.register_3, cheat.ValueWidth(4));
-
- if (overflow_function(register_3, cheat.ValueWidth(4))) {
- LOG_WARNING(Common_Filesystem,
- "overflow will occur when performing arithmetic operation={:02X} with operands "
- "a={:016X}, b={:016X}!",
- static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
- }
-
- register_3 = function(register_3, cheat.ValueWidth(4));
-}
-
-void CheatList::BeginConditionalInput(const Cheat& cheat) {
- if (EvaluateConditional(cheat))
- return;
-
- const auto iter = block_pairs.find(current_index);
- ASSERT(iter != block_pairs.end());
- current_index = iter->second - 1;
-}
-
-VAddr CheatList::SanitizeAddress(VAddr in) const {
- if ((in < main_region_begin || in >= main_region_end) &&
- (in < heap_region_begin || in >= heap_region_end)) {
- LOG_ERROR(Common_Filesystem,
- "Cheat attempting to access memory at invalid address={:016X}, if this persists, "
- "the cheat may be incorrect. However, this may be normal early in execution if "
- "the game has not properly set up yet.",
- in);
- return 0; ///< Invalid addresses will hard crash
- }
-
- return in;
-}
-
-void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
- using CheatOperationFunction = void (CheatList::*)(const Cheat&);
- constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
- &CheatList::WriteImmediate, &CheatList::BeginConditional,
- &CheatList::EndConditional, &CheatList::Loop,
- &CheatList::LoadImmediate, &CheatList::LoadIndexed,
- &CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
- &CheatList::BeginConditionalInput,
- };
-
- const auto index = static_cast<u8>(cheat.type.Value());
- ASSERT(index < sizeof(cheat_operation_functions));
- const auto op = cheat_operation_functions[index];
- (this->*op)(cheat);
-}
-
-void CheatList::ExecuteBlock(const Block& block) {
- encountered_loops.clear();
-
- ProcessBlockPairs(block);
- for (std::size_t i = 0; i < block.size(); ++i) {
- current_index = i;
- ExecuteSingleCheat(block[i]);
- i = current_index;
- }
-}
-
-CheatParser::~CheatParser() = default;
-
-CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
- CheatList::ProgramSegment standard) const {
- return {system, std::move(master), std::move(standard)};
-}
-
-TextCheatParser::~TextCheatParser() = default;
-
-CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
- std::stringstream ss;
- ss.write(reinterpret_cast<const char*>(data.data()), data.size());
-
- std::vector<std::string> lines;
- std::string stream_line;
- while (std::getline(ss, stream_line)) {
- // Remove a trailing \r
- if (!stream_line.empty() && stream_line.back() == '\r')
- stream_line.pop_back();
- lines.push_back(std::move(stream_line));
- }
-
- CheatList::ProgramSegment master_list;
- CheatList::ProgramSegment standard_list;
-
- for (std::size_t i = 0; i < lines.size(); ++i) {
- auto line = lines[i];
-
- if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
- const auto master = line[0] == '{';
- const auto begin = master ? line.find('{') : line.find('[');
- const auto end = master ? line.rfind('}') : line.rfind(']');
-
- ASSERT(begin != std::string::npos && end != std::string::npos);
-
- const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
- CheatList::Block block{};
-
- while (i < lines.size() - 1) {
- line = lines[++i];
- if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
- --i;
- break;
- }
-
- if (line.size() < 8)
- continue;
-
- Cheat out{};
- out.raw = ParseSingleLineCheat(line);
- block.push_back(out);
- }
-
- (master ? master_list : standard_list).emplace_back(patch_name, block);
- }
- }
-
- return MakeCheatList(system, master_list, standard_list);
-}
-
-std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
- std::array<u8, 16> out{};
-
- if (line.size() < 8)
- return out;
-
- const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
- std::memcpy(out.data(), word1.data(), sizeof(u32));
-
- if (line.size() < 17 || line[8] != ' ')
- return out;
-
- const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
- std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
-
- if (line.size() < 26 || line[17] != ' ') {
- // Perform shifting in case value is truncated early.
- const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
- if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
- type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
- std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
- std::memset(out.data() + 4, 0, sizeof(u32));
- }
-
- return out;
- }
-
- const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
- std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
-
- if (line.size() < 35 || line[26] != ' ') {
- // Perform shifting in case value is truncated early.
- const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
- if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
- std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
- std::memset(out.data() + 8, 0, sizeof(u32));
- }
-
- return out;
- }
-
- const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
- std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
-
- return out;
-}
-
-namespace {
-u64 MemoryReadImpl(u32 width, VAddr addr) {
- switch (width) {
- case 1:
- return Memory::Read8(addr);
- case 2:
- return Memory::Read16(addr);
- case 4:
- return Memory::Read32(addr);
- case 8:
- return Memory::Read64(addr);
- default:
- UNREACHABLE();
- return 0;
- }
-}
-
-void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
- switch (width) {
- case 1:
- Memory::Write8(addr, static_cast<u8>(value));
- break;
- case 2:
- Memory::Write16(addr, static_cast<u16>(value));
- break;
- case 4:
- Memory::Write32(addr, static_cast<u32>(value));
- break;
- case 8:
- Memory::Write64(addr, value);
- break;
- default:
- UNREACHABLE();
- }
-}
-} // Anonymous namespace
-
-CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
- const std::string& build_id, VAddr code_region_start,
- VAddr code_region_end)
- : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
- event = core_timing.RegisterEvent(
- "CheatEngine::FrameCallback::" + build_id,
- [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
- core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
-
- const auto& vm_manager = system.CurrentProcess()->VMManager();
- for (auto& list : this->cheats) {
- list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
- code_region_end, vm_manager.GetHeapRegionEndAddress(),
- &MemoryWriteImpl, &MemoryReadImpl);
- }
-}
-
-CheatEngine::~CheatEngine() {
- core_timing.UnscheduleEvent(event, 0);
-}
-
-void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
- for (auto& list : cheats) {
- list.Execute();
- }
-
- core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
-}
-
-} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <set>
-#include <vector>
-#include "common/bit_field.h"
-#include "common/common_types.h"
-
-namespace Core {
-class System;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-struct EventType;
-} // namespace Core::Timing
-
-namespace FileSys {
-
-enum class CodeType : u32 {
- // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
- // Writes a T sized value Y to the address A added to the value of register R in memory domain M
- WriteImmediate = 0,
-
- // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
- // Compares the T sized value Y to the value at address A in memory domain M using the
- // conditional function C. If success, continues execution. If failure, jumps to the matching
- // EndConditional statement.
- Conditional = 1,
-
- // 20000000
- // Terminates a Conditional or ConditionalInput block.
- EndConditional = 2,
-
- // 300R0000 VVVVVVVV
- // Starts looping V times, storing the current count in register R.
- // Loop block is terminated with a matching 310R0000.
- Loop = 3,
-
- // 400R0000 VVVVVVVV VVVVVVVV
- // Sets the value of register R to the value V.
- LoadImmediate = 4,
-
- // 5TMRI0AA AAAAAAAA
- // Sets the value of register R to the value of width T at address A in memory domain M, with
- // the current value of R added to the address if I == 1.
- LoadIndexed = 5,
-
- // 6T0RIFG0 VVVVVVVV VVVVVVVV
- // Writes the value V of width T to the memory address stored in register R. Adds the value of
- // register G to the final calculation if F is nonzero. Increments the value of register R by T
- // after operation if I is nonzero.
- StoreIndexed = 6,
-
- // 7T0RA000 VVVVVVVV
- // Performs the arithmetic operation A on the value in register R and the value V of width T,
- // storing the result in register R.
- RegisterArithmetic = 7,
-
- // 8KKKKKKK
- // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
- // execution continues. If none are, execution skips to the next EndConditional command.
- ConditionalInput = 8,
-};
-
-enum class MemoryType : u32 {
- // Addressed relative to start of main NSO
- MainNSO = 0,
-
- // Addressed relative to start of heap
- Heap = 1,
-};
-
-enum class ArithmeticOp : u32 {
- Add = 0,
- Sub = 1,
- Mult = 2,
- LShift = 3,
- RShift = 4,
-};
-
-enum class ComparisonOp : u32 {
- GreaterThan = 1,
- GreaterThanEqual = 2,
- LessThan = 3,
- LessThanEqual = 4,
- Equal = 5,
- Inequal = 6,
-};
-
-union Cheat {
- std::array<u8, 16> raw;
-
- BitField<4, 4, CodeType> type;
- BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
- BitField<0, 4, u32> end_of_loop;
- BitField<12, 4, MemoryType> memory_type;
- BitField<8, 4, u32> register_3;
- BitField<8, 4, ComparisonOp> comparison_op;
- BitField<20, 4, u32> load_from_register;
- BitField<20, 4, u32> increment_register;
- BitField<20, 4, ArithmeticOp> arithmetic_op;
- BitField<16, 4, u32> add_additional_register;
- BitField<28, 4, u32> register_6;
-
- u64 Address() const;
- u64 ValueWidth(u64 offset) const;
- u64 Value(u64 offset, u64 width) const;
- u32 KeypadValue() const;
-};
-
-class CheatParser;
-
-// Represents a full collection of cheats for a game. The Execute function should be called every
-// interval that all cheats should be executed. Clients should not directly instantiate this class
-// (hence private constructor), they should instead receive an instance from CheatParser, which
-// guarantees the list is always in an acceptable state.
-class CheatList {
-public:
- friend class CheatParser;
-
- using Block = std::vector<Cheat>;
- using ProgramSegment = std::vector<std::pair<std::string, Block>>;
-
- // (width in bytes, address, value)
- using MemoryWriter = void (*)(u32, VAddr, u64);
- // (width in bytes, address) -> value
- using MemoryReader = u64 (*)(u32, VAddr);
-
- void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
- MemoryWriter writer, MemoryReader reader);
-
- void Execute();
-
-private:
- CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
-
- void ProcessBlockPairs(const Block& block);
- void ExecuteSingleCheat(const Cheat& cheat);
-
- void ExecuteBlock(const Block& block);
-
- bool EvaluateConditional(const Cheat& cheat) const;
-
- // Individual cheat operations
- void WriteImmediate(const Cheat& cheat);
- void BeginConditional(const Cheat& cheat);
- void EndConditional(const Cheat& cheat);
- void Loop(const Cheat& cheat);
- void LoadImmediate(const Cheat& cheat);
- void LoadIndexed(const Cheat& cheat);
- void StoreIndexed(const Cheat& cheat);
- void RegisterArithmetic(const Cheat& cheat);
- void BeginConditionalInput(const Cheat& cheat);
-
- VAddr SanitizeAddress(VAddr in) const;
-
- // Master Codes are defined as codes that cannot be disabled and are run prior to all
- // others.
- ProgramSegment master_list;
- // All other codes
- ProgramSegment standard_list;
-
- bool in_standard = false;
-
- // 16 (0x0-0xF) scratch registers that can be used by cheats
- std::array<u64, 16> scratch{};
-
- MemoryWriter writer = nullptr;
- MemoryReader reader = nullptr;
-
- u64 main_region_begin{};
- u64 heap_region_begin{};
- u64 main_region_end{};
- u64 heap_region_end{};
-
- u64 current_block{};
- // The current index of the cheat within the current Block
- u64 current_index{};
-
- // The 'stack' of the program. When a conditional or loop statement is encountered, its index is
- // pushed onto this queue. When a end block is encountered, the condition is checked.
- std::map<u64, u64> block_pairs;
-
- std::set<u64> encountered_loops;
-
- const Core::System* system;
-};
-
-// Intermediary class that parses a text file or other disk format for storing cheats into a
-// CheatList object, that can be used for execution.
-class CheatParser {
-public:
- virtual ~CheatParser();
-
- virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
-
-protected:
- CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
- CheatList::ProgramSegment standard) const;
-};
-
-// CheatParser implementation that parses text files
-class TextCheatParser final : public CheatParser {
-public:
- ~TextCheatParser() override;
-
- CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
-
-private:
- std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
-};
-
-// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
-class CheatEngine final {
-public:
- CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
- VAddr code_region_start, VAddr code_region_end);
- ~CheatEngine();
-
-private:
- void FrameCallback(u64 userdata, s64 cycles_late);
-
- std::vector<CheatList> cheats;
-
- Core::Timing::EventType* event;
- Core::Timing::CoreTiming& core_timing;
-};
-
-} // 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 {
return header.title_id;
}
+std::array<u8, 16> NCA::GetRightsId() const {
+ return header.rights_id;
+}
+
+u32 NCA::GetSDKVersion() const {
+ return header.sdk_version;
+}
+
bool NCA::IsUpdate() const {
return is_update;
}
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:
NCAContentType GetType() const;
u64 GetTitleId() const;
+ std::array<u8, 0x10> GetRightsId() const;
+ u32 GetSDKVersion() const;
bool IsUpdate() const;
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 @@
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
+#include "core/memory/cheat_engine.h"
#include "core/settings.h"
namespace FileSys {
@@ -63,7 +64,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
- const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
+ const auto dump_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
@@ -88,7 +90,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
// LayeredExeFS
- const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ const auto load_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
@@ -174,7 +177,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
- const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
+ const auto dump_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
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
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
- const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ const auto load_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ if (load_dir == nullptr) {
+ LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+ return nso;
+ }
+
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](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 {
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
- const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ const auto load_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ if (load_dir == nullptr) {
+ LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+ return false;
+ }
+
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](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 {
return !CollectPatches(patch_dirs, build_id).empty();
}
-static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
- const std::array<u8, 0x20>& build_id_,
- const VirtualDir& base_path, bool upper) {
+namespace {
+std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
+ const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
+ const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -252,31 +269,39 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
return std::nullopt;
}
- TextCheatParser parser;
- return parser.Parse(system, data);
+ Memory::TextCheatParser parser;
+ return parser.Parse(
+ system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
}
-std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
- const std::array<u8, 32>& build_id_) const {
- const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+} // Anonymous namespace
+
+std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
+ const Core::System& system, const std::array<u8, 32>& build_id_) const {
+ const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
+ if (load_dir == nullptr) {
+ LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+ return {};
+ }
+
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
- std::vector<CheatList> out;
- out.reserve(patch_dirs.size());
+ std::vector<Memory::CheatEntry> out;
for (const auto& subdir : patch_dirs) {
auto cheats_dir = subdir->GetSubdirectory("cheats");
if (cheats_dir != nullptr) {
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
if (res.has_value()) {
- out.push_back(std::move(*res));
+ std::copy(res->begin(), res->end(), std::back_inserter(out));
continue;
}
res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
- if (res.has_value())
- out.push_back(std::move(*res));
+ if (res.has_value()) {
+ std::copy(res->begin(), res->end(), std::back_inserter(out));
+ }
}
}
@@ -284,7 +309,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
- const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ const auto load_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
@@ -393,6 +419,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
+ if (title_id == 0)
+ return {};
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -423,7 +451,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
// General Mods (LayeredFS and IPS)
- const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ const auto mod_dir =
+ Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
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 @@
#include <memory>
#include <string>
#include "common/common_types.h"
-#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
+#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
@@ -51,8 +51,8 @@ public:
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Creates a CheatList object with all
- std::vector<CheatList> CreateCheatList(const Core::System& system,
- const std::array<u8, 0x20>& build_id) const;
+ std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system,
+ const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - 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 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <random>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
static bool FollowsNcaIdFormat(std::string_view name) {
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
std::regex_constants::icase);
- return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
+ static const std::regex nca_id_cnmt_regex(
+ "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
+ return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
+ (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
}
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
- bool within_two_digit) {
- if (!within_two_digit) {
- return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
- }
+ bool within_two_digit, bool cnmt_suffix) {
+ if (!within_two_digit)
+ return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
+ Common::HexToString(nca_id, second_hex_upper));
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
- return fmt::format("/000000{:02X}/{}.nca", hash[0],
+ return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
Common::HexToString(nca_id, second_hex_upper));
}
@@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
+PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
+
+bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ if (dir->GetFileRelative(path) != nullptr) {
+ return false;
+ }
+
+ Core::Crypto::SHA256Hash hash{};
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+ const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+ const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+ if (dir2 == nullptr)
+ return false;
+
+ const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+
+ if (file == nullptr)
+ return false;
+
+ return file->Resize(size);
+}
+
+bool PlaceholderCache::Delete(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ if (dir->GetFileRelative(path) == nullptr) {
+ return false;
+ }
+
+ Core::Crypto::SHA256Hash hash{};
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+ const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+ const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+ const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
+
+ return res;
+}
+
+bool PlaceholderCache::Exists(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ return dir->GetFileRelative(path) != nullptr;
+}
+
+bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ return file->WriteBytes(data, offset) == data.size();
+}
+
+bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
+ const NcaID& install) const {
+ const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
+
+ if (res != InstallResult::Success)
+ return false;
+
+ return Delete(placeholder);
+}
+
+bool PlaceholderCache::CleanAll() const {
+ return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
+}
+
+std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return std::nullopt;
+
+ NCA nca{file};
+
+ if (nca.GetStatus() != Loader::ResultStatus::Success &&
+ nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
+ return std::nullopt;
+ }
+
+ const auto rights_id = nca.GetRightsId();
+ if (rights_id == NcaID{})
+ return std::nullopt;
+
+ return rights_id;
+}
+
+u64 PlaceholderCache::Size(const NcaID& id) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return 0;
+
+ return file->GetSize();
+}
+
+bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+ const auto file = dir->GetFileRelative(path);
+
+ if (file == nullptr)
+ return false;
+
+ return file->Resize(new_size);
+}
+
+std::vector<NcaID> PlaceholderCache::List() const {
+ std::vector<NcaID> out;
+ for (const auto& sdir : dir->GetSubdirectories()) {
+ for (const auto& file : sdir->GetFiles()) {
+ const auto name = file->GetName();
+ if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
+ name[35] == 'a') {
+ out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
+ }
+ }
+ }
+ return out;
+}
+
+NcaID PlaceholderCache::Generate() {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+
+ NcaID out{};
+
+ const auto v1 = distribution(gen);
+ const auto v2 = distribution(gen);
+ std::memcpy(out.data(), &v1, sizeof(u64));
+ std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
+
+ return out;
+}
+
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
VirtualFile file;
- // Try all four modes of file storage:
- // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
- // 00: /000000**/{:032X}.nca
- // 01: /{:032X}.nca
- // 10: /000000**/{:032x}.nca
- // 11: /{:032x}.nca
- for (u8 i = 0; i < 4; ++i) {
- const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
+ // Try all five relevant modes of file storage:
+ // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
+ // 000: /000000**/{:032X}.nca
+ // 010: /{:032X}.nca
+ // 100: /000000**/{:032x}.nca
+ // 110: /{:032x}.nca
+ // 111: /{:032x}.cnmt.nca
+ for (u8 i = 0; i < 8; ++i) {
+ if ((i % 2) == 1 && i != 7)
+ continue;
+ const auto path =
+ GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
file = OpenFileOrDirectoryConcat(dir, path);
if (file != nullptr)
return file;
@@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
memcpy(id.data(), hash.data(), 16);
}
- std::string path = GetRelativePathFromNcaID(id, false, true);
+ std::string path = GetRelativePathFromNcaID(id, false, true, false);
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
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;
enum class TitleType : u8;
struct ContentRecord;
+struct MetaRecord;
+class RegisteredCache;
using NcaID = std::array<u8, 0x10>;
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -89,6 +91,27 @@ protected:
Core::Crypto::KeyManager keys;
};
+class PlaceholderCache {
+public:
+ explicit PlaceholderCache(VirtualDir dir);
+
+ bool Create(const NcaID& id, u64 size) const;
+ bool Delete(const NcaID& id) const;
+ bool Exists(const NcaID& id) const;
+ bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
+ bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
+ bool CleanAll() const;
+ std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
+ u64 Size(const NcaID& id) const;
+ bool SetSize(const NcaID& id, u64 new_size) const;
+ std::vector<NcaID> List() const;
+
+ static NcaID Generate();
+
+private:
+ VirtualDir dir;
+};
+
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
@@ -103,6 +126,8 @@ protected:
* when 4GB splitting can be ignored.)
*/
class RegisteredCache : public ContentProvider {
+ friend class PlaceholderCache;
+
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// 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 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
-ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
+ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
if (!updatable)
return MakeResult<VirtualFile>(file);
@@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}
-ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
+ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
+ ContentRecordType type) const {
std::shared_ptr<NCA> res;
switch (storage) {
@@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
- res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
+ res =
+ Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
+ title_id, type);
break;
case StorageId::NandUser:
- res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
+ res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
+ title_id, type);
break;
case StorageId::SdCard:
- res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
+ res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
+ title_id, type);
break;
default:
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:
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
- ResultVal<VirtualFile> OpenCurrentProcess();
- ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
+ ResultVal<VirtualFile> OpenCurrentProcess() const;
+ ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
private:
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 {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
-std::string SaveDataDescriptor::DebugInfo() const {
- return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
- "rank={}, index={}]",
- static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
- static_cast<u8>(rank), index);
-}
-
-SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
- // Delete all temporary storages
- // On hardware, it is expected that temporary storage be empty at first use.
- dir->DeleteSubdirectoryRecursive("temp");
-}
-
-SaveDataFactory::~SaveDataFactory() = default;
-
-ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
+namespace {
+void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
@@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
+}
+} // Anonymous namespace
- std::string save_directory =
- GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
+std::string SaveDataDescriptor::DebugInfo() const {
+ return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
+ "save_id={:016X}, "
+ "rank={}, index={}]",
+ static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
+ static_cast<u8>(rank), index);
+}
- // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
- // But, user_ids don't match so this works for now.
+SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
+ // Delete all temporary storages
+ // On hardware, it is expected that temporary storage be empty at first use.
+ dir->DeleteSubdirectoryRecursive("temp");
+}
- auto out = dir->GetDirectoryRelative(save_directory);
+SaveDataFactory::~SaveDataFactory() = default;
+
+ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
+ const SaveDataDescriptor& meta) const {
+ PrintSaveDataDescriptorWarnings(meta);
+
+ const auto save_directory =
+ GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
+
+ auto out = dir->CreateDirectoryRelative(save_directory);
+ // Return an error if the save data doesn't actually exist.
if (out == nullptr) {
- // TODO(bunnei): This is a work-around to always create a save data directory if it does not
- // already exist. This is a hack, as we do not understand yet how this works on hardware.
- // Without a save data directory, many games will assert on boot. This should not have any
- // bad side-effects.
- out = dir->CreateDirectoryRelative(save_directory);
+ // TODO(DarkLordZach): Find out correct error code.
+ return ResultCode(-1);
}
+ return MakeResult<VirtualDir>(std::move(out));
+}
+
+ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
+ const SaveDataDescriptor& meta) const {
+
+ const auto save_directory =
+ GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
+
+ auto out = dir->GetDirectoryRelative(save_directory);
+
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
@@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
}
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
- SaveDataSize new_value) {
+ SaveDataSize new_value) const {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
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:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
- ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
+ ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
+ ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
@@ -73,7 +74,8 @@ public:
u128 user_id, u64 save_id);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
- void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
+ void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
+ SaveDataSize new_value) const;
private:
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 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/xts_archive.h"
+#include "core/settings.h"
namespace FileSys {
@@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return NAX{file, id}.GetDecrypted();
- })) {}
+ })),
+ placeholder(std::make_unique<PlaceholderCache>(
+ GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
SDMCFactory::~SDMCFactory() = default;
-ResultVal<VirtualDir> SDMCFactory::Open() {
+ResultVal<VirtualDir> SDMCFactory::Open() const {
return MakeResult<VirtualDir>(dir);
}
+VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
+ return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
+}
+
RegisteredCache* SDMCFactory::GetSDMCContents() const {
return contents.get();
}
+PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
+ return placeholder.get();
+}
+
+VirtualDir SDMCFactory::GetImageDirectory() const {
+ return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
+}
+
+u64 SDMCFactory::GetSDMCFreeSpace() const {
+ return GetSDMCTotalSpace() - dir->GetSize();
+}
+
+u64 SDMCFactory::GetSDMCTotalSpace() const {
+ return static_cast<u64>(Settings::values.sdmc_size);
+}
+
} // 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 @@
namespace FileSys {
class RegisteredCache;
+class PlaceholderCache;
/// File system interface to the SDCard archive
class SDMCFactory {
@@ -18,13 +19,23 @@ public:
explicit SDMCFactory(VirtualDir dir);
~SDMCFactory();
- ResultVal<VirtualDir> Open();
+ ResultVal<VirtualDir> Open() const;
+
+ VirtualDir GetSDMCContentDirectory() const;
+
RegisteredCache* GetSDMCContents() const;
+ PlaceholderCache* GetSDMCPlaceholder() const;
+
+ VirtualDir GetImageDirectory() const;
+
+ u64 GetSDMCFreeSpace() const;
+ u64 GetSDMCTotalSpace() const;
private:
VirtualDir dir;
std::unique_ptr<RegisteredCache> contents;
+ std::unique_ptr<PlaceholderCache> placeholder;
};
} // 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 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/program_metadata.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
+ if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
+ return Loader::ResultStatus::Success;
+ }
+
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
}
u64 NSP::GetFirstTitleID() const {
+ if (IsExtractedType()) {
+ return GetProgramTitleID();
+ }
+
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
+ if (IsExtractedType()) {
+ if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
+ return 0;
+ }
+
+ ProgramMetadata meta;
+ if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
+ return meta.GetTitleID();
+ } else {
+ return 0;
+ }
+ }
+
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
}
std::vector<u64> NSP::GetTitleIDs() const {
+ if (IsExtractedType()) {
+ return {GetProgramTitleID()};
+ }
+
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
@@ -222,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
for (const auto& outer_file : files) {
- if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
+ if (outer_file->GetName().size() < 9 ||
+ outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
continue;
}
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:
protected:
void Get(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -89,7 +89,7 @@ protected:
}
void GetBase(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
@@ -263,7 +263,7 @@ private:
};
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called");
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
@@ -272,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
- LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
+ LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -280,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
}
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called");
+ LOG_DEBUG(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetAllUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called");
+ LOG_DEBUG(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetOpenUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_ACC, "called");
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
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"} {
IDebugFunctions::~IDebugFunctions() = default;
-ISelfController::ISelfController(Core::System& system_,
- std::shared_ptr<NVFlinger::NVFlinger> nvflinger_)
- : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) {
+ISelfController::ISelfController(Core::System& system,
+ std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
+ : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "Exit"},
+ {0, &ISelfController::Exit, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
@@ -282,7 +282,7 @@ ISelfController::ISelfController(Core::System& system_,
RegisterHandlers(functions);
- auto& kernel = system_.Kernel();
+ auto& kernel = system.Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
@@ -298,15 +298,28 @@ ISelfController::ISelfController(Core::System& system_,
ISelfController::~ISelfController() = default;
+void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ system.Shutdown();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
+
+ system.SetExitLock(true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called");
+
+ system.SetExitLock(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -550,6 +563,10 @@ void AppletMessageQueue::OperationModeChanged() {
on_operation_mode_changed.writable->Signal();
}
+void AppletMessageQueue::RequestExit() {
+ PushMessage(AppletMessage::ExitRequested);
+}
+
ICommonStateGetter::ICommonStateGetter(Core::System& system,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) {
@@ -1066,7 +1083,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
}
@@ -1143,13 +1160,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u128 uid = rp.PopRaw<u128>(); // What does this do?
- LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
+ u128 user_id = rp.PopRaw<u128>();
+
+ LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
+
+ FileSys::SaveDataDescriptor descriptor{};
+ descriptor.title_id = Core::CurrentProcess()->GetTitleID();
+ descriptor.user_id = user_id;
+ descriptor.type = FileSys::SaveDataType::SaveData;
+ const auto res = system.GetFileSystemController().CreateSaveData(
+ FileSys::SaveDataSpaceId::NandUser, descriptor);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(RESULT_SUCCESS);
+ rb.Push(res.Code());
rb.Push<u64>(0);
-} // namespace Service::AM
+}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
@@ -1261,8 +1286,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
"new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
- const auto title_id = system.CurrentProcess()->GetTitleID();
- FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size});
+ system.GetFileSystemController().WriteSaveDataSize(
+ type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -1281,8 +1306,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
user_id[1], user_id[0]);
- const auto title_id = system.CurrentProcess()->GetTitleID();
- const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id);
+ const auto size = system.GetFileSystemController().ReadSaveDataSize(
+ type, system.CurrentProcess()->GetTitleID(), user_id);
IPC::ResponseBuilder rb{ctx, 6};
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 {
public:
enum class AppletMessage : u32 {
NoMessage = 0,
+ ExitRequested = 4,
FocusStateChanged = 15,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
@@ -59,6 +60,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void OperationModeChanged();
+ void RequestExit();
private:
std::queue<AppletMessage> messages;
@@ -123,6 +125,7 @@ public:
~ISelfController() override;
private:
+ void Exit(Kernel::HLERequestContext& ctx);
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
void EnterFatalSection(Kernel::HLERequestContext& ctx);
@@ -151,6 +154,8 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
+
+ Core::System& system;
};
class 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 @@
#include "core/hle/service/service.h"
namespace Service {
+namespace FileSystem {
+class FileSystemController;
+}
+
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
+class AppletMessageQueue;
+
class AppletAE final : public ServiceFramework<AppletAE> {
public:
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 @@
#include "core/hle/service/service.h"
namespace Service {
+namespace FileSystem {
+class FileSystemController;
+}
+
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
+class AppletMessageQueue;
+
class AppletOE final : public ServiceFramework<AppletOE> {
public:
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) {
return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
}
-static std::vector<u64> AccumulateAOCTitleIDs() {
+static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
std::vector<u64> add_on_content;
- const auto& rcu = Core::System::GetInstance().GetContentProvider();
+ const auto& rcu = system.GetContentProvider();
const auto list =
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
@@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
return add_on_content;
}
-AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {
+AOC_U::AOC_U(Core::System& system)
+ : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CountAddOnContentByApplicationId"},
@@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"GetAddOnContentListChanged:Event");
}
@@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ const auto current = system.CurrentProcess()->GetTitleID();
const auto& disabled = Settings::values.disabled_addons[current];
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
@@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
process_id);
- const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ const auto current = system.CurrentProcess()->GetTitleID();
std::vector<u32> out;
const auto& disabled = Settings::values.disabled_addons[current];
@@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
const auto res = pm.GetControlMetadata();
@@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(aoc_change_event.readable);
}
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<AOC_U>()->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<AOC_U>(system)->InstallAsService(service_manager);
}
} // 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 {
class AOC_U final : public ServiceFramework<AOC_U> {
public:
- AOC_U();
+ explicit AOC_U(Core::System& system);
~AOC_U() override;
private:
@@ -26,9 +26,10 @@ private:
std::vector<u64> add_on_content;
Kernel::EventPair aoc_change_event;
+ Core::System& system;
};
/// Registers all AOC services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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 {
class Bt final : public ServiceFramework<Bt> {
public:
- explicit Bt() : ServiceFramework{"bt"} {
+ explicit Bt(Core::System& system) : ServiceFramework{"bt"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LeClientReadCharacteristic"},
@@ -33,7 +33,7 @@ public:
// clang-format on
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
register_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
}
@@ -163,9 +163,9 @@ public:
}
};
-void InstallInterfaces(SM::ServiceManager& sm) {
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<BtDrv>()->InstallAsService(sm);
- std::make_shared<Bt>()->InstallAsService(sm);
+ std::make_shared<Bt>(system)->InstallAsService(sm);
}
} // 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 {
class ServiceManager;
}
+namespace Core {
+class System;
+}
+
namespace Service::BtDrv {
/// Registers all BtDrv services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& sm);
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // 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 {
class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
public:
- explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
+ explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
@@ -56,7 +56,7 @@ public:
// clang-format on
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IBtmUserCore:ScanEvent");
connection_event = Kernel::WritableEvent::CreateEventPair(
@@ -108,7 +108,7 @@ private:
class BTM_USR final : public ServiceFramework<BTM_USR> {
public:
- explicit BTM_USR() : ServiceFramework{"btm:u"} {
+ explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BTM_USR::GetCore, "GetCore"},
@@ -123,8 +123,10 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IBtmUserCore>();
+ rb.PushIpcInterface<IBtmUserCore>(system);
}
+
+ Core::System& system;
};
class BTM final : public ServiceFramework<BTM> {
@@ -268,11 +270,11 @@ private:
}
};
-void InstallInterfaces(SM::ServiceManager& sm) {
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<BTM>()->InstallAsService(sm);
std::make_shared<BTM_DBG>()->InstallAsService(sm);
std::make_shared<BTM_SYS>()->InstallAsService(sm);
- std::make_shared<BTM_USR>()->InstallAsService(sm);
+ std::make_shared<BTM_USR>(system)->InstallAsService(sm);
}
} // 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 {
class ServiceManager;
}
+namespace Core {
+class System;
+};
+
namespace Service::BTM {
-void InstallInterfaces(SM::ServiceManager& sm);
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // 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 @@
namespace Service::Fatal {
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : ServiceFramework(name), module(std::move(module)), system(system) {}
Module::Interface::~Interface() = default;
@@ -64,7 +64,8 @@ enum class FatalType : u32 {
ErrorScreen = 2,
};
-static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
+static void GenerateErrorReport(Core::System& system, ResultCode error_code,
+ const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->GetTitleID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
@@ -101,18 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "{}", crash_report);
- Core::System::GetInstance().GetReporter().SaveCrashReport(
+ system.GetReporter().SaveCrashReport(
title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
info.backtrace_size, info.ArchAsString(), info.unk10);
}
-static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
+static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
+ const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
static_cast<u32>(fatal_type), error_code.raw);
switch (fatal_type) {
case FatalType::ErrorReportAndScreen:
- GenerateErrorReport(error_code, info);
+ GenerateErrorReport(system, error_code, info);
[[fallthrough]];
case FatalType::ErrorScreen:
// 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
break;
// Should not throw a fatal screen but should generate an error report
case FatalType::ErrorReport:
- GenerateErrorReport(error_code, info);
+ GenerateErrorReport(system, error_code, info);
break;
}
}
@@ -130,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto error_code = rp.Pop<ResultCode>();
- ThrowFatalError(error_code, FatalType::ErrorScreen, {});
+ ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -141,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
const auto error_code = rp.Pop<ResultCode>();
const auto fatal_type = rp.PopEnum<FatalType>();
- ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
+ ThrowFatalError(system, error_code, fatal_type,
+ {}); // No info is passed with ThrowFatalWithPolicy
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -157,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx)
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
- ThrowFatalError(error_code, fatal_type, info);
+ ThrowFatalError(system, error_code, fatal_type, info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
auto module = std::make_shared<Module>();
- std::make_shared<Fatal_P>(module)->InstallAsService(service_manager);
- std::make_shared<Fatal_U>(module)->InstallAsService(service_manager);
+ std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager);
+ std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager);
}
} // 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 @@
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service::Fatal {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> module, const char* name);
+ explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
~Interface() override;
void ThrowFatal(Kernel::HLERequestContext& ctx);
@@ -21,9 +25,10 @@ public:
protected:
std::shared_ptr<Module> module;
+ Core::System& system;
};
};
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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 @@
namespace Service::Fatal {
-Fatal_P::Fatal_P(std::shared_ptr<Module> module)
- : Module::Interface(std::move(module), "fatal:p") {}
+Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system)
+ : Module::Interface(std::move(module), system, "fatal:p") {}
Fatal_P::~Fatal_P() = default;
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 {
class Fatal_P final : public Module::Interface {
public:
- explicit Fatal_P(std::shared_ptr<Module> module);
+ explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system);
~Fatal_P() override;
};
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 @@
namespace Service::Fatal {
-Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
+Fatal_U::Fatal_U(std::shared_ptr<Module> module, Core::System& system)
+ : Module::Interface(std::move(module), system, "fatal:u") {
static const FunctionInfo functions[] = {
{0, &Fatal_U::ThrowFatal, "ThrowFatal"},
{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 {
class Fatal_U final : public Module::Interface {
public:
- explicit Fatal_U(std::shared_ptr<Module> module);
+ explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system);
~Fatal_U() override;
};
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 @@
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
+#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
@@ -25,14 +26,10 @@
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h"
#include "core/loader/loader.h"
+#include "core/settings.h"
namespace Service::FileSystem {
-// Size of emulated sd card free space, reported in bytes.
-// Just using 32GB because thats reasonable
-// TODO(DarkLordZach): Eventually make this configurable in settings.
-constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
-
// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
@@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
return MakeResult(dir);
}
-u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
- if (backing->IsWritable())
- return EMULATED_SD_REPORTED_SIZE;
-
- return 0;
-}
-
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
@@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
return FileSys::ERROR_PATH_NOT_FOUND;
}
-/**
- * Map of registered file systems, identified by type. Once an file system is registered here, it
- * is never removed until UnregisterFileSystems is called.
- */
-static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
-static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
-static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
-static std::unique_ptr<FileSys::BISFactory> bis_factory;
+FileSystemController::FileSystemController() = default;
+
+FileSystemController::~FileSystemController() = default;
-ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
- ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
+ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
return RESULT_SUCCESS;
}
-ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
- ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
+ResultCode FileSystemController::RegisterSaveData(
+ std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
+ ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return RESULT_SUCCESS;
}
-ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
+ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return RESULT_SUCCESS;
}
-ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
+ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered BIS");
return RESULT_SUCCESS;
}
-void SetPackedUpdate(FileSys::VirtualFile update_raw) {
+void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
LOG_TRACE(Service_FS, "Setting packed update for romfs");
if (romfs_factory == nullptr)
@@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) {
romfs_factory->SetPackedUpdate(std::move(update_raw));
}
-ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
LOG_TRACE(Service_FS, "Opening RomFS for current process");
if (romfs_factory == nullptr) {
@@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
return romfs_factory->OpenCurrentProcess();
}
-ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
- FileSys::ContentRecordType type) {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
+ u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
@@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
return romfs_factory->Open(title_id, storage_id, type);
}
-ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
- const FileSys::SaveDataDescriptor& descriptor) {
+ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
+ LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
+ static_cast<u8>(space), save_struct.DebugInfo());
+
+ if (save_data_factory == nullptr) {
+ return FileSys::ERROR_ENTITY_NOT_FOUND;
+ }
+
+ return save_data_factory->Create(space, save_struct);
+}
+
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), descriptor.DebugInfo());
@@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
return save_data_factory->Open(space, descriptor);
}
-ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
+ FileSys::SaveDataSpaceId space) const {
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
if (save_data_factory == nullptr) {
@@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
}
-ResultVal<FileSys::VirtualDir> OpenSDMC() {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
@@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
-FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
+ FileSys::BisPartitionId id) const {
+ LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
+
+ if (bis_factory == nullptr) {
+ return FileSys::ERROR_ENTITY_NOT_FOUND;
+ }
+
+ auto part = bis_factory->OpenPartition(id);
+ if (part == nullptr) {
+ return FileSys::ERROR_INVALID_ARGUMENT;
+ }
+
+ return MakeResult<FileSys::VirtualDir>(std::move(part));
+}
+
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
+ FileSys::BisPartitionId id) const {
+ LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
+
+ if (bis_factory == nullptr) {
+ return FileSys::ERROR_ENTITY_NOT_FOUND;
+ }
+
+ auto part = bis_factory->OpenPartitionStorage(id);
+ if (part == nullptr) {
+ return FileSys::ERROR_INVALID_ARGUMENT;
+ }
+
+ return MakeResult<FileSys::VirtualFile>(std::move(part));
+}
+
+u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
+ switch (id) {
+ case FileSys::StorageId::None:
+ case FileSys::StorageId::GameCard:
+ return 0;
+ case FileSys::StorageId::SdCard:
+ if (sdmc_factory == nullptr)
+ return 0;
+ return sdmc_factory->GetSDMCFreeSpace();
+ case FileSys::StorageId::Host:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
+ case FileSys::StorageId::NandSystem:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetSystemNANDFreeSpace();
+ case FileSys::StorageId::NandUser:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetUserNANDFreeSpace();
+ }
+
+ return 0;
+}
+
+u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
+ switch (id) {
+ case FileSys::StorageId::None:
+ case FileSys::StorageId::GameCard:
+ return 0;
+ case FileSys::StorageId::SdCard:
+ if (sdmc_factory == nullptr)
+ return 0;
+ return sdmc_factory->GetSDMCTotalSpace();
+ case FileSys::StorageId::Host:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetFullNANDTotalSpace();
+ case FileSys::StorageId::NandSystem:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetSystemNANDTotalSpace();
+ case FileSys::StorageId::NandUser:
+ if (bis_factory == nullptr)
+ return 0;
+ return bis_factory->GetUserNANDTotalSpace();
+ }
+
+ return 0;
+}
+
+FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
+ u64 title_id, u128 user_id) const {
if (save_data_factory == nullptr) {
return {0, 0};
}
@@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
return value;
}
-void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
- FileSys::SaveDataSize new_value) {
+void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+ FileSys::SaveDataSize new_value) const {
if (save_data_factory != nullptr)
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}
-FileSys::RegisteredCache* GetSystemNANDContents() {
+void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
+ gamecard = std::make_unique<FileSys::XCI>(file);
+ const auto dir = gamecard->ConcatenatedPseudoDirectory();
+ gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
+ gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
+}
+
+FileSys::XCI* FileSystemController::GetGameCard() const {
+ return gamecard.get();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
+ return gamecard_registered.get();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
+ return gamecard_placeholder.get();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
LOG_TRACE(Service_FS, "Opening System NAND Contents");
if (bis_factory == nullptr)
@@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() {
return bis_factory->GetSystemNANDContents();
}
-FileSys::RegisteredCache* GetUserNANDContents() {
+FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
LOG_TRACE(Service_FS, "Opening User NAND Contents");
if (bis_factory == nullptr)
@@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() {
return bis_factory->GetUserNANDContents();
}
-FileSys::RegisteredCache* GetSDMCContents() {
+FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
LOG_TRACE(Service_FS, "Opening SDMC Contents");
if (sdmc_factory == nullptr)
@@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() {
return sdmc_factory->GetSDMCContents();
}
-FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
+FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
+ LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetSystemNANDPlaceholder();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
+ LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetUserNANDPlaceholder();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
+ LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
+
+ if (sdmc_factory == nullptr)
+ return nullptr;
+
+ return sdmc_factory->GetSDMCPlaceholder();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
+ FileSys::StorageId id) const {
+ switch (id) {
+ case FileSys::StorageId::None:
+ case FileSys::StorageId::Host:
+ UNIMPLEMENTED();
+ return nullptr;
+ case FileSys::StorageId::GameCard:
+ return GetGameCardContents();
+ case FileSys::StorageId::NandSystem:
+ return GetSystemNANDContents();
+ case FileSys::StorageId::NandUser:
+ return GetUserNANDContents();
+ case FileSys::StorageId::SdCard:
+ return GetSDMCContents();
+ }
+
+ return nullptr;
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
+ FileSys::StorageId id) const {
+ switch (id) {
+ case FileSys::StorageId::None:
+ case FileSys::StorageId::Host:
+ UNIMPLEMENTED();
+ return nullptr;
+ case FileSys::StorageId::GameCard:
+ return GetGameCardPlaceholder();
+ case FileSys::StorageId::NandSystem:
+ return GetSystemNANDPlaceholder();
+ case FileSys::StorageId::NandUser:
+ return GetUserNANDPlaceholder();
+ case FileSys::StorageId::SdCard:
+ return GetSDMCPlaceholder();
+ }
+
+ return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
+ LOG_TRACE(Service_FS, "Opening system NAND content directory");
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetSystemNANDContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
+ LOG_TRACE(Service_FS, "Opening user NAND content directory");
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetUserNANDContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
+ LOG_TRACE(Service_FS, "Opening SDMC content directory");
+
+ if (sdmc_factory == nullptr)
+ return nullptr;
+
+ return sdmc_factory->GetSDMCContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
+ LOG_TRACE(Service_FS, "Opening NAND image directory");
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetImageDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
+ LOG_TRACE(Service_FS, "Opening SDMC image directory");
+
+ if (sdmc_factory == nullptr)
+ return nullptr;
+
+ return sdmc_factory->GetImageDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
+ switch (id) {
+ case ContentStorageId::System:
+ return GetSystemNANDContentDirectory();
+ case ContentStorageId::User:
+ return GetUserNANDContentDirectory();
+ case ContentStorageId::SdCard:
+ return GetSDMCContentDirectory();
+ }
+
+ return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
+ switch (id) {
+ case ImageDirectoryId::NAND:
+ return GetNANDImageDirectory();
+ case ImageDirectoryId::SdCard:
+ return GetSDMCImageDirectory();
+ }
+
+ return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
if (bis_factory == nullptr)
@@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
return bis_factory->GetModificationLoadRoot(title_id);
}
-FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
+FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
if (bis_factory == nullptr)
@@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
return bis_factory->GetModificationDumpRoot(title_id);
}
-void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
+void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
save_data_factory = nullptr;
@@ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
}
void InstallInterfaces(Core::System& system) {
- romfs_factory = nullptr;
- CreateFactories(*system.GetFilesystem(), false);
std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
- std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager());
+ std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
+ ->InstallAsService(system.ServiceManager());
}
} // 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 {
class BISFactory;
class RegisteredCache;
class RegisteredCacheUnion;
+class PlaceholderCache;
class RomFSFactory;
class SaveDataFactory;
class SDMCFactory;
+class XCI;
+enum class BisPartitionId : u32;
enum class ContentRecordType : u8;
enum class Mode : u32;
enum class SaveDataSpaceId : u8;
@@ -36,34 +39,91 @@ class ServiceManager;
namespace FileSystem {
-ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
-ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
-ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
-ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
-
-void SetPackedUpdate(FileSys::VirtualFile update_raw);
-ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
-ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
- FileSys::ContentRecordType type);
-ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
- const FileSys::SaveDataDescriptor& descriptor);
-ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
-ResultVal<FileSys::VirtualDir> OpenSDMC();
-
-FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
-void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
- FileSys::SaveDataSize new_value);
+enum class ContentStorageId : u32 {
+ System,
+ User,
+ SdCard,
+};
-FileSys::RegisteredCache* GetSystemNANDContents();
-FileSys::RegisteredCache* GetUserNANDContents();
-FileSys::RegisteredCache* GetSDMCContents();
+enum class ImageDirectoryId : u32 {
+ NAND,
+ SdCard,
+};
-FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
-FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
+class FileSystemController {
+public:
+ FileSystemController();
+ ~FileSystemController();
+
+ ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
+ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
+ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
+ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
+
+ void SetPackedUpdate(FileSys::VirtualFile update_raw);
+ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
+ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
+ FileSys::ContentRecordType type) const;
+ ResultVal<FileSys::VirtualDir> CreateSaveData(
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ ResultVal<FileSys::VirtualDir> OpenSaveData(
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
+ ResultVal<FileSys::VirtualDir> OpenSDMC() const;
+ ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
+ ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
+
+ u64 GetFreeSpaceSize(FileSys::StorageId id) const;
+ u64 GetTotalSpaceSize(FileSys::StorageId id) const;
+
+ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
+ u128 user_id) const;
+ void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+ FileSys::SaveDataSize new_value) const;
+
+ void SetGameCard(FileSys::VirtualFile file);
+ FileSys::XCI* GetGameCard() const;
+
+ FileSys::RegisteredCache* GetSystemNANDContents() const;
+ FileSys::RegisteredCache* GetUserNANDContents() const;
+ FileSys::RegisteredCache* GetSDMCContents() const;
+ FileSys::RegisteredCache* GetGameCardContents() const;
+
+ FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
+ FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
+ FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
+ FileSys::PlaceholderCache* GetGameCardPlaceholder() const;
+
+ FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
+ FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;
+
+ FileSys::VirtualDir GetSystemNANDContentDirectory() const;
+ FileSys::VirtualDir GetUserNANDContentDirectory() const;
+ FileSys::VirtualDir GetSDMCContentDirectory() const;
+
+ FileSys::VirtualDir GetNANDImageDirectory() const;
+ FileSys::VirtualDir GetSDMCImageDirectory() const;
+
+ FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
+ FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
+
+ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
+ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
+
+ // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
+ // above is called.
+ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
-// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
-// above is called.
-void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
+private:
+ std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
+ std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
+ std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
+ std::unique_ptr<FileSys::BISFactory> bis_factory;
+
+ std::unique_ptr<FileSys::XCI> gamecard;
+ std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
+ std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
+};
void InstallInterfaces(Core::System& system);
@@ -160,12 +220,6 @@ public:
ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
/**
- * Get the free space
- * @return The number of free bytes in the archive
- */
- u64 GetFreeSpaceSize() const;
-
- /**
* Get the type of the specified path
* @return The type of the specified path or error code
*/
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 @@
#include "core/file_sys/mode.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
@@ -30,6 +31,18 @@
namespace Service::FileSystem {
+struct SizeGetter {
+ std::function<u64()> get_free_size;
+ std::function<u64()> get_total_size;
+
+ static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
+ return {
+ [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
+ [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
+ };
+ }
+};
+
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
@@ -289,8 +302,8 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
- explicit IFileSystem(FileSys::VirtualDir backend)
- : ServiceFramework("IFileSystem"), backend(std::move(backend)) {
+ explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
+ : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},
@@ -467,14 +480,31 @@ public:
rb.Push(RESULT_SUCCESS);
}
+ void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(size.get_free_size());
+ }
+
+ void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(size.get_total_size());
+ }
+
private:
VfsDirectoryServiceWrapper backend;
+ SizeGetter size;
};
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public:
- explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
- : ServiceFramework("ISaveDataInfoReader") {
+ explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
+ : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
static const FunctionInfo functions[] = {
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
};
@@ -520,8 +550,13 @@ private:
}
void FindAllSaves(FileSys::SaveDataSpaceId space) {
- const auto save_root = OpenSaveDataSpace(space);
- ASSERT(save_root.Succeeded());
+ const auto save_root = fsc.OpenSaveDataSpace(space);
+
+ if (save_root.Failed() || *save_root == nullptr) {
+ LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
+ static_cast<u8>(space));
+ return;
+ }
for (const auto& type : (*save_root)->GetSubdirectories()) {
if (type->GetName() == "save") {
@@ -610,11 +645,13 @@ private:
};
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
+ FileSystemController& fsc;
std::vector<SaveDataInfo> info;
u64 next_entry_index = 0;
};
-FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) {
+FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
+ : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenFileSystem"},
@@ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
- IFileSystem filesystem(OpenSDMC().Unwrap());
+ IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
+ SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
- LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
- save_struct.DebugInfo(), uid[1], uid[0]);
+ LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
+ uid[1], uid[0]);
+
+ fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
- auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
+ auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
return;
}
- IFileSystem filesystem(std::move(dir.Unwrap()));
+ FileSys::StorageId id;
+ if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
+ id = FileSys::StorageId::NandUser;
+ } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
+ parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
+ id = FileSys::StorageId::SdCard;
+ } else {
+ id = FileSys::StorageId::NandSystem;
+ }
+
+ IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
+ rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
}
void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
- auto romfs = OpenRomFSCurrentProcess();
+ auto romfs = fsc.OpenRomFSCurrentProcess();
if (romfs.Failed()) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
static_cast<u8>(storage_id), unknown, title_id);
- auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
+ auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (data.Failed()) {
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 {
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
public:
- explicit FSP_SRV(const Core::Reporter& reporter);
+ explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
~FSP_SRV() override;
private:
@@ -51,6 +51,8 @@ private:
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
+ FileSystemController& fsc;
+
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
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:
class INotificationService final : public ServiceFramework<INotificationService> {
public:
- INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
+ INotificationService(Common::UUID uuid, Core::System& system)
+ : ServiceFramework("INotificationService"), uuid(uuid) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &INotificationService::GetEvent, "GetEvent"},
@@ -159,6 +160,9 @@ public:
// clang-format on
RegisterHandlers(functions);
+
+ notification_event = Kernel::WritableEvent::CreateEventPair(
+ system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
}
private:
@@ -167,13 +171,6 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
-
- if (!is_event_created) {
- auto& kernel = Core::System::GetInstance().Kernel();
- notification_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
- is_event_created = true;
- }
rb.PushCopyObjects(notification_event.readable);
}
@@ -261,21 +258,21 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<INotificationService>(uuid);
+ rb.PushIpcInterface<INotificationService>(uuid, system);
}
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : ServiceFramework(name), module(std::move(module)), system(system) {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
auto module = std::make_shared<Module>();
- std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager);
- std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager);
- std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager);
- std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager);
- std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager);
+ std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager);
+ std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager);
+ std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager);
+ std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager);
+ std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager);
}
} // 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 @@
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service::Friend {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> module, const char* name);
+ explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
~Interface() override;
void CreateFriendService(Kernel::HLERequestContext& ctx);
@@ -20,10 +24,11 @@ public:
protected:
std::shared_ptr<Module> module;
+ Core::System& system;
};
};
/// Registers all Friend services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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 @@
namespace Service::Friend {
-Friend::Friend(std::shared_ptr<Module> module, const char* name)
- : Interface(std::move(module), name) {
+Friend::Friend(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : Interface(std::move(module), system, name) {
static const FunctionInfo functions[] = {
{0, &Friend::CreateFriendService, "CreateFriendService"},
{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 {
class Friend final : public Module::Interface {
public:
- explicit Friend(std::shared_ptr<Module> module, const char* name);
+ explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name);
~Friend() override;
};
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 @@
namespace Service::HID {
-ControllerBase::ControllerBase() = default;
+ControllerBase::ControllerBase(Core::System& system) : system(system) {}
ControllerBase::~ControllerBase() = default;
void 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 {
class CoreTiming;
}
+namespace Core {
+class System;
+}
+
namespace Service::HID {
class ControllerBase {
public:
- ControllerBase();
+ explicit ControllerBase(Core::System& system);
virtual ~ControllerBase();
// Called when the controller is initialized
@@ -46,5 +50,7 @@ protected:
s64_le entry_count;
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+
+ Core::System& system;
};
} // 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;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
-Controller_DebugPad::Controller_DebugPad() = default;
+Controller_DebugPad::Controller_DebugPad(Core::System& system)
+ : ControllerBase(system), system(system) {}
Controller_DebugPad::~Controller_DebugPad() = default;
void 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 @@
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
- Controller_DebugPad();
+ explicit Controller_DebugPad(Core::System& system);
~Controller_DebugPad() override;
// Called when the controller is initialized
@@ -89,5 +89,6 @@ private:
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
+ Core::System& system;
};
} // 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 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
-Controller_Gesture::Controller_Gesture() = default;
+Controller_Gesture::Controller_Gesture(Core::System& system)
+ : ControllerBase(system), system(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void 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 @@
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
- Controller_Gesture();
+ explicit Controller_Gesture(Core::System& system);
~Controller_Gesture() override;
// Called when the controller is initialized
@@ -59,5 +59,6 @@ private:
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
+ Core::System& system;
};
} // 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 {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
-Controller_Keyboard::Controller_Keyboard() = default;
+Controller_Keyboard::Controller_Keyboard(Core::System& system)
+ : ControllerBase(system), system(system) {}
Controller_Keyboard::~Controller_Keyboard() = default;
void 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 @@
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
- Controller_Keyboard();
+ explicit Controller_Keyboard(Core::System& system);
~Controller_Keyboard() override;
// Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
+ Core::System& system;
};
} // 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 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
-Controller_Mouse::Controller_Mouse() = default;
+Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
Controller_Mouse::~Controller_Mouse() = default;
void 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 @@
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
- Controller_Mouse();
+ explicit Controller_Mouse(Core::System& system);
~Controller_Mouse() override;
// Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
+ Core::System& system;
};
} // 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) {
};
}
-Controller_NPad::Controller_NPad() = default;
+Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_NPad::~Controller_NPad() = default;
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
@@ -168,9 +168,11 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
}
void Controller_NPad::OnInit() {
- auto& kernel = Core::System::GetInstance().Kernel();
- styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged");
+ auto& kernel = system.Kernel();
+ for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
+ styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i));
+ }
if (!IsControllerActivated()) {
return;
@@ -453,7 +455,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
had_controller_update = true;
}
if (had_controller_update) {
- styleset_changed_event.writable->Signal();
+ styleset_changed_events[i].writable->Signal();
}
}
}
@@ -468,7 +470,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
}
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
- styleset_changed_event.writable->Signal();
hold_type = joy_hold_type;
}
@@ -479,7 +480,10 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
- shared_memory_entries[npad_index].pad_assignment = assignment_mode;
+ if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
+ styleset_changed_events[npad_index].writable->Signal();
+ shared_memory_entries[npad_index].pad_assignment = assignment_mode;
+ }
}
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
@@ -498,11 +502,13 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
last_processed_vibration = vibrations.back();
}
-Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const {
+Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
+ u32 npad_id) const {
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
- styleset_changed_event.writable->Signal();
- return styleset_changed_event.readable;
+ const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
+ styleset_event.writable->Signal();
+ return styleset_event.readable;
}
Controller_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?
class Controller_NPad final : public ControllerBase {
public:
- Controller_NPad();
+ explicit Controller_NPad(Core::System& system);
~Controller_NPad() override;
// Called when the controller is initialized
@@ -109,7 +109,7 @@ public:
void VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations);
- Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const;
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
@@ -315,7 +315,8 @@ private:
sticks;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
- Kernel::EventPair styleset_changed_event;
+ // Each controller should have their own styleset changed event
+ std::array<Kernel::EventPair, 10> styleset_changed_events;
Vibration last_processed_vibration{};
std::array<ControllerHolder, 10> connected_controllers{};
bool can_controllers_vibrate{true};
@@ -327,5 +328,6 @@ private:
std::array<ControllerPad, 10> npad_pad_states{};
bool IsControllerSupported(NPadControllerType controller);
bool is_in_lr_assignment_mode{false};
+ Core::System& system;
};
} // 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 @@
namespace Service::HID {
-Controller_Stubbed::Controller_Stubbed() = default;
+Controller_Stubbed::Controller_Stubbed(Core::System& system)
+ : ControllerBase(system), system(system) {}
Controller_Stubbed::~Controller_Stubbed() = default;
void 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 @@
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
- Controller_Stubbed();
+ explicit Controller_Stubbed(Core::System& system);
~Controller_Stubbed() override;
// Called when the controller is initialized
@@ -30,5 +30,6 @@ public:
private:
bool smart_update{};
std::size_t common_offset{};
+ Core::System& system;
};
} // 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 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
-Controller_Touchscreen::Controller_Touchscreen() = default;
+Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
+ : ControllerBase(system), system(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void 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 @@
namespace Service::HID {
class Controller_Touchscreen final : public ControllerBase {
public:
- Controller_Touchscreen();
+ explicit Controller_Touchscreen(Core::System& system);
~Controller_Touchscreen() override;
// Called when the controller is initialized
@@ -69,5 +69,6 @@ private:
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
+ Core::System& system;
};
} // 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 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
-Controller_XPad::Controller_XPad() = default;
+Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_XPad::~Controller_XPad() = default;
void 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 @@
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
- Controller_XPad();
+ explicit Controller_XPad(Core::System& system);
~Controller_XPad() override;
// Called when the controller is initialized
@@ -56,5 +56,6 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
+ Core::System& system;
};
} // 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
constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
-IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
+IAppletResource::IAppletResource(Core::System& system)
+ : ServiceFramework("IAppletResource"), system(system) {
static const FunctionInfo functions[] = {
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
@@ -74,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
// Register update callbacks
- auto& core_timing = Core::System::GetInstance().CoreTiming();
+ auto& core_timing = system.CoreTiming();
pad_update_event =
core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
UpdateControllers(userdata, cycles_late);
@@ -96,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) {
}
IAppletResource ::~IAppletResource() {
- Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0);
+ system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
}
void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -108,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
}
void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
- auto& core_timing = Core::System::GetInstance().CoreTiming();
+ auto& core_timing = system.CoreTiming();
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
for (const auto& controller : controllers) {
@@ -141,13 +142,13 @@ private:
std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
if (applet_resource == nullptr) {
- applet_resource = std::make_shared<IAppletResource>();
+ applet_resource = std::make_shared<IAppletResource>(system);
}
return applet_resource;
}
-Hid::Hid() : ServiceFramework("hid") {
+Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
@@ -286,7 +287,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
if (applet_resource == nullptr) {
- applet_resource = std::make_shared<IAppletResource>();
+ applet_resource = std::make_shared<IAppletResource>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -479,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetStyleSetChangedEvent());
+ .GetStyleSetChangedEvent(npad_id));
}
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
@@ -1053,14 +1054,14 @@ void ReloadInputDevices() {
Settings::values.is_device_reload_pending.store(true);
}
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<Hid>()->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<Hid>(system)->InstallAsService(service_manager);
std::make_shared<HidBus>()->InstallAsService(service_manager);
std::make_shared<HidDbg>()->InstallAsService(service_manager);
std::make_shared<HidSys>()->InstallAsService(service_manager);
std::make_shared<HidTmp>()->InstallAsService(service_manager);
- std::make_shared<IRS>()->InstallAsService(service_manager);
+ std::make_shared<IRS>(system)->InstallAsService(service_manager);
std::make_shared<IRS_SYS>()->InstallAsService(service_manager);
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 {
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
- IAppletResource();
+ explicit IAppletResource(Core::System& system);
~IAppletResource() override;
void ActivateController(HidController controller);
@@ -61,7 +61,7 @@ public:
private:
template <typename T>
void MakeController(HidController controller) {
- controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
+ controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -70,6 +70,7 @@ private:
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
Core::Timing::EventType* pad_update_event;
+ Core::System& system;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
controllers{};
@@ -77,7 +78,7 @@ private:
class Hid final : public ServiceFramework<Hid> {
public:
- Hid();
+ explicit Hid(Core::System& system);
~Hid() override;
std::shared_ptr<IAppletResource> GetAppletResource();
@@ -126,12 +127,13 @@ private:
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
+ Core::System& system;
};
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();
/// Registers all HID services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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 @@
namespace Service::HID {
-IRS::IRS() : ServiceFramework{"irs"} {
+IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
@@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} {
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());
+ rb.PushRaw<u64>(system.CoreTiming().GetTicks());
rb.PushRaw<u32>(0);
}
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 {
class IRS final : public ServiceFramework<IRS> {
public:
- explicit IRS();
+ explicit IRS(Core::System& system);
~IRS() override;
private:
@@ -39,6 +39,7 @@ private:
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
const u32 device_handle{0xABCD};
+ Core::System& system;
};
class 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:
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
public:
- explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
+ explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadNro, "LoadNro"},
@@ -364,7 +364,7 @@ public:
vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
Kernel::VMAPermission::ReadWrite);
- Core::System::GetInstance().InvalidateCpuInstructionCaches();
+ system.InvalidateCpuInstructionCaches();
nro.insert_or_assign(*map_address,
NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
@@ -430,7 +430,7 @@ public:
.IsSuccess());
}
- Core::System::GetInstance().InvalidateCpuInstructionCaches();
+ system.InvalidateCpuInstructionCaches();
nro.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
@@ -516,13 +516,14 @@ private:
Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
Common::Is4KBAligned(header.rw_size);
}
+ Core::System& system;
};
-void InstallInterfaces(SM::ServiceManager& sm) {
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<DebugMonitor>()->InstallAsService(sm);
std::make_shared<ProcessManager>()->InstallAsService(sm);
std::make_shared<Shell>()->InstallAsService(sm);
- std::make_shared<RelocatableObject>()->InstallAsService(sm);
+ std::make_shared<RelocatableObject>(system)->InstallAsService(sm);
}
} // 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;
namespace Service::LDR {
/// Registers all LDR services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& sm);
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // 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,
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
-Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {
- auto& kernel = Core::System::GetInstance().Kernel();
+Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
+ : ServiceFramework(name), module(std::move(module)), system(system) {
+ auto& kernel = system.Kernel();
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IUser:NFCTagDetected");
}
@@ -34,8 +34,8 @@ Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
- IUser(Module::Interface& nfp_interface)
- : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
+ IUser(Module::Interface& nfp_interface, Core::System& system)
+ : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
@@ -65,7 +65,7 @@ public:
};
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
deactivate_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
availability_change_event = Kernel::WritableEvent::CreateEventPair(
@@ -324,6 +324,7 @@ private:
Kernel::EventPair deactivate_event;
Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
+ Core::System& system;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
@@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IUser>(*this);
+ rb.PushIpcInterface<IUser>(*this, system);
}
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
@@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const
return amiibo;
}
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
auto module = std::make_shared<Module>();
- std::make_shared<NFP_User>(module)->InstallAsService(service_manager);
+ std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
}
} // 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 {
public:
class Interface : public ServiceFramework<Interface> {
public:
- explicit Interface(std::shared_ptr<Module> module, const char* name);
+ explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
~Interface() override;
struct ModelInfo {
@@ -43,9 +43,10 @@ public:
protected:
std::shared_ptr<Module> module;
+ Core::System& system;
};
};
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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 @@
namespace Service::NFP {
-NFP_User::NFP_User(std::shared_ptr<Module> module)
- : Module::Interface(std::move(module), "nfp:user") {
+NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system)
+ : Module::Interface(std::move(module), system, "nfp:user") {
static const FunctionInfo functions[] = {
{0, &NFP_User::CreateUserInterface, "CreateUserInterface"},
};
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 {
class NFP_User final : public Module::Interface {
public:
- explicit NFP_User(std::shared_ptr<Module> module);
+ explicit NFP_User(std::shared_ptr<Module> module, Core::System& system);
~NFP_User() override;
};
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:
class IRequest final : public ServiceFramework<IRequest> {
public:
- explicit IRequest() : ServiceFramework("IRequest") {
+ explicit IRequest(Core::System& system) : ServiceFramework("IRequest") {
static const FunctionInfo functions[] = {
{0, &IRequest::GetRequestState, "GetRequestState"},
{1, &IRequest::GetResult, "GetResult"},
@@ -61,7 +61,7 @@ public:
};
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
"IRequest:Event1");
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
@@ -130,7 +130,7 @@ public:
class IGeneralService final : public ServiceFramework<IGeneralService> {
public:
- IGeneralService();
+ IGeneralService(Core::System& system);
private:
void GetClientId(Kernel::HLERequestContext& ctx) {
@@ -155,7 +155,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IRequest>();
+ rb.PushIpcInterface<IRequest>(system);
}
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -198,9 +198,11 @@ private:
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
+ Core::System& system;
};
-IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
+IGeneralService::IGeneralService(Core::System& system)
+ : ServiceFramework("IGeneralService"), system(system) {
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
@@ -245,7 +247,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
public:
- explicit NetworkInterface(const char* name) : ServiceFramework{name} {
+ explicit NetworkInterface(const char* name, Core::System& system)
+ : ServiceFramework{name}, system(system) {
static const FunctionInfo functions[] = {
{4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"},
@@ -258,7 +261,7 @@ public:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
+ rb.PushIpcInterface<IGeneralService>(system);
}
void CreateGeneralService(Kernel::HLERequestContext& ctx) {
@@ -266,14 +269,17 @@ public:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
+ rb.PushIpcInterface<IGeneralService>(system);
}
+
+private:
+ Core::System& system;
};
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager);
- std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager);
- std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager);
+ std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager);
+ std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager);
}
} // 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 {
class ServiceManager;
}
+namespace Core {
+class System;
+}
+
namespace Service::NIFM {
/// Registers all NIFM services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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:
class IEnsureNetworkClockAvailabilityService final
: public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
public:
- IEnsureNetworkClockAvailabilityService()
+ explicit IEnsureNetworkClockAvailabilityService(Core::System& system)
: ServiceFramework("IEnsureNetworkClockAvailabilityService") {
static const FunctionInfo functions[] = {
{0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
@@ -139,7 +139,7 @@ public:
};
RegisterHandlers(functions);
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
finished_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic,
"IEnsureNetworkClockAvailabilityService:FinishEvent");
@@ -200,7 +200,7 @@ private:
class NTC final : public ServiceFramework<NTC> {
public:
- explicit NTC() : ServiceFramework{"ntc"} {
+ explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
@@ -218,7 +218,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
+ rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
}
// TODO(ogniK): Do we need these?
@@ -235,13 +235,14 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+ Core::System& system;
};
-void InstallInterfaces(SM::ServiceManager& sm) {
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<NIM>()->InstallAsService(sm);
std::make_shared<NIM_ECA>()->InstallAsService(sm);
std::make_shared<NIM_SHP>()->InstallAsService(sm);
- std::make_shared<NTC>()->InstallAsService(sm);
+ std::make_shared<NTC>(system)->InstallAsService(sm);
}
} // 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 {
class ServiceManager;
}
+namespace Core {
+class System;
+}
+
namespace Service::NIM {
-void InstallInterfaces(SM::ServiceManager& sm);
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // 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:
}
};
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+
std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
@@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<NS_SU>()->InstallAsService(service_manager);
std::make_shared<NS_VM>()->InstallAsService(service_manager);
- std::make_shared<PL_U>()->InstallAsService(service_manager);
+ std::make_shared<PL_U>(system)->InstallAsService(service_manager);
}
} // 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 @@
#include "core/hle/service/service.h"
-namespace Service::NS {
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace NS {
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
public:
@@ -91,6 +97,7 @@ private:
};
/// Registers all NS services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
-} // namespace Service::NS
+} // namespace NS
+} // 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 {
std::vector<FontRegion> shared_font_regions;
};
-PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
+PL_U::PL_U(Core::System& system)
+ : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) {
+
static const FunctionInfo functions[] = {
{0, &PL_U::RequestLoad, "RequestLoad"},
{1, &PL_U::GetLoadState, "GetLoadState"},
@@ -160,8 +162,11 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
};
RegisterHandlers(functions);
+
+ auto& fsc = system.GetFileSystemController();
+
// Attempt to load shared font data from disk
- const auto* nand = FileSystem::GetSystemNANDContents();
+ const auto* nand = fsc.GetSystemNANDContents();
std::size_t offset = 0;
// Rebuild shared fonts from data ncas
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
@@ -324,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
Kernel::MemoryState::Shared);
// Create shared font memory object
- auto& kernel = Core::System::GetInstance().Kernel();
+ auto& kernel = system.Kernel();
impl->shared_font_mem = Kernel::SharedMemory::Create(
kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
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 @@
#include <memory>
#include "core/hle/service/service.h"
-namespace Service::NS {
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace NS {
class PL_U final : public ServiceFramework<PL_U> {
public:
- PL_U();
+ explicit PL_U(Core::System& system);
~PL_U() override;
private:
@@ -24,6 +30,9 @@ private:
struct Impl;
std::unique_ptr<Impl> impl;
+ Core::System& system;
};
-} // namespace Service::NS
+} // namespace NS
+
+} // 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 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/service.h"
namespace Core {
class System;
@@ -38,8 +39,9 @@ public:
* @param output A buffer where the output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) = 0;
+ virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) = 0;
protected:
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
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
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:
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvdisp_disp0() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
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_
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
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:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_as_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
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)
: nvdevice(system), events_interface{events_interface} {}
nvhost_ctrl::~nvhost_ctrl() = default;
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
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:
explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
~nvhost_ctrl() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
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 {
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& input2, std::vector<u8>& output,
+ std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetCharacteristicsCommand:
- return GetCharacteristics(input, output);
+ return GetCharacteristics(input, output, output2, version);
case IoctlCommand::IocGetTPCMasksCommand:
return GetTPCMasks(input, output);
case IoctlCommand::IocGetActiveSlotMaskCommand:
@@ -44,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
return 0;
}
-u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& output2, IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
@@ -85,7 +87,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
- std::memcpy(output.data(), &params, output.size());
+
+ if (version == IoctlVersion::Version3) {
+ std::memcpy(output.data(), input.data(), output.size());
+ std::memcpy(output2.data(), &params.gc, output2.size());
+ } else {
+ std::memcpy(output.data(), &params, output.size());
+ }
return 0;
}
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:
explicit nvhost_ctrl_gpu(Core::System& system);
~nvhost_ctrl_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
enum class IoctlCommand : u32_le {
@@ -162,7 +163,8 @@ private:
};
static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
- u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& output2, IoctlVersion version);
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
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)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_gpu::~nvhost_gpu() = default;
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
@@ -50,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
return SubmitGPFIFO(input, output);
}
if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
- return KickoffPB(input, output);
+ return KickoffPB(input, output, input2, version);
}
}
@@ -146,8 +147,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
- params.address, params.num_entries, params.flags.raw);
+ LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
+ params.num_entries, params.flags.raw);
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -173,19 +174,24 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
return 0;
}
-u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
+u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
+ const std::vector<u8>& input2, IoctlVersion version) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
- params.address, params.num_entries, params.flags.raw);
+ LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
+ params.num_entries, params.flags.raw);
Tegra::CommandList entries(params.num_entries);
- Memory::ReadBlock(params.address, entries.data(),
- params.num_entries * sizeof(Tegra::CommandListHeader));
-
+ if (version == IoctlVersion::Version2) {
+ std::memcpy(entries.data(), input2.data(),
+ params.num_entries * sizeof(Tegra::CommandListHeader));
+ } else {
+ Memory::ReadBlock(params.address, entries.data(),
+ params.num_entries * sizeof(Tegra::CommandListHeader));
+ }
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
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:
explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
enum class IoctlCommand : u32_le {
@@ -183,7 +184,8 @@ private:
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
- u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
+ const std::vector<u8>& input2, IoctlVersion version);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
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 {
nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
nvhost_nvdec::~nvhost_nvdec() = default;
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
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:
explicit nvhost_nvdec(Core::System& system);
~nvhost_nvdec() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
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 {
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
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:
explicit nvhost_nvjpg(Core::System& system);
~nvhost_nvjpg() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
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 {
nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
nvhost_vic::~nvhost_vic() = default;
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
command.raw, input.size(), output.size());
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:
explicit nvhost_vic(Core::System& system);
~nvhost_vic() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
private:
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 {
return object->addr;
}
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::Create:
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:
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) override;
+ u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) override;
/// Represents an nvmap object.
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) {
rb.Push<u32>(0);
}
-void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
-
+void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
IPC::RequestParser rp{ctx};
u32 fd = rp.Pop<u32>();
u32 command = rp.Pop<u32>();
- std::vector<u8> output(ctx.GetWriteBufferSize());
+ /// Ioctl 3 has 2 outputs, first in the input params, second is the result
+ std::vector<u8> output(ctx.GetWriteBufferSize(0));
+ std::vector<u8> output2;
+ if (version == IoctlVersion::Version3) {
+ output2.resize((ctx.GetWriteBufferSize(1)));
+ }
+
+ /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
+ /// KickOfPB uses this
+ auto input = ctx.ReadBuffer(0);
+
+ std::vector<u8> input2;
+ if (version == IoctlVersion::Version2) {
+ input2 = ctx.ReadBuffer(1);
+ }
IoctlCtrl ctrl{};
- u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl);
+ u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
if (ctrl.must_delay) {
ctrl.fresh_call = false;
- ctx.SleepClientThread(
- "NVServices::DelayedResponse", ctrl.timeout,
- [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
- Kernel::ThreadWakeupReason reason) {
- IoctlCtrl ctrl2{ctrl};
- std::vector<u8> output2 = output;
- u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2);
- ctx.WriteBuffer(output2);
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push(result);
- },
- nvdrv->GetEventWriteable(ctrl.event_id));
+ ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
+ [=](Kernel::SharedPtr<Kernel::Thread> thread,
+ Kernel::HLERequestContext& ctx,
+ Kernel::ThreadWakeupReason reason) {
+ IoctlCtrl ctrl2{ctrl};
+ std::vector<u8> tmp_output = output;
+ std::vector<u8> tmp_output2 = output2;
+ u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
+ tmp_output2, ctrl2, version);
+ ctx.WriteBuffer(tmp_output, 0);
+ if (version == IoctlVersion::Version3) {
+ ctx.WriteBuffer(tmp_output2, 1);
+ }
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(result);
+ },
+ nvdrv->GetEventWriteable(ctrl.event_id));
} else {
ctx.WriteBuffer(output);
+ if (version == IoctlVersion::Version3) {
+ ctx.WriteBuffer(output2, 1);
+ }
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(result);
}
+void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NVDRV, "called");
+ IoctlBase(ctx, IoctlVersion::Version1);
+}
+
+void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NVDRV, "called");
+ IoctlBase(ctx, IoctlVersion::Version2);
+}
+
+void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NVDRV, "called");
+ IoctlBase(ctx, IoctlVersion::Version3);
+}
+
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
@@ -154,8 +189,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
{8, &NVDRV::SetClientPID, "SetClientPID"},
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
{10, nullptr, "InitializeDevtools"},
- {11, &NVDRV::Ioctl, "Ioctl2"},
- {12, nullptr, "Ioctl3"},
+ {11, &NVDRV::Ioctl2, "Ioctl2"},
+ {12, &NVDRV::Ioctl3, "Ioctl3"},
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
};
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:
private:
void Open(Kernel::HLERequestContext& ctx);
void Ioctl(Kernel::HLERequestContext& ctx);
+ void Ioctl2(Kernel::HLERequestContext& ctx);
+ void Ioctl3(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
void QueryEvent(Kernel::HLERequestContext& ctx);
@@ -31,6 +33,7 @@ private:
void FinishInitialize(Kernel::HLERequestContext& ctx);
void GetStatus(Kernel::HLERequestContext& ctx);
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
+ void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
std::shared_ptr<Module> nvdrv;
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 {
Busy = 3,
};
+enum class IoctlVersion : u32 {
+ Version1,
+ Version2,
+ Version3,
+};
+
struct IoctlCtrl {
// First call done to the servioce for services that call itself again after a call.
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) {
return fd;
}
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl) {
+u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version) {
auto itr = open_files.find(fd);
ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
auto& device = itr->second;
- return device->ioctl({command}, input, output, ctrl);
+ return device->ioctl({command}, input, input2, output, output2, ctrl, version);
}
ResultCode 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:
/// Opens a device node and returns a file descriptor to it.
u32 Open(const std::string& device_name);
/// Sends an ioctl command to the specified file descriptor.
- u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output,
- IoctlCtrl& ctrl);
+ u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
+ std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
+ IoctlVersion version);
/// Closes a device file descriptor and returns operation success.
ResultCode Close(u32 fd);
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 {
constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
-NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
- displays.emplace_back(0, "Default");
- displays.emplace_back(1, "External");
- displays.emplace_back(2, "Edid");
- displays.emplace_back(3, "Internal");
- displays.emplace_back(4, "Null");
+NVFlinger::NVFlinger(Core::System& system) : system(system) {
+ displays.emplace_back(0, "Default", system);
+ displays.emplace_back(1, "External", system);
+ displays.emplace_back(2, "Edid", system);
+ displays.emplace_back(3, "Internal", system);
+ displays.emplace_back(4, "Null", system);
// Schedule the screen composition events
- composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata,
- s64 cycles_late) {
- Compose();
- const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
- this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event);
- });
-
- core_timing.ScheduleEvent(frame_ticks, composition_event);
+ composition_event = system.CoreTiming().RegisterEvent(
+ "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
+ Compose();
+ const auto ticks =
+ Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
+ this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
+ composition_event);
+ });
+
+ system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
}
NVFlinger::~NVFlinger() {
- core_timing.UnscheduleEvent(composition_event, 0);
+ system.CoreTiming().UnscheduleEvent(composition_event, 0);
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -185,11 +187,9 @@ void NVFlinger::Compose() {
MicroProfileFlip();
if (!buffer) {
- auto& system_instance = Core::System::GetInstance();
-
// There was no queued buffer to draw, render previous frame
- system_instance.GetPerfStats().EndGameFrame();
- system_instance.GPU().SwapBuffers({});
+ system.GetPerfStats().EndGameFrame();
+ system.GPU().SwapBuffers({});
continue;
}
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;
class NVFlinger final {
public:
- explicit NVFlinger(Core::Timing::CoreTiming& core_timing);
+ explicit NVFlinger(Core::System& system);
~NVFlinger();
/// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -105,8 +105,7 @@ private:
/// Event that handles screen composition.
Core::Timing::EventType* composition_event;
- /// Core timing instance for registering/unregistering the composition event.
- Core::Timing::CoreTiming& core_timing;
+ Core::System& system;
};
} // 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <json.hpp>
-#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
-#include "common/scm_rev.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/service.h"
#include "core/reporter.h"
-#include "core/settings.h"
namespace Service::PlayReport {
class PlayReport final : public ServiceFramework<PlayReport> {
public:
- explicit PlayReport(const char* name) : ServiceFramework{name} {
+ explicit PlayReport(const char* name, Core::System& system)
+ : ServiceFramework{name}, system(system) {
// clang-format off
static const FunctionInfo functions[] = {
- {10100, nullptr, "SaveReportOld"},
- {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
- {10102, nullptr, "SaveReport"},
- {10103, nullptr, "SaveReportWithUser"},
+ {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"},
+ {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
+ {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
+ {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
{10200, nullptr, "RequestImmediateTransmission"},
{10300, nullptr, "GetTransmissionStatus"},
- {20100, nullptr, "SaveSystemReport"},
- {20101, nullptr, "SaveSystemReportWithUser"},
+ {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
+ {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
{20200, nullptr, "SetOperationMode"},
{30100, nullptr, "ClearStorage"},
{30200, nullptr, "ClearStatistics"},
@@ -47,7 +44,28 @@ public:
}
private:
- void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
+ template <Core::Reporter::PlayReportType Type>
+ void SaveReport(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = ctx.ReadBuffer(1);
+
+ LOG_DEBUG(Service_PREPO,
+ "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
+ static_cast<u8>(Type), process_id, data1.size(), data2.size());
+
+ const auto& reporter{system.GetReporter()};
+ reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
+ process_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ template <Core::Reporter::PlayReportType Type>
+ void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
@@ -57,24 +75,65 @@ private:
LOG_DEBUG(
Service_PREPO,
- "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
- user_id[1], user_id[0], process_id, data1.size(), data2.size());
+ "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, "
+ "data2_size={:016X}",
+ static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
+
+ const auto& reporter{system.GetReporter()};
+ reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
+ process_id, user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SaveSystemReport(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = ctx.ReadBuffer(1);
+
+ LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
+ title_id, data1.size(), data2.size());
+
+ const auto& reporter{system.GetReporter()};
+ reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2});
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
- const auto& reporter{Core::System::GetInstance().GetReporter()};
- reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
- user_id);
+ void SaveSystemReportWithUser(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto user_id = rp.PopRaw<u128>();
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = ctx.ReadBuffer(1);
+
+ LOG_DEBUG(Service_PREPO,
+ "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
+ "data2_size={:016X}",
+ user_id[1], user_id[0], title_id, data1.size(), data2.size());
+
+ const auto& reporter{system.GetReporter()};
+ reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2},
+ std::nullopt, user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ Core::System& system;
};
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
- std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
- std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
- std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
- std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager);
+ std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager);
+ std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager);
+ std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager);
+ std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager);
}
} // 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 {
class ServiceManager;
}
+namespace Core {
+class System;
+}
+
namespace Service::PlayReport {
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // 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
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
- auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
+ auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system);
+ system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
SM::ServiceManager::InstallInterfaces(sm);
Account::InstallInterfaces(system);
AM::InstallInterfaces(*sm, nv_flinger, system);
- AOC::InstallInterfaces(*sm);
+ AOC::InstallInterfaces(*sm, system);
APM::InstallInterfaces(system);
Audio::InstallInterfaces(*sm, system);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
- BtDrv::InstallInterfaces(*sm);
- BTM::InstallInterfaces(*sm);
+ BtDrv::InstallInterfaces(*sm, system);
+ BTM::InstallInterfaces(*sm, system);
Capture::InstallInterfaces(*sm);
ERPT::InstallInterfaces(*sm);
ES::InstallInterfaces(*sm);
EUPLD::InstallInterfaces(*sm);
- Fatal::InstallInterfaces(*sm);
+ Fatal::InstallInterfaces(*sm, system);
FGM::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(system);
- Friend::InstallInterfaces(*sm);
+ Friend::InstallInterfaces(*sm, system);
Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm);
- HID::InstallInterfaces(*sm);
+ HID::InstallInterfaces(*sm, system);
LBL::InstallInterfaces(*sm);
LDN::InstallInterfaces(*sm);
- LDR::InstallInterfaces(*sm);
+ LDR::InstallInterfaces(*sm, system);
LM::InstallInterfaces(*sm);
Migration::InstallInterfaces(*sm);
Mii::InstallInterfaces(*sm);
MM::InstallInterfaces(*sm);
NCM::InstallInterfaces(*sm);
NFC::InstallInterfaces(*sm);
- NFP::InstallInterfaces(*sm);
- NIFM::InstallInterfaces(*sm);
- NIM::InstallInterfaces(*sm);
+ NFP::InstallInterfaces(*sm, system);
+ NIFM::InstallInterfaces(*sm, system);
+ NIM::InstallInterfaces(*sm, system);
NPNS::InstallInterfaces(*sm);
- NS::InstallInterfaces(*sm);
+ NS::InstallInterfaces(*sm, system);
Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
PCIe::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
PCV::InstallInterfaces(*sm);
- PlayReport::InstallInterfaces(*sm);
+ PlayReport::InstallInterfaces(*sm, system);
PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm);
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 {
class System;
}
-namespace FileSys {
-class VfsFilesystem;
-}
-
namespace Kernel {
class ClientPort;
class ServerPort;
@@ -31,6 +27,10 @@ class HLERequestContext;
namespace Service {
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
namespace SM {
class ServiceManager;
}
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 @@
namespace Service::Time {
Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
- const char* name)
- : Module::Interface(std::move(time), std::move(shared_memory), name) {
+ Core::System& system, const char* name)
+ : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
// clang-format off
static const FunctionInfo functions[] = {
{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;
class Time final : public Module::Interface {
public:
explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
- const char* name);
+ Core::System& system, const char* name);
~Time() override;
};
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:
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
- ISteadyClock(std::shared_ptr<SharedMemory> shared_memory)
- : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) {
+ ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
+ : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
};
@@ -150,12 +150,13 @@ private:
}
SteadyClockTimePoint GetCurrentTimePoint() const {
- const auto& core_timing = Core::System::GetInstance().CoreTiming();
+ const auto& core_timing = system.CoreTiming();
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
return {static_cast<u64_le>(ms.count() / 1000), {}};
}
std::shared_ptr<SharedMemory> shared_memory;
+ Core::System& system;
};
class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -290,7 +291,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>(shared_memory);
+ rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -325,7 +326,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
return;
}
- const auto& core_timing = Core::System::GetInstance().CoreTiming();
+ const auto& core_timing = system.CoreTiming();
const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
@@ -407,8 +408,10 @@ void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
}
Module::Interface::Interface(std::shared_ptr<Module> time,
- std::shared_ptr<SharedMemory> shared_memory, const char* name)
- : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {}
+ std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
+ const char* name)
+ : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
+ system(system) {}
Module::Interface::~Interface() = default;
@@ -416,9 +419,11 @@ void InstallInterfaces(Core::System& system) {
auto time = std::make_shared<Module>();
auto shared_mem = std::make_shared<SharedMemory>(system);
- std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager());
- std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager());
- std::make_shared<Time>(std::move(time), shared_mem, "time:u")
+ std::make_shared<Time>(time, shared_mem, system, "time:a")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(time, shared_mem, system, "time:s")
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
->InstallAsService(system.ServiceManager());
}
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:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> time,
- std::shared_ptr<SharedMemory> shared_memory, const char* name);
+ std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
+ const char* name);
~Interface() override;
void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -97,6 +98,7 @@ public:
protected:
std::shared_ptr<Module> time;
std::shared_ptr<SharedMemory> shared_memory;
+ Core::System& system;
};
};
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 @@
namespace Service::VI {
-Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
- auto& kernel = Core::System::GetInstance().Kernel();
+Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
+ auto& kernel = system.Kernel();
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
fmt::format("Display VSync Event {}", id));
}
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:
/// @param id The unique ID for this display.
/// @param name The name for this display.
///
- Display(u64 id, std::string name);
+ Display(u64 id, std::string name, Core::System& system);
~Display();
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 @@
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// Register the RomFS if a ".romfs" file was found
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
romfs = *romfs_iter;
- Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+ Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+ std::make_unique<FileSys::RomFSFactory>(*this));
}
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 @@
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
@@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
}
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
- Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+ Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+ std::make_unique<FileSys::RomFSFactory>(*this));
}
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 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
@@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
}
if (romfs != nullptr) {
- Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+ Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+ std::make_unique<FileSys::RomFSFactory>(*this));
}
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,
auto& system = Core::System::GetInstance();
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
if (!cheats.empty()) {
- system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
- load_base + program_image.size());
+ system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
}
}
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 @@
#include <vector>
#include "common/common_types.h"
+#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
@@ -26,20 +27,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
if (nsp->GetStatus() != ResultStatus::Success)
return;
- if (nsp->IsExtractedType())
- return;
-
- const auto control_nca =
- nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
- if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
- return;
-
- std::tie(nacp_file, icon_file) =
- FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
} else {
+ const auto control_nca =
+ nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
+ if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
+ return;
+
+ std::tie(nacp_file, icon_file) =
+ FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
+
if (title_id == 0)
return;
@@ -56,11 +55,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
if (nsp.GetStatus() == ResultStatus::Success) {
// Extracted Type case
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
- FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
+ FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
return FileType::NSP;
}
- // Non-Ectracted Type case
+ // Non-Extracted Type case
if (!nsp.IsExtractedType() &&
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
@@ -77,7 +76,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (title_id == 0) {
+ if (!nsp->IsExtractedType() && title_id == 0) {
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
}
@@ -91,7 +90,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
return {nsp_program_status, {}};
}
- if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
+ if (!nsp->IsExtractedType() &&
+ nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
}
@@ -106,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
- Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
+ std::move(update_raw));
}
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 @@
#include <vector>
#include "common/common_types.h"
+#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
@@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
- Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
+ std::move(update_raw));
}
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
// During boot, current_page_table might not be set yet, in which case we need not flush
if (Core::System::GetInstance().IsPoweredOn()) {
- Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
- size * PAGE_SIZE);
+ auto& gpu = Core::System::GetInstance().GPU();
+ for (u64 i = 0; i < size; i++) {
+ const auto page = base + i;
+ if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
+ gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
+ }
+ }
}
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 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <locale>
+#include "common/hex_util.h"
+#include "common/microprofile.h"
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/memory/cheat_engine.h"
+
+namespace Memory {
+
+constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
+constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
+
+StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
+ const CheatProcessMetadata& metadata)
+ : system(system), metadata(metadata) {}
+
+StandardVmCallbacks::~StandardVmCallbacks() = default;
+
+void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
+ ReadBlock(SanitizeAddress(address), data, size);
+}
+
+void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
+ WriteBlock(SanitizeAddress(address), data, size);
+}
+
+u64 StandardVmCallbacks::HidKeysDown() {
+ const auto applet_resource =
+ system.ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
+ if (applet_resource == nullptr) {
+ LOG_WARNING(CheatEngine,
+ "Attempted to read input state, but applet resource is not initialized!");
+ return false;
+ }
+
+ const auto press_state =
+ applet_resource
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
+ .GetAndResetPressState();
+ return press_state & KEYPAD_BITMASK;
+}
+
+void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
+ LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
+}
+
+void StandardVmCallbacks::CommandLog(std::string_view data) {
+ LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
+ data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
+}
+
+VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
+ if ((in < metadata.main_nso_extents.base ||
+ in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
+ (in < metadata.heap_extents.base ||
+ in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
+ LOG_ERROR(CheatEngine,
+ "Cheat attempting to access memory at invalid address={:016X}, if this "
+ "persists, "
+ "the cheat may be incorrect. However, this may be normal early in execution if "
+ "the game has not properly set up yet.",
+ in);
+ return 0; ///< Invalid addresses will hard crash
+ }
+
+ return in;
+}
+
+CheatParser::~CheatParser() = default;
+
+TextCheatParser::~TextCheatParser() = default;
+
+namespace {
+template <char match>
+std::string_view ExtractName(std::string_view data, std::size_t start_index) {
+ auto end_index = start_index;
+ while (data[end_index] != match) {
+ ++end_index;
+ if (end_index > data.size() ||
+ (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
+ return {};
+ }
+ }
+
+ return data.substr(start_index, end_index - start_index);
+}
+} // Anonymous namespace
+
+std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
+ std::string_view data) const {
+ std::vector<CheatEntry> out(1);
+ std::optional<u64> current_entry = std::nullopt;
+
+ for (std::size_t i = 0; i < data.size(); ++i) {
+ if (::isspace(data[i])) {
+ continue;
+ }
+
+ if (data[i] == '{') {
+ current_entry = 0;
+
+ if (out[*current_entry].definition.num_opcodes > 0) {
+ return {};
+ }
+
+ const auto name = ExtractName<'}'>(data, i + 1);
+ if (name.empty()) {
+ return {};
+ }
+
+ std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
+ std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
+ name.size()));
+ out[*current_entry]
+ .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
+ '\0';
+
+ i += name.length() + 1;
+ } else if (data[i] == '[') {
+ current_entry = out.size();
+ out.emplace_back();
+
+ const auto name = ExtractName<']'>(data, i + 1);
+ if (name.empty()) {
+ return {};
+ }
+
+ std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
+ std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
+ name.size()));
+ out[*current_entry]
+ .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
+ '\0';
+
+ i += name.length() + 1;
+ } else if (::isxdigit(data[i])) {
+ if (!current_entry || out[*current_entry].definition.num_opcodes >=
+ out[*current_entry].definition.opcodes.size()) {
+ return {};
+ }
+
+ const auto hex = std::string(data.substr(i, 8));
+ if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
+ return {};
+ }
+
+ out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
+ std::stoul(hex, nullptr, 0x10);
+
+ i += 8;
+ } else {
+ return {};
+ }
+ }
+
+ out[0].enabled = out[0].definition.num_opcodes > 0;
+ out[0].cheat_id = 0;
+
+ for (u32 i = 1; i < out.size(); ++i) {
+ out[i].enabled = out[i].definition.num_opcodes > 0;
+ out[i].cheat_id = i;
+ }
+
+ return out;
+}
+
+CheatEngine::CheatEngine(Core::System& system, std::vector<CheatEntry> cheats,
+ const std::array<u8, 0x20>& build_id)
+ : system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique<StandardVmCallbacks>(
+ system, metadata)},
+ cheats(std::move(cheats)) {
+ metadata.main_nso_build_id = build_id;
+}
+
+CheatEngine::~CheatEngine() {
+ core_timing.UnscheduleEvent(event, 0);
+}
+
+void CheatEngine::Initialize() {
+ event = core_timing.RegisterEvent(
+ "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
+ [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
+
+ metadata.process_id = system.CurrentProcess()->GetProcessID();
+ metadata.title_id = system.CurrentProcess()->GetTitleID();
+
+ const auto& vm_manager = system.CurrentProcess()->VMManager();
+ metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()};
+ metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(),
+ vm_manager.GetAddressSpaceSize()};
+ metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()};
+
+ is_pending_reload.exchange(true);
+}
+
+void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
+ metadata.main_nso_extents = {main_region_begin, main_region_size};
+}
+
+void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
+ this->cheats = std::move(cheats);
+ is_pending_reload.exchange(true);
+}
+
+MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
+
+void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
+ if (is_pending_reload.exchange(false)) {
+ vm.LoadProgram(cheats);
+ }
+
+ if (vm.GetProgramSize() == 0) {
+ return;
+ }
+
+ MICROPROFILE_SCOPE(Cheat_Engine);
+
+ vm.Execute(metadata);
+
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
+}
+
+} // 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 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <vector>
+#include "common/common_types.h"
+#include "core/memory/dmnt_cheat_types.h"
+#include "core/memory/dmnt_cheat_vm.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+class CoreTiming;
+struct EventType;
+} // namespace Core::Timing
+
+namespace Memory {
+
+class StandardVmCallbacks : public DmntCheatVm::Callbacks {
+public:
+ StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
+ ~StandardVmCallbacks() override;
+
+ void MemoryRead(VAddr address, void* data, u64 size) override;
+ void MemoryWrite(VAddr address, const void* data, u64 size) override;
+ u64 HidKeysDown() override;
+ void DebugLog(u8 id, u64 value) override;
+ void CommandLog(std::string_view data) override;
+
+private:
+ VAddr SanitizeAddress(VAddr address) const;
+
+ const CheatProcessMetadata& metadata;
+ const Core::System& system;
+};
+
+// Intermediary class that parses a text file or other disk format for storing cheats into a
+// CheatList object, that can be used for execution.
+class CheatParser {
+public:
+ virtual ~CheatParser();
+
+ virtual std::vector<CheatEntry> Parse(const Core::System& system,
+ std::string_view data) const = 0;
+};
+
+// CheatParser implementation that parses text files
+class TextCheatParser final : public CheatParser {
+public:
+ ~TextCheatParser() override;
+
+ std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override;
+};
+
+// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
+class CheatEngine final {
+public:
+ CheatEngine(Core::System& system_, std::vector<CheatEntry> cheats_,
+ const std::array<u8, 0x20>& build_id);
+ ~CheatEngine();
+
+ void Initialize();
+ void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
+
+ void Reload(std::vector<CheatEntry> cheats);
+
+private:
+ void FrameCallback(u64 userdata, s64 cycles_late);
+
+ DmntCheatVm vm;
+ CheatProcessMetadata metadata;
+
+ std::vector<CheatEntry> cheats;
+ std::atomic_bool is_pending_reload{false};
+
+ Core::Timing::EventType* event{};
+ Core::Timing::CoreTiming& core_timing;
+ Core::System& system;
+};
+
+} // 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 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2019 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Memory {
+
+struct MemoryRegionExtents {
+ u64 base{};
+ u64 size{};
+};
+
+struct CheatProcessMetadata {
+ u64 process_id{};
+ u64 title_id{};
+ MemoryRegionExtents main_nso_extents{};
+ MemoryRegionExtents heap_extents{};
+ MemoryRegionExtents alias_extents{};
+ MemoryRegionExtents address_space_extents{};
+ std::array<u8, 0x20> main_nso_build_id{};
+};
+
+struct CheatDefinition {
+ std::array<char, 0x40> readable_name{};
+ u32 num_opcodes{};
+ std::array<u32, 0x100> opcodes{};
+};
+
+struct CheatEntry {
+ bool enabled{};
+ u32 cheat_id{};
+ CheatDefinition definition{};
+};
+
+} // 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 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2019 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#include "common/assert.h"
+#include "common/scope_exit.h"
+#include "core/memory/dmnt_cheat_types.h"
+#include "core/memory/dmnt_cheat_vm.h"
+
+namespace Memory {
+
+DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {}
+
+DmntCheatVm::~DmntCheatVm() = default;
+
+void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
+ callbacks->DebugLog(static_cast<u8>(log_id), value);
+}
+
+void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
+ if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Store Static");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
+ callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
+ } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Begin Conditional");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
+ callbacks->CommandLog(
+ fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
+ callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
+ } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: End Conditional");
+ } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
+ if (ctrl_loop->start_loop) {
+ callbacks->CommandLog("Opcode: Start Loop");
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
+ callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
+ } else {
+ callbacks->CommandLog("Opcode: End Loop");
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
+ }
+ } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Load Register Static");
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
+ callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value));
+ } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Load Register Memory");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
+ callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
+ } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Store Static to Address");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
+ if (str_static->add_offset_reg) {
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
+ }
+ callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
+ callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
+ } else if (auto perform_math_static =
+ std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Perform Static Arithmetic");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
+ callbacks->CommandLog(
+ fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
+ callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
+ } else if (auto begin_keypress_cond =
+ std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Begin Keypress Conditional");
+ callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
+ } else if (auto perform_math_reg =
+ std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Perform Register Arithmetic");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
+ callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
+ callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
+ if (perform_math_reg->has_immediate) {
+ callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
+ } else {
+ callbacks->CommandLog(
+ fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
+ }
+ } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Store Register to Address");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
+ callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
+ callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
+ callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
+ switch (str_register->ofs_type) {
+ case StoreRegisterOffsetType::None:
+ break;
+ case StoreRegisterOffsetType::Reg:
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
+ break;
+ case StoreRegisterOffsetType::Imm:
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
+ break;
+ case StoreRegisterOffsetType::MemReg:
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
+ break;
+ case StoreRegisterOffsetType::MemImm:
+ case StoreRegisterOffsetType::MemImmReg:
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
+ break;
+ }
+ } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Begin Register Conditional");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
+ callbacks->CommandLog(
+ fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
+ callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
+ switch (begin_reg_cond->comp_type) {
+ case CompareRegisterValueType::StaticValue:
+ callbacks->CommandLog("Comp Type: Static Value");
+ callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
+ break;
+ case CompareRegisterValueType::OtherRegister:
+ callbacks->CommandLog("Comp Type: Other Register");
+ callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
+ break;
+ case CompareRegisterValueType::MemoryRelAddr:
+ callbacks->CommandLog("Comp Type: Memory Relative Address");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
+ break;
+ case CompareRegisterValueType::MemoryOfsReg:
+ callbacks->CommandLog("Comp Type: Memory Offset Register");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
+ break;
+ case CompareRegisterValueType::RegisterRelAddr:
+ callbacks->CommandLog("Comp Type: Register Relative Address");
+ callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
+ break;
+ case CompareRegisterValueType::RegisterOfsReg:
+ callbacks->CommandLog("Comp Type: Register Offset Register");
+ callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
+ break;
+ }
+ } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Save or Restore Register");
+ callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
+ callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
+ callbacks->CommandLog(
+ fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type)));
+ } else if (auto save_restore_regmask =
+ std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Save or Restore Register Mask");
+ callbacks->CommandLog(
+ fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ callbacks->CommandLog(
+ fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
+ }
+ } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Debug Log");
+ callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
+ callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
+ callbacks->CommandLog(
+ fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
+ switch (debug_log->val_type) {
+ case DebugLogValueType::RegisterValue:
+ callbacks->CommandLog("Val Type: Register Value");
+ callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
+ break;
+ case DebugLogValueType::MemoryRelAddr:
+ callbacks->CommandLog("Val Type: Memory Relative Address");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
+ break;
+ case DebugLogValueType::MemoryOfsReg:
+ callbacks->CommandLog("Val Type: Memory Offset Register");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
+ break;
+ case DebugLogValueType::RegisterRelAddr:
+ callbacks->CommandLog("Val Type: Register Relative Address");
+ callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
+ break;
+ case DebugLogValueType::RegisterOfsReg:
+ callbacks->CommandLog("Val Type: Register Offset Register");
+ callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
+ break;
+ }
+ } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
+ callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
+ }
+}
+
+DmntCheatVm::Callbacks::~Callbacks() = default;
+
+bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
+ // If we've ever seen a decode failure, return false.
+ bool valid = decode_success;
+ CheatVmOpcode opcode = {};
+ SCOPE_EXIT({
+ decode_success &= valid;
+ if (valid) {
+ out = opcode;
+ }
+ });
+
+ // Helper function for getting instruction dwords.
+ const auto GetNextDword = [&] {
+ if (instruction_ptr >= num_opcodes) {
+ valid = false;
+ return static_cast<u32>(0);
+ }
+ return program[instruction_ptr++];
+ };
+
+ // Helper function for parsing a VmInt.
+ const auto GetNextVmInt = [&](const u32 bit_width) {
+ VmInt val{};
+
+ const u32 first_dword = GetNextDword();
+ switch (bit_width) {
+ case 1:
+ val.bit8 = static_cast<u8>(first_dword);
+ break;
+ case 2:
+ val.bit16 = static_cast<u16>(first_dword);
+ break;
+ case 4:
+ val.bit32 = first_dword;
+ break;
+ case 8:
+ val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword());
+ break;
+ }
+
+ return val;
+ };
+
+ // Read opcode.
+ const u32 first_dword = GetNextDword();
+ if (!valid) {
+ return valid;
+ }
+
+ auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF));
+ if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) {
+ opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
+ ((first_dword >> 24) & 0xF));
+ }
+ if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) {
+ opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
+ ((first_dword >> 20) & 0xF));
+ }
+
+ // detect condition start.
+ switch (opcode_type) {
+ case CheatVmOpcodeType::BeginConditionalBlock:
+ case CheatVmOpcodeType::BeginKeypressConditionalBlock:
+ case CheatVmOpcodeType::BeginRegisterConditionalBlock:
+ opcode.begin_conditional_block = true;
+ break;
+ default:
+ opcode.begin_conditional_block = false;
+ break;
+ }
+
+ switch (opcode_type) {
+ case CheatVmOpcodeType::StoreStatic: {
+ StoreStaticOpcode store_static{};
+ // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+ // Read additional words.
+ const u32 second_dword = GetNextDword();
+ store_static.bit_width = (first_dword >> 24) & 0xF;
+ store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+ store_static.offset_register = ((first_dword >> 16) & 0xF);
+ store_static.rel_address =
+ (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+ store_static.value = GetNextVmInt(store_static.bit_width);
+ opcode.opcode = store_static;
+ } break;
+ case CheatVmOpcodeType::BeginConditionalBlock: {
+ BeginConditionalOpcode begin_cond{};
+ // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+ // Read additional words.
+ const u32 second_dword = GetNextDword();
+ begin_cond.bit_width = (first_dword >> 24) & 0xF;
+ begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+ begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
+ begin_cond.rel_address =
+ (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+ begin_cond.value = GetNextVmInt(begin_cond.bit_width);
+ opcode.opcode = begin_cond;
+ } break;
+ case CheatVmOpcodeType::EndConditionalBlock: {
+ // 20000000
+ // There's actually nothing left to process here!
+ opcode.opcode = EndConditionalOpcode{};
+ } break;
+ case CheatVmOpcodeType::ControlLoop: {
+ ControlLoopOpcode ctrl_loop{};
+ // 300R0000 VVVVVVVV
+ // 310R0000
+ // Parse register, whether loop start or loop end.
+ ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
+ ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
+
+ // Read number of iters if loop start.
+ if (ctrl_loop.start_loop) {
+ ctrl_loop.num_iters = GetNextDword();
+ }
+ opcode.opcode = ctrl_loop;
+ } break;
+ case CheatVmOpcodeType::LoadRegisterStatic: {
+ LoadRegisterStaticOpcode ldr_static{};
+ // 400R0000 VVVVVVVV VVVVVVVV
+ // Read additional words.
+ ldr_static.reg_index = ((first_dword >> 16) & 0xF);
+ ldr_static.value =
+ (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
+ opcode.opcode = ldr_static;
+ } break;
+ case CheatVmOpcodeType::LoadRegisterMemory: {
+ LoadRegisterMemoryOpcode ldr_memory{};
+ // 5TMRI0AA AAAAAAAA
+ // Read additional words.
+ const u32 second_dword = GetNextDword();
+ ldr_memory.bit_width = (first_dword >> 24) & 0xF;
+ ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+ ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
+ ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
+ ldr_memory.rel_address =
+ (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+ opcode.opcode = ldr_memory;
+ } break;
+ case CheatVmOpcodeType::StoreStaticToAddress: {
+ StoreStaticToAddressOpcode str_static{};
+ // 6T0RIor0 VVVVVVVV VVVVVVVV
+ // Read additional words.
+ str_static.bit_width = (first_dword >> 24) & 0xF;
+ str_static.reg_index = ((first_dword >> 16) & 0xF);
+ str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+ str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
+ str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
+ str_static.value =
+ (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
+ opcode.opcode = str_static;
+ } break;
+ case CheatVmOpcodeType::PerformArithmeticStatic: {
+ PerformArithmeticStaticOpcode perform_math_static{};
+ // 7T0RC000 VVVVVVVV
+ // Read additional words.
+ perform_math_static.bit_width = (first_dword >> 24) & 0xF;
+ perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
+ perform_math_static.math_type =
+ static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF);
+ perform_math_static.value = GetNextDword();
+ opcode.opcode = perform_math_static;
+ } break;
+ case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
+ BeginKeypressConditionalOpcode begin_keypress_cond{};
+ // 8kkkkkkk
+ // Just parse the mask.
+ begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
+ } break;
+ case CheatVmOpcodeType::PerformArithmeticRegister: {
+ PerformArithmeticRegisterOpcode perform_math_reg{};
+ // 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
+ perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
+ perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF);
+ perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
+ perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
+ perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
+ if (perform_math_reg.has_immediate) {
+ perform_math_reg.src_reg_2_index = 0;
+ perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
+ } else {
+ perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
+ }
+ opcode.opcode = perform_math_reg;
+ } break;
+ case CheatVmOpcodeType::StoreRegisterToAddress: {
+ StoreRegisterToAddressOpcode str_register{};
+ // ATSRIOxa (aaaaaaaa)
+ // A = opcode 10
+ // T = bit width
+ // S = src register index
+ // R = address register index
+ // I = 1 if increment address register, 0 if not increment address register
+ // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
+ // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
+ // Relative Address
+ // x = offset register (for offset type 1), memory type (for offset type 3)
+ // a = relative address (for offset type 2+3)
+ str_register.bit_width = (first_dword >> 24) & 0xF;
+ str_register.str_reg_index = ((first_dword >> 20) & 0xF);
+ str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
+ str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+ str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF));
+ str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
+ switch (str_register.ofs_type) {
+ case StoreRegisterOffsetType::None:
+ case StoreRegisterOffsetType::Reg:
+ // Nothing more to do
+ break;
+ case StoreRegisterOffsetType::Imm:
+ str_register.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ case StoreRegisterOffsetType::MemReg:
+ str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ break;
+ case StoreRegisterOffsetType::MemImm:
+ case StoreRegisterOffsetType::MemImmReg:
+ str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ str_register.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ default:
+ str_register.ofs_type = StoreRegisterOffsetType::None;
+ break;
+ }
+ opcode.opcode = str_register;
+ } break;
+ case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
+ BeginRegisterConditionalOpcode begin_reg_cond{};
+ // C0TcSX##
+ // C0TcS0Ma aaaaaaaa
+ // C0TcS1Mr
+ // C0TcS2Ra aaaaaaaa
+ // C0TcS3Rr
+ // C0TcS400 VVVVVVVV (VVVVVVVV)
+ // C0TcS5X0
+ // C0 = opcode 0xC0
+ // T = bit width
+ // c = condition type.
+ // S = source register.
+ // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
+ // register,
+ // 2 = register with relative offset, 3 = register with offset register, 4 = static
+ // value, 5 = other register.
+ // M = memory type.
+ // R = address register.
+ // a = relative address.
+ // r = offset register.
+ // X = other register.
+ // V = value.
+ begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
+ begin_reg_cond.cond_type =
+ static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
+ begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
+ begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF);
+
+ switch (begin_reg_cond.comp_type) {
+ case CompareRegisterValueType::StaticValue:
+ begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width);
+ break;
+ case CompareRegisterValueType::OtherRegister:
+ begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
+ break;
+ case CompareRegisterValueType::MemoryRelAddr:
+ begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ begin_reg_cond.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ case CompareRegisterValueType::MemoryOfsReg:
+ begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ break;
+ case CompareRegisterValueType::RegisterRelAddr:
+ begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ begin_reg_cond.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ case CompareRegisterValueType::RegisterOfsReg:
+ begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ break;
+ }
+ opcode.opcode = begin_reg_cond;
+ } break;
+ case CheatVmOpcodeType::SaveRestoreRegister: {
+ SaveRestoreRegisterOpcode save_restore_reg{};
+ // C10D0Sx0
+ // C1 = opcode 0xC1
+ // D = destination index.
+ // S = source index.
+ // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
+ // a register.
+ // NOTE: If we add more save slots later, current encoding is backwards compatible.
+ save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
+ save_restore_reg.src_index = (first_dword >> 8) & 0xF;
+ save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF);
+ opcode.opcode = save_restore_reg;
+ } break;
+ case CheatVmOpcodeType::SaveRestoreRegisterMask: {
+ SaveRestoreRegisterMaskOpcode save_restore_regmask{};
+ // C2x0XXXX
+ // C2 = opcode 0xC2
+ // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
+ // X = 16-bit bitmask, bit i --> save or restore register i.
+ save_restore_regmask.op_type =
+ static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF);
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
+ }
+ opcode.opcode = save_restore_regmask;
+ } break;
+ case CheatVmOpcodeType::DebugLog: {
+ DebugLogOpcode debug_log{};
+ // FFFTIX##
+ // FFFTI0Ma aaaaaaaa
+ // FFFTI1Mr
+ // FFFTI2Ra aaaaaaaa
+ // FFFTI3Rr
+ // FFFTI4X0
+ // FFF = opcode 0xFFF
+ // T = bit width.
+ // I = log id.
+ // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
+ // register,
+ // 2 = register with relative offset, 3 = register with offset register, 4 = register
+ // value.
+ // M = memory type.
+ // R = address register.
+ // a = relative address.
+ // r = offset register.
+ // X = value register.
+ debug_log.bit_width = (first_dword >> 16) & 0xF;
+ debug_log.log_id = ((first_dword >> 12) & 0xF);
+ debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF);
+
+ switch (debug_log.val_type) {
+ case DebugLogValueType::RegisterValue:
+ debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
+ break;
+ case DebugLogValueType::MemoryRelAddr:
+ debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ debug_log.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ case DebugLogValueType::MemoryOfsReg:
+ debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+ debug_log.ofs_reg_index = (first_dword & 0xF);
+ break;
+ case DebugLogValueType::RegisterRelAddr:
+ debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+ debug_log.rel_address =
+ ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ break;
+ case DebugLogValueType::RegisterOfsReg:
+ debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+ debug_log.ofs_reg_index = (first_dword & 0xF);
+ break;
+ }
+ opcode.opcode = debug_log;
+ } break;
+ case CheatVmOpcodeType::ExtendedWidth:
+ case CheatVmOpcodeType::DoubleExtendedWidth:
+ default:
+ // Unrecognized instruction cannot be decoded.
+ valid = false;
+ opcode.opcode = UnrecognizedInstruction{opcode_type};
+ break;
+ }
+
+ // End decoding.
+ return valid;
+}
+
+void DmntCheatVm::SkipConditionalBlock() {
+ if (condition_depth > 0) {
+ // We want to continue until we're out of the current block.
+ const std::size_t desired_depth = condition_depth - 1;
+
+ CheatVmOpcode skip_opcode{};
+ while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) {
+ // Decode instructions until we see end of the current conditional block.
+ // NOTE: This is broken in gateway's implementation.
+ // Gateway currently checks for "0x2" instead of "0x20000000"
+ // In addition, they do a linear scan instead of correctly decoding opcodes.
+ // This causes issues if "0x2" appears as an immediate in the conditional block...
+
+ // We also support nesting of conditional blocks, and Gateway does not.
+ if (skip_opcode.begin_conditional_block) {
+ condition_depth++;
+ } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
+ condition_depth--;
+ }
+ }
+ } else {
+ // Skipping, but condition_depth = 0.
+ // This is an error condition.
+ // However, I don't actually believe it is possible for this to happen.
+ // I guess we'll throw a fatal error here, so as to encourage me to fix the VM
+ // in the event that someone triggers it? I don't know how you'd do that.
+ UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM");
+ }
+}
+
+u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
+ switch (bit_width) {
+ case 1:
+ return value.bit8;
+ case 2:
+ return value.bit16;
+ case 4:
+ return value.bit32;
+ case 8:
+ return value.bit64;
+ default:
+ // Invalid bit width -> return 0.
+ return 0;
+ }
+}
+
+u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
+ MemoryAccessType mem_type, u64 rel_address) {
+ switch (mem_type) {
+ case MemoryAccessType::MainNso:
+ default:
+ return metadata.main_nso_extents.base + rel_address;
+ case MemoryAccessType::Heap:
+ return metadata.heap_extents.base + rel_address;
+ }
+}
+
+void DmntCheatVm::ResetState() {
+ registers.fill(0);
+ saved_values.fill(0);
+ loop_tops.fill(0);
+ instruction_ptr = 0;
+ condition_depth = 0;
+ decode_success = true;
+}
+
+bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
+ // Reset opcode count.
+ num_opcodes = 0;
+
+ for (std::size_t i = 0; i < entries.size(); i++) {
+ if (entries[i].enabled) {
+ // Bounds check.
+ if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) {
+ num_opcodes = 0;
+ return false;
+ }
+
+ for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
+ program[num_opcodes++] = entries[i].definition.opcodes[n];
+ }
+ }
+ }
+
+ return true;
+}
+
+void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
+ CheatVmOpcode cur_opcode{};
+
+ // Get Keys down.
+ u64 kDown = callbacks->HidKeysDown();
+
+ callbacks->CommandLog("Started VM execution.");
+ callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
+ callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
+ callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
+
+ // Clear VM state.
+ ResetState();
+
+ // Loop until program finishes.
+ while (DecodeNextOpcode(cur_opcode)) {
+ callbacks->CommandLog(
+ fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
+
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
+ }
+
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
+ }
+ LogOpcode(cur_opcode);
+
+ // Increment conditional depth, if relevant.
+ if (cur_opcode.begin_conditional_block) {
+ condition_depth++;
+ }
+
+ if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) {
+ // Calculate address, write value to memory.
+ u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type,
+ store_static->rel_address +
+ registers[store_static->offset_register]);
+ u64 dst_value = GetVmInt(store_static->value, store_static->bit_width);
+ switch (store_static->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
+ break;
+ }
+ } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
+ // Read value from memory.
+ u64 src_address =
+ GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
+ u64 src_value = 0;
+ switch (store_static->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
+ break;
+ }
+ // Check against condition.
+ u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width);
+ bool cond_met = false;
+ switch (begin_cond->cond_type) {
+ case ConditionalComparisonType::GT:
+ cond_met = src_value > cond_value;
+ break;
+ case ConditionalComparisonType::GE:
+ cond_met = src_value >= cond_value;
+ break;
+ case ConditionalComparisonType::LT:
+ cond_met = src_value < cond_value;
+ break;
+ case ConditionalComparisonType::LE:
+ cond_met = src_value <= cond_value;
+ break;
+ case ConditionalComparisonType::EQ:
+ cond_met = src_value == cond_value;
+ break;
+ case ConditionalComparisonType::NE:
+ cond_met = src_value != cond_value;
+ break;
+ }
+ // Skip conditional block if condition not met.
+ if (!cond_met) {
+ SkipConditionalBlock();
+ }
+ } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
+ // Decrement the condition depth.
+ // We will assume, graciously, that mismatched conditional block ends are a nop.
+ if (condition_depth > 0) {
+ condition_depth--;
+ }
+ } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
+ if (ctrl_loop->start_loop) {
+ // Start a loop.
+ registers[ctrl_loop->reg_index] = ctrl_loop->num_iters;
+ loop_tops[ctrl_loop->reg_index] = instruction_ptr;
+ } else {
+ // End a loop.
+ registers[ctrl_loop->reg_index]--;
+ if (registers[ctrl_loop->reg_index] != 0) {
+ instruction_ptr = loop_tops[ctrl_loop->reg_index];
+ }
+ }
+ } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) {
+ // Set a register to a static value.
+ registers[ldr_static->reg_index] = ldr_static->value;
+ } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) {
+ // Choose source address.
+ u64 src_address;
+ if (ldr_memory->load_from_reg) {
+ src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address;
+ } else {
+ src_address =
+ GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address);
+ }
+ // Read into register. Gateway only reads on valid bitwidth.
+ switch (ldr_memory->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
+ ldr_memory->bit_width);
+ break;
+ }
+ } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
+ // Calculate address.
+ u64 dst_address = registers[str_static->reg_index];
+ u64 dst_value = str_static->value;
+ if (str_static->add_offset_reg) {
+ dst_address += registers[str_static->offset_reg_index];
+ }
+ // Write value to memory. Gateway only writes on valid bitwidth.
+ switch (str_static->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
+ break;
+ }
+ // Increment register if relevant.
+ if (str_static->increment_reg) {
+ registers[str_static->reg_index] += str_static->bit_width;
+ }
+ } else if (auto perform_math_static =
+ std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) {
+ // Do requested math.
+ switch (perform_math_static->math_type) {
+ case RegisterArithmeticType::Addition:
+ registers[perform_math_static->reg_index] +=
+ static_cast<u64>(perform_math_static->value);
+ break;
+ case RegisterArithmeticType::Subtraction:
+ registers[perform_math_static->reg_index] -=
+ static_cast<u64>(perform_math_static->value);
+ break;
+ case RegisterArithmeticType::Multiplication:
+ registers[perform_math_static->reg_index] *=
+ static_cast<u64>(perform_math_static->value);
+ break;
+ case RegisterArithmeticType::LeftShift:
+ registers[perform_math_static->reg_index] <<=
+ static_cast<u64>(perform_math_static->value);
+ break;
+ case RegisterArithmeticType::RightShift:
+ registers[perform_math_static->reg_index] >>=
+ static_cast<u64>(perform_math_static->value);
+ break;
+ default:
+ // Do not handle extensions here.
+ break;
+ }
+ // Apply bit width.
+ switch (perform_math_static->bit_width) {
+ case 1:
+ registers[perform_math_static->reg_index] =
+ static_cast<u8>(registers[perform_math_static->reg_index]);
+ break;
+ case 2:
+ registers[perform_math_static->reg_index] =
+ static_cast<u16>(registers[perform_math_static->reg_index]);
+ break;
+ case 4:
+ registers[perform_math_static->reg_index] =
+ static_cast<u32>(registers[perform_math_static->reg_index]);
+ break;
+ case 8:
+ registers[perform_math_static->reg_index] =
+ static_cast<u64>(registers[perform_math_static->reg_index]);
+ break;
+ }
+ } else if (auto begin_keypress_cond =
+ std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) {
+ // Check for keypress.
+ if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
+ // Keys not pressed. Skip conditional block.
+ SkipConditionalBlock();
+ }
+ } else if (auto perform_math_reg =
+ std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
+ const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index];
+ const u64 operand_2_value =
+ perform_math_reg->has_immediate
+ ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width)
+ : registers[perform_math_reg->src_reg_2_index];
+
+ u64 res_val = 0;
+ // Do requested math.
+ switch (perform_math_reg->math_type) {
+ case RegisterArithmeticType::Addition:
+ res_val = operand_1_value + operand_2_value;
+ break;
+ case RegisterArithmeticType::Subtraction:
+ res_val = operand_1_value - operand_2_value;
+ break;
+ case RegisterArithmeticType::Multiplication:
+ res_val = operand_1_value * operand_2_value;
+ break;
+ case RegisterArithmeticType::LeftShift:
+ res_val = operand_1_value << operand_2_value;
+ break;
+ case RegisterArithmeticType::RightShift:
+ res_val = operand_1_value >> operand_2_value;
+ break;
+ case RegisterArithmeticType::LogicalAnd:
+ res_val = operand_1_value & operand_2_value;
+ break;
+ case RegisterArithmeticType::LogicalOr:
+ res_val = operand_1_value | operand_2_value;
+ break;
+ case RegisterArithmeticType::LogicalNot:
+ res_val = ~operand_1_value;
+ break;
+ case RegisterArithmeticType::LogicalXor:
+ res_val = operand_1_value ^ operand_2_value;
+ break;
+ case RegisterArithmeticType::None:
+ res_val = operand_1_value;
+ break;
+ }
+
+ // Apply bit width.
+ switch (perform_math_reg->bit_width) {
+ case 1:
+ res_val = static_cast<u8>(res_val);
+ break;
+ case 2:
+ res_val = static_cast<u16>(res_val);
+ break;
+ case 4:
+ res_val = static_cast<u32>(res_val);
+ break;
+ case 8:
+ res_val = static_cast<u64>(res_val);
+ break;
+ }
+
+ // Save to register.
+ registers[perform_math_reg->dst_reg_index] = res_val;
+ } else if (auto str_register =
+ std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) {
+ // Calculate address.
+ u64 dst_value = registers[str_register->str_reg_index];
+ u64 dst_address = registers[str_register->addr_reg_index];
+ switch (str_register->ofs_type) {
+ case StoreRegisterOffsetType::None:
+ // Nothing more to do
+ break;
+ case StoreRegisterOffsetType::Reg:
+ dst_address += registers[str_register->ofs_reg_index];
+ break;
+ case StoreRegisterOffsetType::Imm:
+ dst_address += str_register->rel_address;
+ break;
+ case StoreRegisterOffsetType::MemReg:
+ dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+ registers[str_register->addr_reg_index]);
+ break;
+ case StoreRegisterOffsetType::MemImm:
+ dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+ str_register->rel_address);
+ break;
+ case StoreRegisterOffsetType::MemImmReg:
+ dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+ registers[str_register->addr_reg_index] +
+ str_register->rel_address);
+ break;
+ }
+
+ // Write value to memory. Write only on valid bitwidth.
+ switch (str_register->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
+ break;
+ }
+
+ // Increment register if relevant.
+ if (str_register->increment_reg) {
+ registers[str_register->addr_reg_index] += str_register->bit_width;
+ }
+ } else if (auto begin_reg_cond =
+ std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) {
+ // Get value from register.
+ u64 src_value = 0;
+ switch (begin_reg_cond->bit_width) {
+ case 1:
+ src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul);
+ break;
+ case 2:
+ src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ src_value =
+ static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+
+ // Read value from memory.
+ u64 cond_value = 0;
+ if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) {
+ cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width);
+ } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) {
+ switch (begin_reg_cond->bit_width) {
+ case 1:
+ cond_value =
+ static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul);
+ break;
+ case 2:
+ cond_value =
+ static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ cond_value =
+ static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+ } else {
+ u64 cond_address = 0;
+ switch (begin_reg_cond->comp_type) {
+ case CompareRegisterValueType::MemoryRelAddr:
+ cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
+ begin_reg_cond->rel_address);
+ break;
+ case CompareRegisterValueType::MemoryOfsReg:
+ cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
+ registers[begin_reg_cond->ofs_reg_index]);
+ break;
+ case CompareRegisterValueType::RegisterRelAddr:
+ cond_address =
+ registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address;
+ break;
+ case CompareRegisterValueType::RegisterOfsReg:
+ cond_address = registers[begin_reg_cond->addr_reg_index] +
+ registers[begin_reg_cond->ofs_reg_index];
+ break;
+ default:
+ break;
+ }
+ switch (begin_reg_cond->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
+ break;
+ }
+ }
+
+ // Check against condition.
+ bool cond_met = false;
+ switch (begin_reg_cond->cond_type) {
+ case ConditionalComparisonType::GT:
+ cond_met = src_value > cond_value;
+ break;
+ case ConditionalComparisonType::GE:
+ cond_met = src_value >= cond_value;
+ break;
+ case ConditionalComparisonType::LT:
+ cond_met = src_value < cond_value;
+ break;
+ case ConditionalComparisonType::LE:
+ cond_met = src_value <= cond_value;
+ break;
+ case ConditionalComparisonType::EQ:
+ cond_met = src_value == cond_value;
+ break;
+ case ConditionalComparisonType::NE:
+ cond_met = src_value != cond_value;
+ break;
+ }
+
+ // Skip conditional block if condition not met.
+ if (!cond_met) {
+ SkipConditionalBlock();
+ }
+ } else if (auto save_restore_reg =
+ std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
+ // Save or restore a register.
+ switch (save_restore_reg->op_type) {
+ case SaveRestoreRegisterOpType::ClearRegs:
+ registers[save_restore_reg->dst_index] = 0ul;
+ break;
+ case SaveRestoreRegisterOpType::ClearSaved:
+ saved_values[save_restore_reg->dst_index] = 0ul;
+ break;
+ case SaveRestoreRegisterOpType::Save:
+ saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index];
+ break;
+ case SaveRestoreRegisterOpType::Restore:
+ default:
+ registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index];
+ break;
+ }
+ } else if (auto save_restore_regmask =
+ std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) {
+ // Save or restore register mask.
+ u64* src;
+ u64* dst;
+ switch (save_restore_regmask->op_type) {
+ case SaveRestoreRegisterOpType::ClearSaved:
+ case SaveRestoreRegisterOpType::Save:
+ src = registers.data();
+ dst = saved_values.data();
+ break;
+ case SaveRestoreRegisterOpType::ClearRegs:
+ case SaveRestoreRegisterOpType::Restore:
+ default:
+ src = registers.data();
+ dst = saved_values.data();
+ break;
+ }
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ if (save_restore_regmask->should_operate[i]) {
+ switch (save_restore_regmask->op_type) {
+ case SaveRestoreRegisterOpType::ClearSaved:
+ case SaveRestoreRegisterOpType::ClearRegs:
+ dst[i] = 0ul;
+ break;
+ case SaveRestoreRegisterOpType::Save:
+ case SaveRestoreRegisterOpType::Restore:
+ default:
+ dst[i] = src[i];
+ break;
+ }
+ }
+ }
+ } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
+ // Read value from memory.
+ u64 log_value = 0;
+ if (debug_log->val_type == DebugLogValueType::RegisterValue) {
+ switch (debug_log->bit_width) {
+ case 1:
+ log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul);
+ break;
+ case 2:
+ log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ log_value =
+ static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ log_value = static_cast<u64>(registers[debug_log->val_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+ } else {
+ u64 val_address = 0;
+ switch (debug_log->val_type) {
+ case DebugLogValueType::MemoryRelAddr:
+ val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
+ debug_log->rel_address);
+ break;
+ case DebugLogValueType::MemoryOfsReg:
+ val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
+ registers[debug_log->ofs_reg_index]);
+ break;
+ case DebugLogValueType::RegisterRelAddr:
+ val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address;
+ break;
+ case DebugLogValueType::RegisterOfsReg:
+ val_address =
+ registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index];
+ break;
+ default:
+ break;
+ }
+ switch (debug_log->bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
+ break;
+ }
+ }
+
+ // Log value.
+ DebugLog(debug_log->log_id, log_value);
+ }
+ }
+}
+
+} // 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 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2019 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#pragma once
+
+#include <variant>
+#include <vector>
+#include <fmt/printf.h>
+#include "common/common_types.h"
+#include "core/memory/dmnt_cheat_types.h"
+
+namespace Memory {
+
+enum class CheatVmOpcodeType : u32 {
+ StoreStatic = 0,
+ BeginConditionalBlock = 1,
+ EndConditionalBlock = 2,
+ ControlLoop = 3,
+ LoadRegisterStatic = 4,
+ LoadRegisterMemory = 5,
+ StoreStaticToAddress = 6,
+ PerformArithmeticStatic = 7,
+ BeginKeypressConditionalBlock = 8,
+
+ // These are not implemented by Gateway's VM.
+ PerformArithmeticRegister = 9,
+ StoreRegisterToAddress = 10,
+ Reserved11 = 11,
+
+ // This is a meta entry, and not a real opcode.
+ // This is to facilitate multi-nybble instruction decoding.
+ ExtendedWidth = 12,
+
+ // Extended width opcodes.
+ BeginRegisterConditionalBlock = 0xC0,
+ SaveRestoreRegister = 0xC1,
+ SaveRestoreRegisterMask = 0xC2,
+
+ // This is a meta entry, and not a real opcode.
+ // This is to facilitate multi-nybble instruction decoding.
+ DoubleExtendedWidth = 0xF0,
+
+ // Double-extended width opcodes.
+ DebugLog = 0xFFF,
+};
+
+enum class MemoryAccessType : u32 {
+ MainNso = 0,
+ Heap = 1,
+};
+
+enum class ConditionalComparisonType : u32 {
+ GT = 1,
+ GE = 2,
+ LT = 3,
+ LE = 4,
+ EQ = 5,
+ NE = 6,
+};
+
+enum class RegisterArithmeticType : u32 {
+ Addition = 0,
+ Subtraction = 1,
+ Multiplication = 2,
+ LeftShift = 3,
+ RightShift = 4,
+
+ // These are not supported by Gateway's VM.
+ LogicalAnd = 5,
+ LogicalOr = 6,
+ LogicalNot = 7,
+ LogicalXor = 8,
+
+ None = 9,
+};
+
+enum class StoreRegisterOffsetType : u32 {
+ None = 0,
+ Reg = 1,
+ Imm = 2,
+ MemReg = 3,
+ MemImm = 4,
+ MemImmReg = 5,
+};
+
+enum class CompareRegisterValueType : u32 {
+ MemoryRelAddr = 0,
+ MemoryOfsReg = 1,
+ RegisterRelAddr = 2,
+ RegisterOfsReg = 3,
+ StaticValue = 4,
+ OtherRegister = 5,
+};
+
+enum class SaveRestoreRegisterOpType : u32 {
+ Restore = 0,
+ Save = 1,
+ ClearSaved = 2,
+ ClearRegs = 3,
+};
+
+enum class DebugLogValueType : u32 {
+ MemoryRelAddr = 0,
+ MemoryOfsReg = 1,
+ RegisterRelAddr = 2,
+ RegisterOfsReg = 3,
+ RegisterValue = 4,
+};
+
+union VmInt {
+ u8 bit8;
+ u16 bit16;
+ u32 bit32;
+ u64 bit64;
+};
+
+struct StoreStaticOpcode {
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ u32 offset_register{};
+ u64 rel_address{};
+ VmInt value{};
+};
+
+struct BeginConditionalOpcode {
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ ConditionalComparisonType cond_type{};
+ u64 rel_address{};
+ VmInt value{};
+};
+
+struct EndConditionalOpcode {};
+
+struct ControlLoopOpcode {
+ bool start_loop{};
+ u32 reg_index{};
+ u32 num_iters{};
+};
+
+struct LoadRegisterStaticOpcode {
+ u32 reg_index{};
+ u64 value{};
+};
+
+struct LoadRegisterMemoryOpcode {
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ u32 reg_index{};
+ bool load_from_reg{};
+ u64 rel_address{};
+};
+
+struct StoreStaticToAddressOpcode {
+ u32 bit_width{};
+ u32 reg_index{};
+ bool increment_reg{};
+ bool add_offset_reg{};
+ u32 offset_reg_index{};
+ u64 value{};
+};
+
+struct PerformArithmeticStaticOpcode {
+ u32 bit_width{};
+ u32 reg_index{};
+ RegisterArithmeticType math_type{};
+ u32 value{};
+};
+
+struct BeginKeypressConditionalOpcode {
+ u32 key_mask{};
+};
+
+struct PerformArithmeticRegisterOpcode {
+ u32 bit_width{};
+ RegisterArithmeticType math_type{};
+ u32 dst_reg_index{};
+ u32 src_reg_1_index{};
+ u32 src_reg_2_index{};
+ bool has_immediate{};
+ VmInt value{};
+};
+
+struct StoreRegisterToAddressOpcode {
+ u32 bit_width{};
+ u32 str_reg_index{};
+ u32 addr_reg_index{};
+ bool increment_reg{};
+ StoreRegisterOffsetType ofs_type{};
+ MemoryAccessType mem_type{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
+};
+
+struct BeginRegisterConditionalOpcode {
+ u32 bit_width{};
+ ConditionalComparisonType cond_type{};
+ u32 val_reg_index{};
+ CompareRegisterValueType comp_type{};
+ MemoryAccessType mem_type{};
+ u32 addr_reg_index{};
+ u32 other_reg_index{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
+ VmInt value{};
+};
+
+struct SaveRestoreRegisterOpcode {
+ u32 dst_index{};
+ u32 src_index{};
+ SaveRestoreRegisterOpType op_type{};
+};
+
+struct SaveRestoreRegisterMaskOpcode {
+ SaveRestoreRegisterOpType op_type{};
+ std::array<bool, 0x10> should_operate{};
+};
+
+struct DebugLogOpcode {
+ u32 bit_width{};
+ u32 log_id{};
+ DebugLogValueType val_type{};
+ MemoryAccessType mem_type{};
+ u32 addr_reg_index{};
+ u32 val_reg_index{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
+};
+
+struct UnrecognizedInstruction {
+ CheatVmOpcodeType opcode{};
+};
+
+struct CheatVmOpcode {
+ bool begin_conditional_block{};
+ std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
+ LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
+ PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
+ PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
+ BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
+ SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction>
+ opcode{};
+};
+
+class DmntCheatVm {
+public:
+ /// Helper Type for DmntCheatVm <=> yuzu Interface
+ class Callbacks {
+ public:
+ virtual ~Callbacks();
+
+ virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
+ virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
+
+ virtual u64 HidKeysDown() = 0;
+
+ virtual void DebugLog(u8 id, u64 value) = 0;
+ virtual void CommandLog(std::string_view data) = 0;
+ };
+
+ static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
+ static constexpr std::size_t NumRegisters = 0x10;
+
+ explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks);
+ ~DmntCheatVm();
+
+ std::size_t GetProgramSize() const {
+ return this->num_opcodes;
+ }
+
+ bool LoadProgram(const std::vector<CheatEntry>& cheats);
+ void Execute(const CheatProcessMetadata& metadata);
+
+private:
+ std::unique_ptr<Callbacks> callbacks;
+
+ std::size_t num_opcodes = 0;
+ std::size_t instruction_ptr = 0;
+ std::size_t condition_depth = 0;
+ bool decode_success = false;
+ std::array<u32, MaximumProgramOpcodeCount> program{};
+ std::array<u64, NumRegisters> registers{};
+ std::array<u64, NumRegisters> saved_values{};
+ std::array<std::size_t, NumRegisters> loop_tops{};
+
+ bool DecodeNextOpcode(CheatVmOpcode& out);
+ void SkipConditionalBlock();
+ void ResetState();
+
+ // For implementing the DebugLog opcode.
+ void DebugLog(u32 log_id, u64 value);
+
+ void LogOpcode(const CheatVmOpcode& opcode);
+
+ static u64 GetVmInt(VmInt value, u32 bit_width);
+ static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
+ MemoryAccessType mem_type, u64 rel_address);
+};
+
+}; // 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 @@
#include <algorithm>
#include <chrono>
+#include <iterator>
#include <mutex>
+#include <numeric>
+#include <sstream>
#include <thread>
+#include <fmt/chrono.h>
+#include <fmt/format.h>
+#include "common/file_util.h"
#include "common/math_util.h"
#include "core/perf_stats.h"
#include "core/settings.h"
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
using std::chrono::duration_cast;
using std::chrono::microseconds;
+// Purposefully ignore the first five frames, as there's a significant amount of overhead in
+// booting that we shouldn't account for
+constexpr std::size_t IgnoreFrames = 5;
+
namespace Core {
+PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
+
+PerfStats::~PerfStats() {
+ if (!Settings::values.record_frame_times || title_id == 0) {
+ return;
+ }
+
+ const std::time_t t = std::time(nullptr);
+ std::ostringstream stream;
+ std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
+ std::ostream_iterator<double>(stream, "\n"));
+ const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
+ // %F Date format expanded is "%Y-%m-%d"
+ const std::string filename =
+ fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
+ FileUtil::IOFile file(filename, "w");
+ file.WriteString(stream.str());
+}
+
void PerfStats::BeginSystemFrame() {
std::lock_guard lock{object_mutex};
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
std::lock_guard lock{object_mutex};
auto frame_end = Clock::now();
- accumulated_frametime += frame_end - frame_begin;
+ const auto frame_time = frame_end - frame_begin;
+ if (current_index < perf_history.size()) {
+ perf_history[current_index++] =
+ std::chrono::duration<double, std::milli>(frame_time).count();
+ }
+ accumulated_frametime += frame_time;
system_frames += 1;
previous_frame_length = frame_end - previous_frame_end;
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
+double PerfStats::GetMeanFrametime() {
+ std::lock_guard lock{object_mutex};
+
+ if (current_index <= IgnoreFrames) {
+ return 0;
+ }
+ const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
+ perf_history.begin() + current_index, 0);
+ return sum / (current_index - IgnoreFrames);
+}
+
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard lock{object_mutex};
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 @@
#pragma once
+#include <array>
#include <chrono>
+#include <cstddef>
#include <mutex>
#include "common/common_types.h"
@@ -27,6 +29,10 @@ struct PerfStatsResults {
*/
class PerfStats {
public:
+ explicit PerfStats(u64 title_id);
+
+ ~PerfStats();
+
using Clock = std::chrono::high_resolution_clock;
void BeginSystemFrame();
@@ -36,13 +42,26 @@ public:
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
+ * Returns the Arthimetic Mean of all frametime values stored in the performance history.
+ */
+ double GetMeanFrametime();
+
+ /**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
* useful for scaling inputs or outputs moving between the two time domains.
*/
double GetLastFrameTimeScale();
private:
- std::mutex object_mutex;
+ std::mutex object_mutex{};
+
+ /// Title ID for the game that is running. 0 if there is no game running yet
+ u64 title_id{0};
+ /// Current index for writing to the perf_history array
+ std::size_t current_index{0};
+ /// Stores an hour of historical frametime data useful for processing and tracking performance
+ /// regressions with code changes.
+ std::array<double, 216000> perf_history = {};
/// Point when the cumulative counters were reset
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(
SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
}
-void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
- std::optional<u128> user_id) const {
+void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
+ std::optional<u64> process_id, std::optional<u128> user_id) const {
if (!IsReportingEnabled()) {
return;
}
@@ -321,7 +321,11 @@ void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vec
data_out.push_back(Common::HexToString(d));
}
- out["play_report_process_id"] = fmt::format("{:016X}", process_id);
+ if (process_id.has_value()) {
+ out["play_report_process_id"] = fmt::format("{:016X}", *process_id);
+ }
+
+ out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type));
out["play_report_data"] = std::move(data_out);
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:
std::vector<std::vector<u8>> normal_channel,
std::vector<std::vector<u8>> interactive_channel) const;
- void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
- std::optional<u128> user_id = {}) const;
+ enum class PlayReportType {
+ Old,
+ New,
+ System,
+ };
+
+ void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
+ std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
void SaveErrorReport(u64 title_id, ResultCode result,
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/file_util.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/service/hid/hid.h"
@@ -97,8 +98,8 @@ void LogSettings() {
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
- LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
- LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
+ LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
+ LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
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 {
u32 rotation_angle;
};
+enum class NANDTotalSize : u64 {
+ S29_1GB = 0x747C00000ULL,
+};
+
+enum class NANDUserSize : u64 {
+ S26GB = 0x680000000ULL,
+};
+
+enum class NANDSystemSize : u64 {
+ S2_5GB = 0xA0000000,
+};
+
+enum class SDMCSize : u64 {
+ S1GB = 0x40000000,
+ S2GB = 0x80000000,
+ S4GB = 0x100000000ULL,
+ S8GB = 0x200000000ULL,
+ S16GB = 0x400000000ULL,
+ S32GB = 0x800000000ULL,
+ S64GB = 0x1000000000ULL,
+ S128GB = 0x2000000000ULL,
+ S256GB = 0x4000000000ULL,
+ S1TB = 0x10000000000ULL,
+};
+
struct Values {
// System
bool use_docked_mode;
@@ -382,8 +407,13 @@ struct Values {
// Data Storage
bool use_virtual_sd;
- std::string nand_dir;
- std::string sdmc_dir;
+ bool gamecard_inserted;
+ bool gamecard_current_game;
+ std::string gamecard_path;
+ NANDTotalSize nand_total_size;
+ NANDSystemSize nand_system_size;
+ NANDUserSize nand_user_size;
+ SDMCSize sdmc_size;
// Renderer
float resolution_factor;
@@ -409,6 +439,7 @@ struct Values {
float volume;
// Debugging
+ bool record_frame_times;
bool use_gdbstub;
u16 gdbstub_port;
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) {
}
void Fermi2D::HandleSurfaceCopy() {
- LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}",
- static_cast<u32>(regs.operation));
+ LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}",
+ static_cast<u32>(regs.operation));
// TODO(Subv): Only raw copies are implemented.
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() {
// Some games (like Super Mario Odyssey) assume that SRGB is enabled.
regs.framebuffer_srgb = 1;
+ mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
+ mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
+ mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
+ mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
}
#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
// Execute the current macro.
macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters);
+ if (mme_draw.current_mode != MMEDrawMode::Undefined) {
+ FlushMMEInlineDraw();
+ }
}
void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
@@ -416,6 +423,97 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
}
}
+void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
+ if (mme_draw.current_mode == MMEDrawMode::Undefined) {
+ if (mme_draw.gl_begin_consume) {
+ mme_draw.current_mode = expected_mode;
+ mme_draw.current_count = count;
+ mme_draw.instance_count = 1;
+ mme_draw.gl_begin_consume = false;
+ mme_draw.gl_end_count = 0;
+ }
+ return;
+ } else {
+ if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
+ mme_draw.instance_mode && mme_draw.gl_begin_consume) {
+ mme_draw.instance_count++;
+ mme_draw.gl_begin_consume = false;
+ return;
+ } else {
+ FlushMMEInlineDraw();
+ }
+ }
+ // Tail call in case it needs to retry.
+ StepInstance(expected_mode, count);
+}
+
+void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) {
+ const u32 method = method_call.method;
+ if (mme_inline[method]) {
+ regs.reg_array[method] = method_call.argument;
+ if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
+ method == MAXWELL3D_REG_INDEX(index_array.count)) {
+ const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
+ ? MMEDrawMode::Array
+ : MMEDrawMode::Indexed;
+ StepInstance(expected_mode, method_call.argument);
+ } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) {
+ mme_draw.instance_mode =
+ (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0);
+ mme_draw.gl_begin_consume = true;
+ } else {
+ mme_draw.gl_end_count++;
+ }
+ } else {
+ if (mme_draw.current_mode != MMEDrawMode::Undefined) {
+ FlushMMEInlineDraw();
+ }
+ CallMethod(method_call);
+ }
+}
+
+void Maxwell3D::FlushMMEInlineDraw() {
+ LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
+ regs.vertex_buffer.count);
+ ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
+ ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
+
+ auto debug_context = system.GetGPUDebugContext();
+
+ if (debug_context) {
+ debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
+ }
+
+ // Both instance configuration registers can not be set at the same time.
+ ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
+ "Illegal combination of instancing parameters");
+
+ const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
+ if (ShouldExecute()) {
+ rasterizer.DrawMultiBatch(is_indexed);
+ }
+
+ if (debug_context) {
+ debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+ }
+
+ // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
+ // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
+ // it's possible that it is incorrect and that there is some other register used to specify the
+ // drawing mode.
+ if (is_indexed) {
+ regs.index_array.count = 0;
+ } else {
+ regs.vertex_buffer.count = 0;
+ }
+ mme_draw.current_mode = MMEDrawMode::Undefined;
+ mme_draw.current_count = 0;
+ mme_draw.instance_count = 0;
+ mme_draw.instance_mode = false;
+ mme_draw.gl_begin_consume = false;
+ mme_draw.gl_end_count = 0;
+}
+
void Maxwell3D::ProcessMacroUpload(u32 data) {
ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
"upload_address exceeded macro_memory size!");
@@ -541,7 +639,7 @@ void Maxwell3D::ProcessSyncPoint() {
}
void Maxwell3D::DrawArrays() {
- LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
+ LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
@@ -564,7 +662,9 @@ void Maxwell3D::DrawArrays() {
}
const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
- rasterizer.AccelerateDrawBatch(is_indexed);
+ if (ShouldExecute()) {
+ rasterizer.DrawBatch(is_indexed);
+ }
if (debug_context) {
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:
INSERT_PADDING_WORDS(0x21);
u32 vb_element_base;
+ u32 vb_base_instance;
- INSERT_PADDING_WORDS(0x36);
+ INSERT_PADDING_WORDS(0x35);
union {
BitField<0, 1, u32> c0;
@@ -1238,6 +1239,11 @@ public:
/// Write the value to the register identified by method.
void CallMethod(const GPU::MethodCall& method_call);
+ /// Write the value to the register identified by method.
+ void CallMethodFromMME(const GPU::MethodCall& method_call);
+
+ void FlushMMEInlineDraw();
+
/// Given a Texture Handle, returns the TSC and TIC entries.
Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
std::size_t offset) const;
@@ -1263,6 +1269,21 @@ public:
return execute_on;
}
+ enum class MMEDrawMode : u32 {
+ Undefined,
+ Array,
+ Indexed,
+ };
+
+ struct MMEDrawState {
+ MMEDrawMode current_mode{MMEDrawMode::Undefined};
+ u32 current_count{};
+ u32 instance_count{};
+ bool instance_mode{};
+ bool gl_begin_consume{};
+ u32 gl_end_count{};
+ } mme_draw;
+
private:
void InitializeRegisterDefaults();
@@ -1275,6 +1296,8 @@ private:
/// Start offsets of each macro in macro_memory
std::array<u32, 0x80> macro_positions = {};
+ std::array<bool, Regs::NUM_REGS> mme_inline{};
+
/// Memory for macro code
MacroMemory macro_memory;
@@ -1346,6 +1369,9 @@ private:
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
+
+ // Handles a instance drawcall from MME
+ void StepInstance(MMEDrawMode expected_mode, u32 count);
};
#define ASSERT_REG_POSITION(field_name, position) \
@@ -1402,6 +1428,7 @@ ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
+ASSERT_REG_POSITION(vb_base_instance, 0x50E);
ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_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 {
} isetp;
union {
+ BitField<48, 1, u64> is_signed;
+ BitField<49, 3, PredCondition> cond;
+ } icmp;
+
+ union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
BitField<12, 3, u64> pred12;
@@ -1646,6 +1651,10 @@ public:
SEL_C,
SEL_R,
SEL_IMM,
+ ICMP_RC,
+ ICMP_R,
+ ICMP_CR,
+ ICMP_IMM,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
@@ -1912,6 +1921,10 @@ private:
INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
+ INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"),
+ INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
+ INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
+ INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
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) {
case RenderTargetFormat::RGBA16_UINT:
case RenderTargetFormat::RGBA16_UNORM:
case RenderTargetFormat::RGBA16_FLOAT:
+ case RenderTargetFormat::RGBX16_FLOAT:
case RenderTargetFormat::RG32_FLOAT:
case RenderTargetFormat::RG32_UINT:
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 {
RGBA16_FLOAT = 0xCA,
RG32_FLOAT = 0xCB,
RG32_UINT = 0xCD,
+ RGBX16_FLOAT = 0xCE,
BGRA8_UNORM = 0xCF,
BGRA8_SRGB = 0xD0,
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) {
}
void MacroInterpreter::Send(u32 value) {
- maxwell3d.CallMethod({method_address.address, value});
+ maxwell3d.CallMethodFromMME({method_address.address, value});
// Increment the method address by the method increment.
method_address.address.Assign(method_address.address.Value() +
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 = {
MortonCopy<true, PixelFormat::RG8U>,
MortonCopy<true, PixelFormat::RG8S>,
MortonCopy<true, PixelFormat::RG32UI>,
+ MortonCopy<true, PixelFormat::RGBX16F>,
MortonCopy<true, PixelFormat::R32UI>,
MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
@@ -151,6 +152,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
MortonCopy<false, PixelFormat::RG8U>,
MortonCopy<false, PixelFormat::RG8S>,
MortonCopy<false, PixelFormat::RG32UI>,
+ MortonCopy<false, PixelFormat::RGBX16F>,
MortonCopy<false, PixelFormat::R32UI>,
nullptr,
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:
virtual ~RasterizerInterface() {}
/// Draw the current batch of vertex arrays
- virtual void DrawArrays() = 0;
+ virtual bool DrawBatch(bool is_indexed) = 0;
+
+ /// Draw the current batch of multiple instances of vertex arrays
+ virtual bool DrawMultiBatch(bool is_indexed) = 0;
/// Clear the current framebuffer
virtual void Clear() = 0;
@@ -69,10 +72,6 @@ public:
return false;
}
- virtual bool AccelerateDrawBatch(bool is_indexed) {
- return false;
- }
-
/// Increase/decrease the number of object in pages touching the specified region
virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
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
local_state.draw.draw_framebuffer = framebuffer.handle;
local_state.ApplyFramebufferState();
- if (key.is_single_buffer) {
- if (key.color_attachments[0] != GL_NONE && key.colors[0]) {
- key.colors[0]->Attach(key.color_attachments[0], GL_DRAW_FRAMEBUFFER);
- glDrawBuffer(key.color_attachments[0]);
- } else {
- glDrawBuffer(GL_NONE);
- }
- } else {
- for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
- if (key.colors[index]) {
- key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
- GL_DRAW_FRAMEBUFFER);
- }
+ for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
+ if (key.colors[index]) {
+ key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
+ GL_DRAW_FRAMEBUFFER);
}
+ }
+ if (key.colors_count) {
glDrawBuffers(key.colors_count, key.color_attachments.data());
+ } else {
+ glDrawBuffer(GL_NONE);
}
if (key.zeta) {
@@ -67,9 +62,9 @@ std::size_t FramebufferCacheKey::Hash() const {
}
bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const {
- return std::tie(is_single_buffer, stencil_enable, colors_count, color_attachments, colors,
- zeta) == std::tie(rhs.is_single_buffer, rhs.stencil_enable, rhs.colors_count,
- rhs.color_attachments, rhs.colors, rhs.zeta);
+ return std::tie(stencil_enable, colors_count, color_attachments, colors, zeta) ==
+ std::tie(rhs.stencil_enable, rhs.colors_count, rhs.color_attachments, rhs.colors,
+ rhs.zeta);
}
} // 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 @@
namespace OpenGL {
struct alignas(sizeof(u64)) FramebufferCacheKey {
- bool is_single_buffer = false;
bool stencil_enable = false;
u16 colors_count = 0;
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));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100));
-struct DrawParameters {
- GLenum primitive_mode;
- GLsizei count;
- GLint current_instance;
- bool use_indexed;
-
- GLint vertex_first;
-
- GLenum index_format;
- GLint base_vertex;
- GLintptr index_buffer_offset;
-
- void DispatchDraw() const {
- if (use_indexed) {
- const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
- if (current_instance > 0) {
- glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
- index_buffer_ptr, 1, base_vertex,
- current_instance);
- } else {
- glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
- base_vertex);
- }
- } else {
- if (current_instance > 0) {
- glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1,
- current_instance);
- } else {
- glDrawArrays(primitive_mode, vertex_first, count);
- }
- }
- }
-};
-
static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
const GLShader::ConstBufferEntry& entry) {
if (!entry.IsIndirect()) {
@@ -270,29 +236,6 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
return offset;
}
-DrawParameters RasterizerOpenGL::SetupDraw(GLintptr index_buffer_offset) {
- const auto& gpu = system.GPU().Maxwell3D();
- const auto& regs = gpu.regs;
- const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
-
- DrawParameters params{};
- params.current_instance = gpu.state.current_instance;
-
- params.use_indexed = is_indexed;
- params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
-
- if (is_indexed) {
- params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
- params.count = regs.index_array.count;
- params.index_buffer_offset = index_buffer_offset;
- params.base_vertex = static_cast<GLint>(regs.vb_element_base);
- } else {
- params.count = regs.vertex_buffer.count;
- params.vertex_first = regs.vertex_buffer.first;
- }
- return params;
-}
-
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
MICROPROFILE_SCOPE(OpenGL_Shader);
auto& gpu = system.GPU().Maxwell3D();
@@ -399,12 +342,6 @@ std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
}
-bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
- accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
- DrawArrays();
- return true;
-}
-
template <typename Map, typename Interval>
static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
@@ -445,99 +382,51 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
shader_cache.LoadDiskCache(stop_loading, callback);
}
-std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
- OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
- std::optional<std::size_t> single_color_target) {
+void RasterizerOpenGL::ConfigureFramebuffers() {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
auto& gpu = system.GPU().Maxwell3D();
- const auto& regs = gpu.regs;
-
- const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
- single_color_target};
- if (fb_config_state == current_framebuffer_config_state && !gpu.dirty.render_settings) {
- // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
- // single color targets). This is done because the guest registers may not change but the
- // host framebuffer may contain different attachments
- return current_depth_stencil_usage;
+ if (!gpu.dirty.render_settings) {
+ return;
}
gpu.dirty.render_settings = false;
- current_framebuffer_config_state = fb_config_state;
texture_cache.GuardRenderTargets(true);
- View depth_surface{};
- if (using_depth_fb) {
- depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents);
- } else {
- texture_cache.SetEmptyDepthBuffer();
- }
+ View depth_surface = texture_cache.GetDepthBufferSurface(true);
+ const auto& regs = gpu.regs;
+ state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
// Bind the framebuffer surfaces
- current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
-
FramebufferCacheKey fbkey;
+ for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
+ View color_surface{texture_cache.GetColorBufferSurface(index, true)};
- if (using_color_fb) {
- if (single_color_target) {
- // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
- View color_surface{
- texture_cache.GetColorBufferSurface(*single_color_target, preserve_contents)};
-
- if (color_surface) {
- // Assume that a surface will be written to if it is used as a framebuffer, even if
- // the shader doesn't actually write to it.
- texture_cache.MarkColorBufferInUse(*single_color_target);
- }
-
- fbkey.is_single_buffer = true;
- fbkey.color_attachments[0] =
- GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target);
- fbkey.colors[0] = color_surface;
- for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
- if (index != *single_color_target) {
- texture_cache.SetEmptyColorBuffer(index);
- }
- }
- } else {
- // Multiple color attachments are enabled
- for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
- View color_surface{texture_cache.GetColorBufferSurface(index, preserve_contents)};
-
- if (color_surface) {
- // Assume that a surface will be written to if it is used as a framebuffer, even
- // if the shader doesn't actually write to it.
- texture_cache.MarkColorBufferInUse(index);
- }
-
- fbkey.color_attachments[index] =
- GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
- fbkey.colors[index] = color_surface;
- }
- fbkey.is_single_buffer = false;
- fbkey.colors_count = regs.rt_control.count;
+ if (color_surface) {
+ // Assume that a surface will be written to if it is used as a framebuffer, even
+ // if the shader doesn't actually write to it.
+ texture_cache.MarkColorBufferInUse(index);
}
- } else {
- // No color attachments are enabled - leave them as zero
- fbkey.is_single_buffer = true;
+
+ fbkey.color_attachments[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
+ fbkey.colors[index] = std::move(color_surface);
}
+ fbkey.colors_count = regs.rt_control.count;
if (depth_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it.
texture_cache.MarkDepthBufferInUse();
- fbkey.zeta = depth_surface;
fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
+ fbkey.zeta = std::move(depth_surface);
}
texture_cache.GuardRenderTargets(false);
- current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey);
- SyncViewport(current_state);
-
- return current_depth_stencil_usage = {static_cast<bool>(depth_surface), fbkey.stencil_enable};
+ state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey);
+ SyncViewport(state);
}
void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
@@ -688,17 +577,9 @@ void RasterizerOpenGL::Clear() {
}
}
-void RasterizerOpenGL::DrawArrays() {
- if (accelerate_draw == AccelDraw::Disabled)
- return;
-
- MICROPROFILE_SCOPE(OpenGL_Drawing);
+void RasterizerOpenGL::DrawPrelude() {
auto& gpu = system.GPU().Maxwell3D();
- if (!gpu.ShouldExecute()) {
- return;
- }
-
SyncColorMask();
SyncFragmentColorClampState();
SyncMultiSampleState();
@@ -743,10 +624,7 @@ void RasterizerOpenGL::DrawArrays() {
// Upload vertex and index data.
SetupVertexBuffer(vao);
SetupVertexInstances(vao);
- const GLintptr index_buffer_offset = SetupIndexBuffer();
-
- // Setup draw parameters. It will automatically choose what glDraw* method to use.
- const DrawParameters params = SetupDraw(index_buffer_offset);
+ index_buffer_offset = SetupIndexBuffer();
// Prepare packed bindings.
bind_ubo_pushbuffer.Setup(0);
@@ -754,10 +632,11 @@ void RasterizerOpenGL::DrawArrays() {
// Setup shaders and their used resources.
texture_cache.GuardSamplers(true);
- SetupShaders(params.primitive_mode);
+ const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
+ SetupShaders(primitive_mode);
texture_cache.GuardSamplers(false);
- ConfigureFramebuffers(state);
+ ConfigureFramebuffers();
// Signal the buffer cache that we are not going to upload more things.
const bool invalidate = buffer_cache.Unmap();
@@ -778,11 +657,107 @@ void RasterizerOpenGL::DrawArrays() {
if (texture_cache.TextureBarrier()) {
glTextureBarrier();
}
+}
+
+struct DrawParams {
+ bool is_indexed{};
+ bool is_instanced{};
+ GLenum primitive_mode{};
+ GLint count{};
+ GLint base_vertex{};
+
+ // Indexed settings
+ GLenum index_format{};
+ GLintptr index_buffer_offset{};
+
+ // Instanced setting
+ GLint num_instances{};
+ GLint base_instance{};
+
+ void DispatchDraw() {
+ if (is_indexed) {
+ const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
+ if (is_instanced) {
+ glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
+ index_buffer_ptr, num_instances,
+ base_vertex, base_instance);
+ } else {
+ glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
+ base_vertex);
+ }
+ } else {
+ if (is_instanced) {
+ glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
+ base_instance);
+ } else {
+ glDrawArrays(primitive_mode, base_vertex, count);
+ }
+ }
+ }
+};
- params.DispatchDraw();
+bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
+ accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
+ MICROPROFILE_SCOPE(OpenGL_Drawing);
+
+ DrawPrelude();
+
+ auto& maxwell3d = system.GPU().Maxwell3D();
+ const auto& regs = maxwell3d.regs;
+ const auto current_instance = maxwell3d.state.current_instance;
+ DrawParams draw_call{};
+ draw_call.is_indexed = is_indexed;
+ draw_call.num_instances = static_cast<GLint>(1);
+ draw_call.base_instance = static_cast<GLint>(current_instance);
+ draw_call.is_instanced = current_instance > 0;
+ draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
+ if (draw_call.is_indexed) {
+ draw_call.count = static_cast<GLint>(regs.index_array.count);
+ draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
+ draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
+ draw_call.index_buffer_offset = index_buffer_offset;
+ } else {
+ draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
+ draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
+ }
+ draw_call.DispatchDraw();
+
+ maxwell3d.dirty.memory_general = false;
accelerate_draw = AccelDraw::Disabled;
- gpu.dirty.memory_general = false;
+ return true;
+}
+
+bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
+ accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
+
+ MICROPROFILE_SCOPE(OpenGL_Drawing);
+
+ DrawPrelude();
+
+ auto& maxwell3d = system.GPU().Maxwell3D();
+ const auto& regs = maxwell3d.regs;
+ const auto& draw_setup = maxwell3d.mme_draw;
+ DrawParams draw_call{};
+ draw_call.is_indexed = is_indexed;
+ draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
+ draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
+ draw_call.is_instanced = draw_setup.instance_count > 1;
+ draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
+ if (draw_call.is_indexed) {
+ draw_call.count = static_cast<GLint>(regs.index_array.count);
+ draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
+ draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
+ draw_call.index_buffer_offset = index_buffer_offset;
+ } else {
+ draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
+ draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
+ }
+ draw_call.DispatchDraw();
+
+ maxwell3d.dirty.memory_general = false;
+ accelerate_draw = AccelDraw::Disabled;
+ return true;
}
void 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:
ScreenInfo& info);
~RasterizerOpenGL() override;
- void DrawArrays() override;
+ bool DrawBatch(bool is_indexed) override;
+ bool DrawMultiBatch(bool is_indexed) override;
void Clear() override;
void DispatchCompute(GPUVAddr code_addr) override;
void FlushAll() override;
@@ -71,45 +72,13 @@ public:
const Tegra::Engines::Fermi2D::Config& copy_config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride) override;
- bool AccelerateDrawBatch(bool is_indexed) override;
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
void LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
private:
- struct FramebufferConfigState {
- bool using_color_fb{};
- bool using_depth_fb{};
- bool preserve_contents{};
- std::optional<std::size_t> single_color_target;
-
- bool operator==(const FramebufferConfigState& rhs) const {
- return std::tie(using_color_fb, using_depth_fb, preserve_contents,
- single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb,
- rhs.preserve_contents,
- rhs.single_color_target);
- }
- bool operator!=(const FramebufferConfigState& rhs) const {
- return !operator==(rhs);
- }
- };
-
- /**
- * Configures the color and depth framebuffer states.
- *
- * @param current_state The current OpenGL state.
- * @param using_color_fb If true, configure color framebuffers.
- * @param using_depth_fb If true, configure the depth/stencil framebuffer.
- * @param preserve_contents If true, tries to preserve data from a previously used
- * framebuffer.
- * @param single_color_target Specifies if a single color buffer target should be used.
- *
- * @returns If depth (first) or stencil (second) are being stored in the bound zeta texture
- * (requires using_depth_fb to be true)
- */
- std::pair<bool, bool> ConfigureFramebuffers(
- OpenGLState& current_state, bool using_color_fb = true, bool using_depth_fb = true,
- bool preserve_contents = true, std::optional<std::size_t> single_color_target = {});
+ /// Configures the color and depth framebuffer states.
+ void ConfigureFramebuffers();
void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
bool using_depth_fb, bool using_stencil_fb);
@@ -136,6 +105,9 @@ private:
void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
std::size_t size);
+ /// Syncs all the state, shaders, render targets and textures setting before a draw call.
+ void DrawPrelude();
+
/// Configures the current textures to use for the draw command. Returns shaders texture buffer
/// usage.
TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
@@ -228,9 +200,6 @@ private:
OGLVertexArray>
vertex_array_cache;
- FramebufferConfigState current_framebuffer_config_state;
- std::pair<bool, bool> current_depth_stencil_usage{};
-
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLBufferCache buffer_cache;
@@ -250,7 +219,7 @@ private:
GLintptr SetupIndexBuffer();
- DrawParameters SetupDraw(GLintptr index_buffer_offset);
+ GLintptr index_buffer_offset;
void SetupShaders(GLenum primitive_mode);
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:
code.AddLine("float gl_PointSize;");
}
+ if (ir.UsesInstanceId()) {
+ code.AddLine("int gl_InstanceID;");
+ }
+
+ if (ir.UsesVertexId()) {
+ code.AddLine("int gl_VertexID;");
+ }
+
--code.scope;
code.AddLine("}};");
code.AddNewLine();
@@ -951,7 +959,7 @@ private:
switch (element) {
case 2:
// Config pack's first value is instance_id.
- return {"config_pack[0]", Type::Uint};
+ return {"gl_InstanceID", Type::Int};
case 3:
return {"gl_VertexID", Type::Int};
}
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
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
{GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
+ {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBX16F
{GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
{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,
case Tegra::Texture::TextureMipmapFilter::None:
return GL_LINEAR;
case Tegra::Texture::TextureMipmapFilter::Nearest:
- return GL_NEAREST_MIPMAP_LINEAR;
+ return GL_LINEAR_MIPMAP_NEAREST;
case Tegra::Texture::TextureMipmapFilter::Linear:
return GL_LINEAR_MIPMAP_LINEAR;
}
@@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
case Tegra::Texture::TextureMipmapFilter::Nearest:
return GL_NEAREST_MIPMAP_NEAREST;
case Tegra::Texture::TextureMipmapFilter::Linear:
- return GL_LINEAR_MIPMAP_NEAREST;
+ return GL_NEAREST_MIPMAP_LINEAR;
}
}
}
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
{vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U
{vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S
{vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI
+ {vk::Format::eUndefined, ComponentType::Invalid, false}, // RGBX16F
{vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI
{vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8
{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) {
SetRegister(bb, instr.gpr0, value);
break;
}
+ case OpCode::Id::ICMP_CR:
+ case OpCode::Id::ICMP_R:
+ case OpCode::Id::ICMP_RC:
+ case OpCode::Id::ICMP_IMM: {
+ const Node zero = Immediate(0);
+
+ const auto [op_b, test] = [&]() -> std::pair<Node, Node> {
+ switch (opcode->get().GetId()) {
+ case OpCode::Id::ICMP_CR:
+ return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset),
+ GetRegister(instr.gpr39)};
+ case OpCode::Id::ICMP_R:
+ return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
+ case OpCode::Id::ICMP_RC:
+ return {GetRegister(instr.gpr39),
+ GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
+ case OpCode::Id::ICMP_IMM:
+ return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
+ default:
+ UNREACHABLE();
+ return {zero, zero};
+ }
+ }();
+ const Node op_a = GetRegister(instr.gpr8);
+ const Node comparison =
+ GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero);
+ SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_a, op_b));
+ break;
+ }
case OpCode::Id::LOP_C:
case OpCode::Id::LOP_R:
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
break;
}
}
+ if (index == Attribute::Index::TessCoordInstanceIDVertexID) {
+ switch (element) {
+ case 2:
+ uses_instance_id = true;
+ break;
+ case 3:
+ uses_vertex_id = true;
+ break;
+ default:
+ break;
+ }
+ }
if (index == Attribute::Index::ClipDistances0123 ||
index == Attribute::Index::ClipDistances4567) {
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:
return uses_point_size;
}
+ bool UsesInstanceId() const {
+ return uses_instance_id;
+ }
+
+ bool UsesVertexId() const {
+ return uses_vertex_id;
+ }
+
bool HasPhysicalAttributes() const {
return uses_physical_attributes;
}
@@ -370,6 +378,8 @@ private:
bool uses_viewport_index{};
bool uses_point_size{};
bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
+ bool uses_instance_id{};
+ bool uses_vertex_id{};
Tegra::Shader::Header header;
};
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)
return PixelFormat::R32UI;
case Tegra::RenderTargetFormat::RG32_UINT:
return PixelFormat::RG32UI;
+ case Tegra::RenderTargetFormat::RGBX16_FLOAT:
+ return PixelFormat::RGBX16F;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -415,6 +417,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
case Tegra::RenderTargetFormat::RG8_SNORM:
return ComponentType::SNorm;
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
+ case Tegra::RenderTargetFormat::RGBX16_FLOAT:
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
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 {
RG8U = 39,
RG8S = 40,
RG32UI = 41,
- R32UI = 42,
- ASTC_2D_8X8 = 43,
- ASTC_2D_8X5 = 44,
- ASTC_2D_5X4 = 45,
- BGRA8_SRGB = 46,
- DXT1_SRGB = 47,
- DXT23_SRGB = 48,
- DXT45_SRGB = 49,
- BC7U_SRGB = 50,
- ASTC_2D_4X4_SRGB = 51,
- ASTC_2D_8X8_SRGB = 52,
- ASTC_2D_8X5_SRGB = 53,
- ASTC_2D_5X4_SRGB = 54,
- ASTC_2D_5X5 = 55,
- ASTC_2D_5X5_SRGB = 56,
- ASTC_2D_10X8 = 57,
- ASTC_2D_10X8_SRGB = 58,
+ RGBX16F = 42,
+ R32UI = 43,
+ ASTC_2D_8X8 = 44,
+ ASTC_2D_8X5 = 45,
+ ASTC_2D_5X4 = 46,
+ BGRA8_SRGB = 47,
+ DXT1_SRGB = 48,
+ DXT23_SRGB = 49,
+ DXT45_SRGB = 50,
+ BC7U_SRGB = 51,
+ ASTC_2D_4X4_SRGB = 52,
+ ASTC_2D_8X8_SRGB = 53,
+ ASTC_2D_8X5_SRGB = 54,
+ ASTC_2D_5X4_SRGB = 55,
+ ASTC_2D_5X5 = 56,
+ ASTC_2D_5X5_SRGB = 57,
+ ASTC_2D_10X8 = 58,
+ ASTC_2D_10X8_SRGB = 59,
MaxColorFormat,
// Depth formats
- Z32F = 59,
- Z16 = 60,
+ Z32F = 60,
+ Z16 = 61,
MaxDepthFormat,
// DepthStencil formats
- Z24S8 = 61,
- S8Z24 = 62,
- Z32FS8 = 63,
+ Z24S8 = 62,
+ S8Z24 = 63,
+ Z32FS8 = 64,
MaxDepthStencilFormat,
@@ -166,6 +167,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
0, // RG8U
0, // RG8S
0, // RG32UI
+ 0, // RGBX16F
0, // R32UI
2, // ASTC_2D_8X8
2, // ASTC_2D_8X5
@@ -249,6 +251,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
1, // RG8U
1, // RG8S
1, // RG32UI
+ 1, // RGBX16F
1, // R32UI
8, // ASTC_2D_8X8
8, // ASTC_2D_8X5
@@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
1, // RG8U
1, // RG8S
1, // RG32UI
+ 1, // RGBX16F
1, // R32UI
8, // ASTC_2D_8X8
5, // ASTC_2D_8X5
@@ -399,6 +403,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
16, // RG8U
16, // RG8S
64, // RG32UI
+ 64, // RGBX16F
32, // R32UI
128, // ASTC_2D_8X8
128, // ASTC_2D_8X5
@@ -489,6 +494,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
SurfaceCompression::None, // RG8U
SurfaceCompression::None, // RG8S
SurfaceCompression::None, // RG32UI
+ SurfaceCompression::None, // RGBX16F
SurfaceCompression::None, // R32UI
SurfaceCompression::Converted, // ASTC_2D_8X8
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
configuration/configure_debug.ui
configuration/configure_dialog.cpp
configuration/configure_dialog.h
+ configuration/configure_filesystem.cpp
+ configuration/configure_filesystem.h
+ configuration/configure_filesystem.ui
configuration/configure_gamelist.cpp
configuration/configure_gamelist.h
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() {
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
.toString()
.toStdString());
+ FileUtil::GetUserPath(
+ FileUtil::UserPath::LoadDir,
+ qt_config
+ ->value(QStringLiteral("load_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
+ .toString()
+ .toStdString());
+ FileUtil::GetUserPath(
+ FileUtil::UserPath::DumpDir,
+ qt_config
+ ->value(QStringLiteral("dump_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
+ .toString()
+ .toStdString());
+ FileUtil::GetUserPath(
+ FileUtil::UserPath::CacheDir,
+ qt_config
+ ->value(QStringLiteral("cache_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
+ .toString()
+ .toStdString());
+ Settings::values.gamecard_inserted =
+ ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
+ Settings::values.gamecard_current_game =
+ ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
+ Settings::values.gamecard_path =
+ ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
+ Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
+ ReadSetting(QStringLiteral("nand_total_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
+ .toULongLong());
+ Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
+ ReadSetting(QStringLiteral("nand_user_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
+ .toULongLong());
+ Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+ ReadSetting(QStringLiteral("nand_system_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
+ .toULongLong());
+ Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
+ ReadSetting(QStringLiteral("sdmc_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
+ .toULongLong());
qt_config->endGroup();
}
@@ -466,6 +509,9 @@ void Config::ReadDataStorageValues() {
void Config::ReadDebuggingValues() {
qt_config->beginGroup(QStringLiteral("Debugging"));
+ // Intentionally not using the QT default setting as this is intended to be changed in the ini
+ Settings::values.record_frame_times =
+ qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
Settings::values.program_args =
@@ -872,13 +918,40 @@ void Config::SaveDataStorageValues() {
WriteSetting(QStringLiteral("sdmc_directory"),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
-
+ WriteSetting(QStringLiteral("load_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+ WriteSetting(QStringLiteral("dump_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+ WriteSetting(QStringLiteral("cache_directory"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+ WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
+ WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
+ false);
+ WriteSetting(QStringLiteral("gamecard_path"),
+ QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
+ WriteSetting(QStringLiteral("nand_total_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
+ WriteSetting(QStringLiteral("nand_user_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
+ WriteSetting(QStringLiteral("nand_system_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
+ WriteSetting(QStringLiteral("sdmc_size"),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
+ QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
qt_config->endGroup();
}
void Config::SaveDebuggingValues() {
qt_config->beginGroup(QStringLiteral("Debugging"));
+ // Intentionally not using the QT default setting as this is intended to be changed in the ini
+ qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
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 @@
<string>Profiles</string>
</attribute>
</widget>
+ <widget class="ConfigureFilesystem" name="filesystemTab">
+ <attribute name="title">
+ <string>Filesystem</string>
+ </attribute>
+ </widget>
<widget class="ConfigureInputSimple" name="inputTab">
<attribute name="title">
<string>Input</string>
@@ -126,6 +131,12 @@
<container>1</container>
</customwidget>
<customwidget>
+ <class>ConfigureFilesystem</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_filesystem.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
<class>ConfigureAudio</class>
<extends>QWidget</extends>
<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() {
ui->toggle_console->setChecked(UISettings::values.show_console);
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
- ui->dump_exefs->setChecked(Settings::values.dump_exefs);
- ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
ui->reporting_services->setChecked(Settings::values.reporting_services);
ui->quest_flag->setChecked(Settings::values.quest_flag);
}
@@ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() {
UISettings::values.show_console = ui->toggle_console->isChecked();
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
- Settings::values.dump_exefs = ui->dump_exefs->isChecked();
- Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
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 @@
</item>
</layout>
</item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_3">
- <property name="title">
- <string>Homebrew</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_4">
- <item>
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Arguments String</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLineEdit" name="homebrew_args_edit"/>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="groupBox_4">
- <property name="title">
- <string>Dump</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
- <widget class="QCheckBox" name="dump_decompressed_nso">
- <property name="whatsThis">
- <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
- </property>
- <property name="text">
- <string>Dump Decompressed NSOs</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="dump_exefs">
- <property name="whatsThis">
- <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
- </property>
- <property name="text">
- <string>Dump ExeFS</string>
- </property>
- </widget>
- </item>
<item>
<widget class="QCheckBox" name="reporting_services">
<property name="text">
@@ -163,7 +111,7 @@
</widget>
</item>
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QLabel" name="label">
<property name="font">
<font>
<italic>true</italic>
@@ -197,10 +145,36 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Homebrew</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Arguments String</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="homebrew_args_edit"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
<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() {
ui->gameListTab->ApplyConfiguration();
ui->systemTab->ApplyConfiguration();
ui->profileManagerTab->ApplyConfiguration();
+ ui->filesystemTab->applyConfiguration();
ui->inputTab->ApplyConfiguration();
ui->hotkeysTab->ApplyConfiguration(registry);
ui->graphicsTab->ApplyConfiguration();
@@ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
- {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}},
+ {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}},
{tr("Graphics"), {ui->graphicsTab}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
};
@@ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->debugTab, tr("Debug")},
{ui->webTab, tr("Web")},
{ui->gameListTab, tr("Game List")},
+ {ui->filesystemTab, tr("Filesystem")},
};
[[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 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "core/settings.h"
+#include "ui_configure_filesystem.h"
+#include "yuzu/configuration/configure_filesystem.h"
+#include "yuzu/uisettings.h"
+
+namespace {
+
+template <typename T>
+void SetComboBoxFromData(QComboBox* combo_box, T data) {
+ const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
+ if (index >= combo_box->count() || index < 0)
+ return;
+
+ combo_box->setCurrentIndex(index);
+}
+
+} // Anonymous namespace
+
+ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
+ ui->setupUi(this);
+ this->setConfiguration();
+
+ connect(ui->nand_directory_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); });
+ connect(ui->sdmc_directory_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); });
+ connect(ui->gamecard_path_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); });
+ connect(ui->dump_path_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); });
+ connect(ui->load_path_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); });
+ connect(ui->cache_directory_button, &QToolButton::pressed, this,
+ [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); });
+
+ connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
+ &ConfigureFilesystem::ResetMetadata);
+
+ connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this,
+ &ConfigureFilesystem::UpdateEnabledControls);
+ connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this,
+ &ConfigureFilesystem::UpdateEnabledControls);
+}
+
+ConfigureFilesystem::~ConfigureFilesystem() = default;
+
+void ConfigureFilesystem::setConfiguration() {
+ ui->nand_directory_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
+ ui->sdmc_directory_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+ ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
+ ui->dump_path_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+ ui->load_path_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+ ui->cache_directory_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+
+ ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
+ ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
+ ui->dump_exefs->setChecked(Settings::values.dump_exefs);
+ ui->dump_nso->setChecked(Settings::values.dump_nso);
+
+ ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
+
+ SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
+ SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
+ SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
+ SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
+
+ UpdateEnabledControls();
+}
+
+void ConfigureFilesystem::applyConfiguration() {
+ FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
+ ui->nand_directory_edit->text().toStdString());
+ FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
+ ui->sdmc_directory_edit->text().toStdString());
+ FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString());
+ FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString());
+ FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
+ ui->cache_directory_edit->text().toStdString());
+ Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
+
+ Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
+ Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
+ Settings::values.dump_exefs = ui->dump_exefs->isChecked();
+ Settings::values.dump_nso = ui->dump_nso->isChecked();
+
+ UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
+
+ Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
+ ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
+ Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+ ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
+ Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
+ ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
+ Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
+ ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
+}
+
+void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
+ QString caption;
+
+ switch (target) {
+ case DirectoryTarget::NAND:
+ caption = tr("Select Emulated NAND Directory...");
+ break;
+ case DirectoryTarget::SD:
+ caption = tr("Select Emulated SD Directory...");
+ break;
+ case DirectoryTarget::Gamecard:
+ caption = tr("Select Gamecard Path...");
+ break;
+ case DirectoryTarget::Dump:
+ caption = tr("Select Dump Directory...");
+ break;
+ case DirectoryTarget::Load:
+ caption = tr("Select Mod Load Directory...");
+ break;
+ case DirectoryTarget::Cache:
+ caption = tr("Select Cache Directory...");
+ break;
+ }
+
+ QString str;
+ if (target == DirectoryTarget::Gamecard) {
+ str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
+ QStringLiteral("NX Gamecard;*.xci"));
+ } else {
+ str = QFileDialog::getExistingDirectory(this, caption, edit->text());
+ }
+
+ if (str.isEmpty())
+ return;
+
+ edit->setText(str);
+}
+
+void ConfigureFilesystem::ResetMetadata() {
+ if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
+ "game_list")) {
+ QMessageBox::information(this, tr("Reset Metadata Cache"),
+ tr("The metadata cache is already empty."));
+ } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
+ DIR_SEP + "game_list")) {
+ QMessageBox::information(this, tr("Reset Metadata Cache"),
+ tr("The operation completed successfully."));
+ UISettings::values.is_game_list_reload_pending.exchange(true);
+ } else {
+ QMessageBox::warning(
+ this, tr("Reset Metadata Cache"),
+ tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
+ }
+}
+
+void ConfigureFilesystem::UpdateEnabledControls() {
+ ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked());
+ ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() &&
+ !ui->gamecard_current_game->isChecked());
+ ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() &&
+ !ui->gamecard_current_game->isChecked());
+}
+
+void ConfigureFilesystem::retranslateUi() {
+ ui->retranslateUi(this);
+}
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 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+class QLineEdit;
+
+namespace Ui {
+class ConfigureFilesystem;
+}
+
+class ConfigureFilesystem : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureFilesystem(QWidget* parent = nullptr);
+ ~ConfigureFilesystem() override;
+
+ void applyConfiguration();
+ void retranslateUi();
+
+private:
+ void setConfiguration();
+
+ enum class DirectoryTarget {
+ NAND,
+ SD,
+ Gamecard,
+ Dump,
+ Load,
+ Cache,
+ };
+
+ void SetDirectory(DirectoryTarget target, QLineEdit* edit);
+ void ResetMetadata();
+ void UpdateEnabledControls();
+
+ std::unique_ptr<Ui::ConfigureFilesystem> ui;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureFilesystem</class>
+ <widget class="QWidget" name="ConfigureFilesystem">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>453</width>
+ <height>561</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Storage Directories</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>NAND</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QToolButton" name="nand_directory_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLineEdit" name="nand_directory_edit"/>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="sdmc_directory_edit"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>SD Card</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QToolButton" name="sdmc_directory_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Maximum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Gamecard</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Path</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLineEdit" name="gamecard_path_edit"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="gamecard_inserted">
+ <property name="text">
+ <string>Inserted</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="gamecard_current_game">
+ <property name="text">
+ <string>Current Game</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QToolButton" name="gamecard_path_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Storage Sizes</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>SD Card</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>System NAND</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="sysnand_size">
+ <item>
+ <property name="text">
+ <string>2.5 GB</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="sdmc_size">
+ <property name="currentText">
+ <string>32 GB</string>
+ </property>
+ <item>
+ <property name="text">
+ <string>1 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>2 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>4 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>8 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>16 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>32 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>64 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>128 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>256 GB</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>1 TB</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="usrnand_size">
+ <item>
+ <property name="text">
+ <string>26 GB</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>User NAND</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>NAND</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="nand_size">
+ <item>
+ <property name="text">
+ <string>29.1 GB</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Patch Manager</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="load_path_edit"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLineEdit" name="dump_path_edit"/>
+ </item>
+ <item row="0" column="3">
+ <widget class="QToolButton" name="dump_path_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QToolButton" name="load_path_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="4">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="dump_nso">
+ <property name="text">
+ <string>Dump Decompressed NSOs</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="dump_exefs">
+ <property name="text">
+ <string>Dump ExeFS</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Mod Load Root</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Dump Root</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Caching</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Cache Directory</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLineEdit" name="cache_directory_edit"/>
+ </item>
+ <item row="0" column="3">
+ <widget class="QToolButton" name="cache_directory_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="4">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="cache_game_list">
+ <property name="text">
+ <string>Cache Game List Metadata</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="reset_game_list_cache">
+ <property name="text">
+ <string>Reset Metadata Cache</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <QCheckBox>
+#include <QSpinBox>
#include "core/core.h"
#include "core/settings.h"
#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() {
players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
}
+ ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
+ !ui->use_docked_mode->isChecked());
ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
!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 @@
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/scope_acquire_window_context.h"
#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/am/applet_ae.h"
+#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
@@ -54,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QProgressDialog>
#include <QShortcut>
#include <QStatusBar>
+#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include <fmt/format.h>
@@ -66,6 +69,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
+#ifdef ARCHITECTURE_x86_64
+#include "common/x64/cpu_detect.h"
+#endif
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
@@ -79,6 +85,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/file_sys/submission_package.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/service/am/am.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/sm/sm.h"
@@ -205,6 +212,10 @@ GMainWindow::GMainWindow()
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
+#ifdef ARCHITECTURE_x86_64
+ LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
+#endif
+ LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
UpdateWindowTitle();
show();
@@ -213,7 +224,7 @@ GMainWindow::GMainWindow()
std::make_unique<FileSys::ContentProviderUnion>());
Core::System::GetInstance().RegisterContentProvider(
FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
- Service::FileSystem::CreateFactories(*vfs);
+ Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
// Gen keys if necessary
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
@@ -964,11 +975,11 @@ void GMainWindow::BootGame(const QString& filename) {
}
status_bar_update_timer.start(2000);
+ const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+
std::string title_name;
const auto res = Core::System::GetInstance().GetGameName(title_name);
if (res != Loader::ResultStatus::Success) {
- const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
-
const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata();
if (nacp != nullptr)
title_name = nacp->GetApplicationName();
@@ -976,7 +987,7 @@ void GMainWindow::BootGame(const QString& filename) {
if (title_name.empty())
title_name = FileUtil::GetFilename(filename.toStdString());
}
-
+ LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name);
UpdateWindowTitle(QString::fromStdString(title_name));
loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
@@ -1499,15 +1510,19 @@ void GMainWindow::OnMenuInstallToNAND() {
failed();
return;
}
- const auto res =
- Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy);
+ const auto res = Core::System::GetInstance()
+ .GetFileSystemController()
+ .GetUserNANDContents()
+ ->InstallEntry(*nsp, false, qt_raw_copy);
if (res == FileSys::InstallResult::Success) {
success();
} else {
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
- const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- *nsp, true, qt_raw_copy);
+ const auto res2 = Core::System::GetInstance()
+ .GetFileSystemController()
+ .GetUserNANDContents()
+ ->InstallEntry(*nsp, true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {
@@ -1561,19 +1576,28 @@ void GMainWindow::OnMenuInstallToNAND() {
FileSys::InstallResult res;
if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
- res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ res = Core::System::GetInstance()
+ .GetFileSystemController()
+ .GetUserNANDContents()
+ ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
+ qt_raw_copy);
} else {
- res = Service::FileSystem::GetSystemNANDContents()->InstallEntry(
- *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ res = Core::System::GetInstance()
+ .GetFileSystemController()
+ .GetSystemNANDContents()
+ ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
+ qt_raw_copy);
}
if (res == FileSys::InstallResult::Success) {
success();
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
- const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
+ const auto res2 = Core::System::GetInstance()
+ .GetFileSystemController()
+ .GetUserNANDContents()
+ ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
+ true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {
@@ -1603,7 +1627,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
: FileUtil::UserPath::NANDDir,
dir_path.toStdString());
- Service::FileSystem::CreateFactories(*vfs);
+ Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
game_list->PopulateAsync(UISettings::values.game_dirs);
}
}
@@ -1653,6 +1677,11 @@ void GMainWindow::OnStartGame() {
}
void GMainWindow::OnPauseGame() {
+ Core::System& system{Core::System::GetInstance()};
+ if (system.GetExitLock() && !ConfirmForceLockedExit()) {
+ return;
+ }
+
emu_thread->SetRunning(false);
ui.action_Start->setEnabled(true);
@@ -1664,6 +1693,11 @@ void GMainWindow::OnPauseGame() {
}
void GMainWindow::OnStopGame() {
+ Core::System& system{Core::System::GetInstance()};
+ if (system.GetExitLock() && !ConfirmForceLockedExit()) {
+ return;
+ }
+
ShutdownGame();
}
@@ -1988,7 +2022,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
const auto function = [this, &keys, &pdm] {
keys.PopulateFromPartitionData(pdm);
- Service::FileSystem::CreateFactories(*vfs);
+ Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
keys.DeriveETicket(pdm);
};
@@ -2033,7 +2067,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
prog.close();
}
- Service::FileSystem::CreateFactories(*vfs);
+ Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
if (behavior == ReinitializeKeyBehavior::Warning) {
game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -2161,13 +2195,41 @@ bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
- auto answer = QMessageBox::question(
+ const auto answer = QMessageBox::question(
this, tr("yuzu"),
tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No;
}
+bool GMainWindow::ConfirmForceLockedExit() {
+ if (emu_thread == nullptr)
+ return true;
+
+ const auto answer =
+ QMessageBox::question(this, tr("yuzu"),
+ tr("The currently running application has requested yuzu to not "
+ "exit.\n\nWould you like to bypass this and exit anyway?"),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ return answer != QMessageBox::No;
+}
+
+void GMainWindow::RequestGameExit() {
+ auto& sm{Core::System::GetInstance().ServiceManager()};
+ auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
+ auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
+ bool has_signalled = false;
+
+ if (applet_oe != nullptr) {
+ applet_oe->GetMessageQueue()->RequestExit();
+ has_signalled = true;
+ }
+
+ if (applet_ae != nullptr && !has_signalled) {
+ applet_ae->GetMessageQueue()->RequestExit();
+ }
+}
+
void GMainWindow::filterBarSetChecked(bool state) {
ui.action_Show_Filter_Bar->setChecked(state);
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:
*/
bool ConfirmClose();
bool ConfirmChangeGame();
+ bool ConfirmForceLockedExit();
+ void RequestGameExit();
void closeEvent(QCloseEvent* event) override;
private 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() {
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
sdl2_config->Get("Data Storage", "sdmc_directory",
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+ FileUtil::GetUserPath(FileUtil::UserPath::LoadDir,
+ sdl2_config->Get("Data Storage", "load_directory",
+ FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+ FileUtil::GetUserPath(FileUtil::UserPath::DumpDir,
+ sdl2_config->Get("Data Storage", "dump_directory",
+ FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+ FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
+ sdl2_config->Get("Data Storage", "cache_directory",
+ FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+ Settings::values.gamecard_inserted =
+ sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
+ Settings::values.gamecard_current_game =
+ sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
+ Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
+ Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
+ "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
+ Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
+ "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
+ Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+ sdl2_config->GetInteger("Data Storage", "nand_system_size",
+ static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
+ Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
+ "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
@@ -374,6 +397,8 @@ void Config::ReadValues() {
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
// Debugging
+ Settings::values.record_frame_times =
+ sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port =
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 =
# 1 (default): Yes, 0: No
use_virtual_sd =
+# Whether or not to enable gamecard emulation
+# 1: Yes, 0 (default): No
+gamecard_inserted =
+
+# Whether or not the gamecard should be emulated as the current game
+# If 'gamecard_inserted' is 0 this setting is irrelevant
+# 1: Yes, 0 (default): No
+gamecard_current_game =
+
+# Path to an XCI file to use as the gamecard
+# If 'gamecard_inserted' is 0 this setting is irrelevant
+# If 'gamecard_current_game' is 1 this setting is irrelevant
+gamecard_path =
+
[System]
# Whether the system is docked
# 1: Yes, 0 (default): No
@@ -213,6 +227,8 @@ region_value =
log_filter = *:Trace
[Debugging]
+# Record frame time data, can be found in the log directory. Boolean value
+record_frame_times =
# Port for listening to GDB connections.
use_gdbstub=false
gdbstub_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) {
Core::System& system{Core::System::GetInstance()};
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
- Service::FileSystem::CreateFactories(*system.GetFilesystem());
+ system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
SCOPE_EXIT({ system.Shutdown(); });
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 @@
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
+#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -216,8 +217,9 @@ int main(int argc, char** argv) {
};
Core::System& system{Core::System::GetInstance()};
+ system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
- Service::FileSystem::CreateFactories(*system.GetFilesystem());
+ system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
SCOPE_EXIT({ system.Shutdown(); });