diff options
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 @@ | |||
| 1 | steps: | ||
| 2 | - script: mkdir artifacts || echo 'X' > artifacts/T1.txt | ||
| 3 | - publish: artifacts | ||
| 4 | artifact: 'yuzu-$(BuildName)-$(BuildSuffix)' | ||
| 5 | displayName: 'Upload Artifacts' \ No newline at end of file | ||
diff --git a/.ci/templates/build-single.yml b/.ci/templates/build-single.yml index 357731eb9..9bc27247e 100644 --- a/.ci/templates/build-single.yml +++ b/.ci/templates/build-single.yml | |||
| @@ -3,17 +3,18 @@ parameters: | |||
| 3 | cache: 'false' | 3 | cache: 'false' |
| 4 | 4 | ||
| 5 | steps: | 5 | steps: |
| 6 | - script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE | ||
| 7 | displayName: 'Determine Build Name' | ||
| 6 | - task: DockerInstaller@0 | 8 | - task: DockerInstaller@0 |
| 7 | displayName: 'Prepare Environment' | 9 | displayName: 'Prepare Environment' |
| 8 | inputs: | 10 | inputs: |
| 9 | dockerVersion: '17.09.0-ce' | 11 | dockerVersion: '17.09.0-ce' |
| 10 | - ${{ if eq(parameters.cache, 'true') }}: | 12 | - task: CacheBeta@0 |
| 11 | - task: CacheBeta@0 | 13 | displayName: 'Cache Build System' |
| 12 | displayName: 'Cache Build System' | 14 | inputs: |
| 13 | inputs: | 15 | key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix) |
| 14 | key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix) | 16 | path: $(System.DefaultWorkingDirectory)/ccache |
| 15 | path: $(System.DefaultWorkingDirectory)/ccache | 17 | cacheHitVar: CACHE_RESTORED |
| 16 | cacheHitVar: CACHE_RESTORED | ||
| 17 | - script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh | 18 | - script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh |
| 18 | displayName: 'Build' | 19 | displayName: 'Build' |
| 19 | - script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh | 20 | - script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh |
diff --git a/.ci/templates/release-download.yml b/.ci/templates/release-download.yml new file mode 100644 index 000000000..50ca06bb2 --- /dev/null +++ b/.ci/templates/release-download.yml | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | steps: | ||
| 2 | - task: DownloadPipelineArtifact@2 | ||
| 3 | displayName: 'Download Windows Release' | ||
| 4 | inputs: | ||
| 5 | artifactName: 'yuzu-$(BuildName)-windows-mingw' | ||
| 6 | buildType: 'current' | ||
| 7 | targetPath: '$(Build.ArtifactStagingDirectory)' | ||
| 8 | - task: DownloadPipelineArtifact@2 | ||
| 9 | displayName: 'Download Linux Release' | ||
| 10 | inputs: | ||
| 11 | artifactName: 'yuzu-$(BuildName)-linux' | ||
| 12 | buildType: 'current' | ||
| 13 | targetPath: '$(Build.ArtifactStagingDirectory)' \ No newline at end of file | ||
diff --git a/.ci/templates/release-github.yml b/.ci/templates/release-github.yml new file mode 100644 index 000000000..39fd47f1c --- /dev/null +++ b/.ci/templates/release-github.yml | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | steps: | ||
| 2 | - template: ./release-download.yml | ||
| 3 | - task: GitHubRelease@0 | ||
| 4 | inputs: | ||
| 5 | action: 'create' | ||
| 6 | title: 'yuzu $(BuildName) #$(Build.BuildId)' | ||
| 7 | assets: '$(Build.ArtifactStagingDirectory)/*' | ||
| 8 | gitHubConnection: $(GitHubReleaseConnectionName) | ||
| 9 | repositoryName: '$(Build.Repository.Name)' | ||
| 10 | target: '$(Build.SourceVersion)' | ||
| 11 | tagSource: 'auto' \ No newline at end of file | ||
diff --git a/.ci/templates/release-universal.yml b/.ci/templates/release-universal.yml new file mode 100644 index 000000000..707697007 --- /dev/null +++ b/.ci/templates/release-universal.yml | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | steps: | ||
| 2 | - template: ./release-download.yml | ||
| 3 | - task: UniversalPackages@0 | ||
| 4 | displayName: Publish Artifacts | ||
| 5 | inputs: | ||
| 6 | command: publish | ||
| 7 | publishDirectory: '$(Build.ArtifactStagingDirectory)' | ||
| 8 | vstsFeedPublish: 'yuzu-$(BuildName)' | ||
| 9 | vstsFeedPackagePublish: 'main' | ||
| 10 | packagePublishDescription: 'Yuzu Windows and Linux Executable Packages' \ No newline at end of file | ||
diff --git a/.ci/yuzu-mainline-step1.yml b/.ci/yuzu-mainline-step1.yml new file mode 100644 index 000000000..3fd33d75a --- /dev/null +++ b/.ci/yuzu-mainline-step1.yml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | trigger: | ||
| 2 | - master | ||
| 3 | |||
| 4 | stages: | ||
| 5 | - stage: merge | ||
| 6 | displayName: 'merge' | ||
| 7 | jobs: | ||
| 8 | - template: ./templates/merge.yml | ||
diff --git a/.ci/yuzu-mainline.yml b/.ci/yuzu-mainline-step2.yml index 2930a8564..fec724d11 100644 --- a/.ci/yuzu-mainline.yml +++ b/.ci/yuzu-mainline-step2.yml | |||
| @@ -2,12 +2,7 @@ trigger: | |||
| 2 | - master | 2 | - master |
| 3 | 3 | ||
| 4 | stages: | 4 | stages: |
| 5 | - stage: merge | ||
| 6 | displayName: 'merge' | ||
| 7 | jobs: | ||
| 8 | - template: ./templates/merge.yml | ||
| 9 | - stage: format | 5 | - stage: format |
| 10 | dependsOn: merge | ||
| 11 | displayName: 'format' | 6 | displayName: 'format' |
| 12 | jobs: | 7 | jobs: |
| 13 | - job: format | 8 | - job: format |
| @@ -17,9 +12,17 @@ stages: | |||
| 17 | steps: | 12 | steps: |
| 18 | - template: ./templates/format-check.yml | 13 | - template: ./templates/format-check.yml |
| 19 | - stage: build | 14 | - stage: build |
| 20 | displayName: 'build' | ||
| 21 | dependsOn: format | 15 | dependsOn: format |
| 16 | displayName: 'build' | ||
| 22 | jobs: | 17 | jobs: |
| 23 | - template: ./templates/build-standard.yml | 18 | - template: ./templates/build-standard.yml |
| 24 | parameters: | 19 | parameters: |
| 25 | cache: 'true' | 20 | cache: 'true' |
| 21 | - stage: release | ||
| 22 | displayName: 'Release' | ||
| 23 | dependsOn: build | ||
| 24 | jobs: | ||
| 25 | - job: github | ||
| 26 | displayName: 'GitHub Release' | ||
| 27 | steps: | ||
| 28 | - template: ./templates/release-github.yml \ No newline at end of file | ||
| @@ -6,9 +6,11 @@ yuzu emulator | |||
| 6 | 6 | ||
| 7 | yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). | 7 | yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). |
| 8 | 8 | ||
| 9 | 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. | 9 | 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. |
| 10 | 10 | ||
| 11 | 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. | 11 | yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional. |
| 12 | |||
| 13 | Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)! | ||
| 12 | 14 | ||
| 13 | yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. | 15 | yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. |
| 14 | 16 | ||
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject 2683a9a3e316b5c3f387bbe6787732b9ff44b8d | Subproject 087a74417abfb0a8ae3bc1463d0d476a9bf94e5 | ||
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index da50a0bbc..e6f38d600 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -107,6 +107,11 @@ Stream::State AudioRenderer::GetStreamState() const { | |||
| 107 | return stream->GetState(); | 107 | return stream->GetState(); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | static constexpr u32 VersionFromRevision(u32_le rev) { | ||
| 111 | // "REV7" -> 7 | ||
| 112 | return ((rev >> 24) & 0xff) - 0x30; | ||
| 113 | } | ||
| 114 | |||
| 110 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { | 115 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { |
| 111 | // Copy UpdateDataHeader struct | 116 | // Copy UpdateDataHeader struct |
| 112 | UpdateDataHeader config{}; | 117 | UpdateDataHeader config{}; |
| @@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 166 | // Copy output header | 171 | // Copy output header |
| 167 | UpdateDataHeader response_data{worker_params}; | 172 | UpdateDataHeader response_data{worker_params}; |
| 168 | std::vector<u8> output_params(response_data.total_size); | 173 | std::vector<u8> output_params(response_data.total_size); |
| 174 | const auto audren_revision = VersionFromRevision(config.revision); | ||
| 175 | if (audren_revision >= 5) { | ||
| 176 | response_data.frame_count = 0x10; | ||
| 177 | response_data.total_size += 0x10; | ||
| 178 | } | ||
| 169 | std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); | 179 | std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); |
| 170 | 180 | ||
| 171 | // Copy output memory pool entries | 181 | // Copy output memory pool entries |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 45afbe759..4f14b91cd 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -194,21 +194,24 @@ struct UpdateDataHeader { | |||
| 194 | mixes_size = 0x0; | 194 | mixes_size = 0x0; |
| 195 | sinks_size = config.sink_count * 0x20; | 195 | sinks_size = config.sink_count * 0x20; |
| 196 | performance_manager_size = 0x10; | 196 | performance_manager_size = 0x10; |
| 197 | frame_count = 0; | ||
| 197 | total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + | 198 | total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + |
| 198 | effects_size + sinks_size + performance_manager_size; | 199 | effects_size + sinks_size + performance_manager_size; |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 201 | u32_le revision; | 202 | u32_le revision{}; |
| 202 | u32_le behavior_size; | 203 | u32_le behavior_size{}; |
| 203 | u32_le memory_pools_size; | 204 | u32_le memory_pools_size{}; |
| 204 | u32_le voices_size; | 205 | u32_le voices_size{}; |
| 205 | u32_le voice_resource_size; | 206 | u32_le voice_resource_size{}; |
| 206 | u32_le effects_size; | 207 | u32_le effects_size{}; |
| 207 | u32_le mixes_size; | 208 | u32_le mixes_size{}; |
| 208 | u32_le sinks_size; | 209 | u32_le sinks_size{}; |
| 209 | u32_le performance_manager_size; | 210 | u32_le performance_manager_size{}; |
| 210 | INSERT_PADDING_WORDS(6); | 211 | INSERT_PADDING_WORDS(1); |
| 211 | u32_le total_size; | 212 | u32_le frame_count{}; |
| 213 | INSERT_PADDING_WORDS(4); | ||
| 214 | u32_le total_size{}; | ||
| 212 | }; | 215 | }; |
| 213 | static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); | 216 | static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); |
| 214 | 217 | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 01abdb3bb..dfed8b51d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -10,6 +10,9 @@ if (DEFINED ENV{CI}) | |||
| 10 | elseif(DEFINED ENV{APPVEYOR}) | 10 | elseif(DEFINED ENV{APPVEYOR}) |
| 11 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) | 11 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) |
| 12 | set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) | 12 | set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) |
| 13 | elseif(DEFINED ENV{AZURE}) | ||
| 14 | set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME}) | ||
| 15 | set(BUILD_TAG $ENV{AZURE_REPO_TAG}) | ||
| 13 | endif() | 16 | endif() |
| 14 | endif() | 17 | endif() |
| 15 | add_custom_command(OUTPUT scm_rev.cpp | 18 | 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) { | |||
| 255 | CLS(Input) \ | 255 | CLS(Input) \ |
| 256 | CLS(Network) \ | 256 | CLS(Network) \ |
| 257 | CLS(Loader) \ | 257 | CLS(Loader) \ |
| 258 | CLS(CheatEngine) \ | ||
| 258 | CLS(Crypto) \ | 259 | CLS(Crypto) \ |
| 259 | CLS(WebService) | 260 | CLS(WebService) |
| 260 | 261 | ||
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 8ed6d5050..259708116 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -117,6 +117,7 @@ enum class Class : ClassType { | |||
| 117 | Audio_DSP, ///< The HLE implementation of the DSP | 117 | Audio_DSP, ///< The HLE implementation of the DSP |
| 118 | Audio_Sink, ///< Emulator audio output backend | 118 | Audio_Sink, ///< Emulator audio output backend |
| 119 | Loader, ///< ROM loader | 119 | Loader, ///< ROM loader |
| 120 | CheatEngine, ///< Memory manipulation and engine VM functions | ||
| 120 | Crypto, ///< Cryptographic engine/functions | 121 | Crypto, ///< Cryptographic engine/functions |
| 121 | Input, ///< Input emulation | 122 | Input, ///< Input emulation |
| 122 | Network, ///< Network emulation | 123 | Network, ///< Network emulation |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 877a9e353..a6b56c9c6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -33,8 +33,6 @@ add_library(core STATIC | |||
| 33 | file_sys/bis_factory.h | 33 | file_sys/bis_factory.h |
| 34 | file_sys/card_image.cpp | 34 | file_sys/card_image.cpp |
| 35 | file_sys/card_image.h | 35 | file_sys/card_image.h |
| 36 | file_sys/cheat_engine.cpp | ||
| 37 | file_sys/cheat_engine.h | ||
| 38 | file_sys/content_archive.cpp | 36 | file_sys/content_archive.cpp |
| 39 | file_sys/content_archive.h | 37 | file_sys/content_archive.h |
| 40 | file_sys/control_metadata.cpp | 38 | file_sys/control_metadata.cpp |
| @@ -477,6 +475,11 @@ add_library(core STATIC | |||
| 477 | loader/nsp.h | 475 | loader/nsp.h |
| 478 | loader/xci.cpp | 476 | loader/xci.cpp |
| 479 | loader/xci.h | 477 | loader/xci.h |
| 478 | memory/cheat_engine.cpp | ||
| 479 | memory/cheat_engine.h | ||
| 480 | memory/dmnt_cheat_types.h | ||
| 481 | memory/dmnt_cheat_vm.cpp | ||
| 482 | memory/dmnt_cheat_vm.h | ||
| 480 | memory.cpp | 483 | memory.cpp |
| 481 | memory.h | 484 | memory.h |
| 482 | memory_setup.h | 485 | memory_setup.h |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 3d0978cbf..92ba42fb9 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -14,8 +14,14 @@ | |||
| 14 | #include "core/core_cpu.h" | 14 | #include "core/core_cpu.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_core_manager.h" | 16 | #include "core/cpu_core_manager.h" |
| 17 | #include "core/file_sys/bis_factory.h" | ||
| 18 | #include "core/file_sys/card_image.h" | ||
| 17 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| 20 | #include "core/file_sys/patch_manager.h" | ||
| 18 | #include "core/file_sys/registered_cache.h" | 21 | #include "core/file_sys/registered_cache.h" |
| 22 | #include "core/file_sys/romfs_factory.h" | ||
| 23 | #include "core/file_sys/savedata_factory.h" | ||
| 24 | #include "core/file_sys/sdmc_factory.h" | ||
| 19 | #include "core/file_sys/vfs_concat.h" | 25 | #include "core/file_sys/vfs_concat.h" |
| 20 | #include "core/file_sys/vfs_real.h" | 26 | #include "core/file_sys/vfs_real.h" |
| 21 | #include "core/gdbstub/gdbstub.h" | 27 | #include "core/gdbstub/gdbstub.h" |
| @@ -27,17 +33,17 @@ | |||
| 27 | #include "core/hle/kernel/thread.h" | 33 | #include "core/hle/kernel/thread.h" |
| 28 | #include "core/hle/service/am/applets/applets.h" | 34 | #include "core/hle/service/am/applets/applets.h" |
| 29 | #include "core/hle/service/apm/controller.h" | 35 | #include "core/hle/service/apm/controller.h" |
| 36 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 30 | #include "core/hle/service/glue/manager.h" | 37 | #include "core/hle/service/glue/manager.h" |
| 31 | #include "core/hle/service/service.h" | 38 | #include "core/hle/service/service.h" |
| 32 | #include "core/hle/service/sm/sm.h" | 39 | #include "core/hle/service/sm/sm.h" |
| 33 | #include "core/loader/loader.h" | 40 | #include "core/loader/loader.h" |
| 41 | #include "core/memory/cheat_engine.h" | ||
| 34 | #include "core/perf_stats.h" | 42 | #include "core/perf_stats.h" |
| 35 | #include "core/reporter.h" | 43 | #include "core/reporter.h" |
| 36 | #include "core/settings.h" | 44 | #include "core/settings.h" |
| 37 | #include "core/telemetry_session.h" | 45 | #include "core/telemetry_session.h" |
| 38 | #include "core/tools/freezer.h" | 46 | #include "core/tools/freezer.h" |
| 39 | #include "file_sys/cheat_engine.h" | ||
| 40 | #include "file_sys/patch_manager.h" | ||
| 41 | #include "video_core/debug_utils/debug_utils.h" | 47 | #include "video_core/debug_utils/debug_utils.h" |
| 42 | #include "video_core/renderer_base.h" | 48 | #include "video_core/renderer_base.h" |
| 43 | #include "video_core/video_core.h" | 49 | #include "video_core/video_core.h" |
| @@ -157,13 +163,10 @@ struct System::Impl { | |||
| 157 | gpu_core = VideoCore::CreateGPU(system); | 163 | gpu_core = VideoCore::CreateGPU(system); |
| 158 | 164 | ||
| 159 | is_powered_on = true; | 165 | is_powered_on = true; |
| 166 | exit_lock = false; | ||
| 160 | 167 | ||
| 161 | LOG_DEBUG(Core, "Initialized OK"); | 168 | LOG_DEBUG(Core, "Initialized OK"); |
| 162 | 169 | ||
| 163 | // Reset counters and set time origin to current frame | ||
| 164 | GetAndResetPerfStats(); | ||
| 165 | perf_stats.BeginSystemFrame(); | ||
| 166 | |||
| 167 | return ResultStatus::Success; | 170 | return ResultStatus::Success; |
| 168 | } | 171 | } |
| 169 | 172 | ||
| @@ -202,10 +205,34 @@ struct System::Impl { | |||
| 202 | gpu_core->Start(); | 205 | gpu_core->Start(); |
| 203 | cpu_core_manager.StartThreads(); | 206 | cpu_core_manager.StartThreads(); |
| 204 | 207 | ||
| 208 | // Initialize cheat engine | ||
| 209 | if (cheat_engine) { | ||
| 210 | cheat_engine->Initialize(); | ||
| 211 | } | ||
| 212 | |||
| 205 | // All threads are started, begin main process execution, now that we're in the clear. | 213 | // All threads are started, begin main process execution, now that we're in the clear. |
| 206 | main_process->Run(load_parameters->main_thread_priority, | 214 | main_process->Run(load_parameters->main_thread_priority, |
| 207 | load_parameters->main_thread_stack_size); | 215 | load_parameters->main_thread_stack_size); |
| 208 | 216 | ||
| 217 | if (Settings::values.gamecard_inserted) { | ||
| 218 | if (Settings::values.gamecard_current_game) { | ||
| 219 | fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); | ||
| 220 | } else if (!Settings::values.gamecard_path.empty()) { | ||
| 221 | fs_controller.SetGameCard( | ||
| 222 | GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path)); | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | u64 title_id{0}; | ||
| 227 | if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { | ||
| 228 | LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", | ||
| 229 | static_cast<u32>(load_result)); | ||
| 230 | } | ||
| 231 | perf_stats = std::make_unique<PerfStats>(title_id); | ||
| 232 | // Reset counters and set time origin to current frame | ||
| 233 | GetAndResetPerfStats(); | ||
| 234 | perf_stats->BeginSystemFrame(); | ||
| 235 | |||
| 209 | status = ResultStatus::Success; | 236 | status = ResultStatus::Success; |
| 210 | return status; | 237 | return status; |
| 211 | } | 238 | } |
| @@ -219,8 +246,11 @@ struct System::Impl { | |||
| 219 | perf_results.game_fps); | 246 | perf_results.game_fps); |
| 220 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", | 247 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", |
| 221 | perf_results.frametime * 1000.0); | 248 | perf_results.frametime * 1000.0); |
| 249 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", | ||
| 250 | perf_stats->GetMeanFrametime()); | ||
| 222 | 251 | ||
| 223 | is_powered_on = false; | 252 | is_powered_on = false; |
| 253 | exit_lock = false; | ||
| 224 | 254 | ||
| 225 | // Shutdown emulation session | 255 | // Shutdown emulation session |
| 226 | renderer.reset(); | 256 | renderer.reset(); |
| @@ -229,6 +259,7 @@ struct System::Impl { | |||
| 229 | service_manager.reset(); | 259 | service_manager.reset(); |
| 230 | cheat_engine.reset(); | 260 | cheat_engine.reset(); |
| 231 | telemetry_session.reset(); | 261 | telemetry_session.reset(); |
| 262 | perf_stats.reset(); | ||
| 232 | gpu_core.reset(); | 263 | gpu_core.reset(); |
| 233 | 264 | ||
| 234 | // Close all CPU/threading state | 265 | // Close all CPU/threading state |
| @@ -286,7 +317,7 @@ struct System::Impl { | |||
| 286 | } | 317 | } |
| 287 | 318 | ||
| 288 | PerfStatsResults GetAndResetPerfStats() { | 319 | PerfStatsResults GetAndResetPerfStats() { |
| 289 | return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); | 320 | return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); |
| 290 | } | 321 | } |
| 291 | 322 | ||
| 292 | Timing::CoreTiming core_timing; | 323 | Timing::CoreTiming core_timing; |
| @@ -295,6 +326,7 @@ struct System::Impl { | |||
| 295 | FileSys::VirtualFilesystem virtual_filesystem; | 326 | FileSys::VirtualFilesystem virtual_filesystem; |
| 296 | /// ContentProviderUnion instance | 327 | /// ContentProviderUnion instance |
| 297 | std::unique_ptr<FileSys::ContentProviderUnion> content_provider; | 328 | std::unique_ptr<FileSys::ContentProviderUnion> content_provider; |
| 329 | Service::FileSystem::FileSystemController fs_controller; | ||
| 298 | /// AppLoader used to load the current executing application | 330 | /// AppLoader used to load the current executing application |
| 299 | std::unique_ptr<Loader::AppLoader> app_loader; | 331 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 300 | std::unique_ptr<VideoCore::RendererBase> renderer; | 332 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| @@ -303,8 +335,9 @@ struct System::Impl { | |||
| 303 | std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; | 335 | std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; |
| 304 | CpuCoreManager cpu_core_manager; | 336 | CpuCoreManager cpu_core_manager; |
| 305 | bool is_powered_on = false; | 337 | bool is_powered_on = false; |
| 338 | bool exit_lock = false; | ||
| 306 | 339 | ||
| 307 | std::unique_ptr<FileSys::CheatEngine> cheat_engine; | 340 | std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| 308 | std::unique_ptr<Tools::Freezer> memory_freezer; | 341 | std::unique_ptr<Tools::Freezer> memory_freezer; |
| 309 | 342 | ||
| 310 | /// Frontend applets | 343 | /// Frontend applets |
| @@ -327,7 +360,7 @@ struct System::Impl { | |||
| 327 | ResultStatus status = ResultStatus::Success; | 360 | ResultStatus status = ResultStatus::Success; |
| 328 | std::string status_details = ""; | 361 | std::string status_details = ""; |
| 329 | 362 | ||
| 330 | Core::PerfStats perf_stats; | 363 | std::unique_ptr<Core::PerfStats> perf_stats; |
| 331 | Core::FrameLimiter frame_limiter; | 364 | Core::FrameLimiter frame_limiter; |
| 332 | }; | 365 | }; |
| 333 | 366 | ||
| @@ -480,11 +513,11 @@ const Timing::CoreTiming& System::CoreTiming() const { | |||
| 480 | } | 513 | } |
| 481 | 514 | ||
| 482 | Core::PerfStats& System::GetPerfStats() { | 515 | Core::PerfStats& System::GetPerfStats() { |
| 483 | return impl->perf_stats; | 516 | return *impl->perf_stats; |
| 484 | } | 517 | } |
| 485 | 518 | ||
| 486 | const Core::PerfStats& System::GetPerfStats() const { | 519 | const Core::PerfStats& System::GetPerfStats() const { |
| 487 | return impl->perf_stats; | 520 | return *impl->perf_stats; |
| 488 | } | 521 | } |
| 489 | 522 | ||
| 490 | Core::FrameLimiter& System::FrameLimiter() { | 523 | Core::FrameLimiter& System::FrameLimiter() { |
| @@ -519,13 +552,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const { | |||
| 519 | return impl->debug_context.get(); | 552 | return impl->debug_context.get(); |
| 520 | } | 553 | } |
| 521 | 554 | ||
| 522 | void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list, | ||
| 523 | const std::string& build_id, VAddr code_region_start, | ||
| 524 | VAddr code_region_end) { | ||
| 525 | impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id, | ||
| 526 | code_region_start, code_region_end); | ||
| 527 | } | ||
| 528 | |||
| 529 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 555 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { |
| 530 | impl->virtual_filesystem = std::move(vfs); | 556 | impl->virtual_filesystem = std::move(vfs); |
| 531 | } | 557 | } |
| @@ -534,6 +560,13 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | |||
| 534 | return impl->virtual_filesystem; | 560 | return impl->virtual_filesystem; |
| 535 | } | 561 | } |
| 536 | 562 | ||
| 563 | void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list, | ||
| 564 | const std::array<u8, 32>& build_id, VAddr main_region_begin, | ||
| 565 | u64 main_region_size) { | ||
| 566 | impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id); | ||
| 567 | impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size); | ||
| 568 | } | ||
| 569 | |||
| 537 | void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { | 570 | void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { |
| 538 | impl->applet_manager.SetAppletFrontendSet(std::move(set)); | 571 | impl->applet_manager.SetAppletFrontendSet(std::move(set)); |
| 539 | } | 572 | } |
| @@ -562,6 +595,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const { | |||
| 562 | return *impl->content_provider; | 595 | return *impl->content_provider; |
| 563 | } | 596 | } |
| 564 | 597 | ||
| 598 | Service::FileSystem::FileSystemController& System::GetFileSystemController() { | ||
| 599 | return impl->fs_controller; | ||
| 600 | } | ||
| 601 | |||
| 602 | const Service::FileSystem::FileSystemController& System::GetFileSystemController() const { | ||
| 603 | return impl->fs_controller; | ||
| 604 | } | ||
| 605 | |||
| 565 | void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | 606 | void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, |
| 566 | FileSys::ContentProvider* provider) { | 607 | FileSys::ContentProvider* provider) { |
| 567 | impl->content_provider->SetSlot(slot, provider); | 608 | impl->content_provider->SetSlot(slot, provider); |
| @@ -591,6 +632,14 @@ const Service::APM::Controller& System::GetAPMController() const { | |||
| 591 | return impl->apm_controller; | 632 | return impl->apm_controller; |
| 592 | } | 633 | } |
| 593 | 634 | ||
| 635 | void System::SetExitLock(bool locked) { | ||
| 636 | impl->exit_lock = locked; | ||
| 637 | } | ||
| 638 | |||
| 639 | bool System::GetExitLock() const { | ||
| 640 | return impl->exit_lock; | ||
| 641 | } | ||
| 642 | |||
| 594 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 643 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 595 | return impl->Init(*this, emu_window); | 644 | return impl->Init(*this, emu_window); |
| 596 | } | 645 | } |
diff --git a/src/core/core.h b/src/core/core.h index 0138d93b0..ff10ebe12 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -18,7 +18,6 @@ class EmuWindow; | |||
| 18 | } // namespace Core::Frontend | 18 | } // namespace Core::Frontend |
| 19 | 19 | ||
| 20 | namespace FileSys { | 20 | namespace FileSys { |
| 21 | class CheatList; | ||
| 22 | class ContentProvider; | 21 | class ContentProvider; |
| 23 | class ContentProviderUnion; | 22 | class ContentProviderUnion; |
| 24 | enum class ContentProviderUnionSlot; | 23 | enum class ContentProviderUnionSlot; |
| @@ -36,6 +35,10 @@ class AppLoader; | |||
| 36 | enum class ResultStatus : u16; | 35 | enum class ResultStatus : u16; |
| 37 | } // namespace Loader | 36 | } // namespace Loader |
| 38 | 37 | ||
| 38 | namespace Memory { | ||
| 39 | struct CheatEntry; | ||
| 40 | } // namespace Memory | ||
| 41 | |||
| 39 | namespace Service { | 42 | namespace Service { |
| 40 | 43 | ||
| 41 | namespace AM::Applets { | 44 | namespace AM::Applets { |
| @@ -47,6 +50,10 @@ namespace APM { | |||
| 47 | class Controller; | 50 | class Controller; |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 53 | namespace FileSystem { | ||
| 54 | class FileSystemController; | ||
| 55 | } // namespace FileSystem | ||
| 56 | |||
| 50 | namespace Glue { | 57 | namespace Glue { |
| 51 | class ARPManager; | 58 | class ARPManager; |
| 52 | } | 59 | } |
| @@ -282,8 +289,9 @@ public: | |||
| 282 | 289 | ||
| 283 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; | 290 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; |
| 284 | 291 | ||
| 285 | void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, | 292 | void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, |
| 286 | VAddr code_region_start, VAddr code_region_end); | 293 | const std::array<u8, 0x20>& build_id, VAddr main_region_begin, |
| 294 | u64 main_region_size); | ||
| 287 | 295 | ||
| 288 | void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); | 296 | void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); |
| 289 | 297 | ||
| @@ -299,6 +307,10 @@ public: | |||
| 299 | 307 | ||
| 300 | const FileSys::ContentProvider& GetContentProvider() const; | 308 | const FileSys::ContentProvider& GetContentProvider() const; |
| 301 | 309 | ||
| 310 | Service::FileSystem::FileSystemController& GetFileSystemController(); | ||
| 311 | |||
| 312 | const Service::FileSystem::FileSystemController& GetFileSystemController() const; | ||
| 313 | |||
| 302 | void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, | 314 | void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, |
| 303 | FileSys::ContentProvider* provider); | 315 | FileSys::ContentProvider* provider); |
| 304 | 316 | ||
| @@ -314,6 +326,10 @@ public: | |||
| 314 | 326 | ||
| 315 | const Service::APM::Controller& GetAPMController() const; | 327 | const Service::APM::Controller& GetAPMController() const; |
| 316 | 328 | ||
| 329 | void SetExitLock(bool locked); | ||
| 330 | |||
| 331 | bool GetExitLock() const; | ||
| 332 | |||
| 317 | private: | 333 | private: |
| 318 | System(); | 334 | System(); |
| 319 | 335 | ||
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 01a969be9..594cd82c5 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) { | |||
| 480 | prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key); | 480 | prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key); |
| 481 | } | 481 | } |
| 482 | 482 | ||
| 483 | FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const { | ||
| 484 | return prodinfo_decrypted; | ||
| 485 | } | ||
| 486 | |||
| 483 | std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { | 487 | std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { |
| 484 | std::array<u8, 0x240> out{}; | 488 | std::array<u8, 0x240> out{}; |
| 485 | if (prodinfo_decrypted != nullptr) | 489 | if (prodinfo_decrypted != nullptr) |
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 0ad007c72..7a7b5d038 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h | |||
| @@ -84,6 +84,7 @@ public: | |||
| 84 | bool HasProdInfo() const; | 84 | bool HasProdInfo() const; |
| 85 | FileSys::VirtualFile GetProdInfoRaw() const; | 85 | FileSys::VirtualFile GetProdInfoRaw() const; |
| 86 | void DecryptProdInfo(std::array<u8, 0x20> bis_key); | 86 | void DecryptProdInfo(std::array<u8, 0x20> bis_key); |
| 87 | FileSys::VirtualFile GetDecryptedProdInfo() const; | ||
| 87 | std::array<u8, 0x240> GetETicketExtendedKek() const; | 88 | std::array<u8, 0x240> GetETicketExtendedKek() const; |
| 88 | 89 | ||
| 89 | private: | 90 | 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 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | #include "common/file_util.h" | ||
| 7 | #include "core/core.h" | ||
| 6 | #include "core/file_sys/bis_factory.h" | 8 | #include "core/file_sys/bis_factory.h" |
| 9 | #include "core/file_sys/mode.h" | ||
| 7 | #include "core/file_sys/registered_cache.h" | 10 | #include "core/file_sys/registered_cache.h" |
| 11 | #include "core/settings.h" | ||
| 8 | 12 | ||
| 9 | namespace FileSys { | 13 | namespace FileSys { |
| 10 | 14 | ||
| @@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir | |||
| 14 | sysnand_cache(std::make_unique<RegisteredCache>( | 18 | sysnand_cache(std::make_unique<RegisteredCache>( |
| 15 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), | 19 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), |
| 16 | usrnand_cache(std::make_unique<RegisteredCache>( | 20 | usrnand_cache(std::make_unique<RegisteredCache>( |
| 17 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} | 21 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))), |
| 22 | sysnand_placeholder(std::make_unique<PlaceholderCache>( | ||
| 23 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))), | ||
| 24 | usrnand_placeholder(std::make_unique<PlaceholderCache>( | ||
| 25 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {} | ||
| 18 | 26 | ||
| 19 | BISFactory::~BISFactory() = default; | 27 | BISFactory::~BISFactory() = default; |
| 20 | 28 | ||
| 29 | VirtualDir BISFactory::GetSystemNANDContentDirectory() const { | ||
| 30 | return GetOrCreateDirectoryRelative(nand_root, "/system/Contents"); | ||
| 31 | } | ||
| 32 | |||
| 33 | VirtualDir BISFactory::GetUserNANDContentDirectory() const { | ||
| 34 | return GetOrCreateDirectoryRelative(nand_root, "/user/Contents"); | ||
| 35 | } | ||
| 36 | |||
| 21 | RegisteredCache* BISFactory::GetSystemNANDContents() const { | 37 | RegisteredCache* BISFactory::GetSystemNANDContents() const { |
| 22 | return sysnand_cache.get(); | 38 | return sysnand_cache.get(); |
| 23 | } | 39 | } |
| @@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const { | |||
| 26 | return usrnand_cache.get(); | 42 | return usrnand_cache.get(); |
| 27 | } | 43 | } |
| 28 | 44 | ||
| 45 | PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const { | ||
| 46 | return sysnand_placeholder.get(); | ||
| 47 | } | ||
| 48 | |||
| 49 | PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const { | ||
| 50 | return usrnand_placeholder.get(); | ||
| 51 | } | ||
| 52 | |||
| 29 | VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { | 53 | VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { |
| 30 | // LayeredFS doesn't work on updates and title id-less homebrew | 54 | // LayeredFS doesn't work on updates and title id-less homebrew |
| 31 | if (title_id == 0 || (title_id & 0x800) > 0) | 55 | if (title_id == 0 || (title_id & 0xFFF) == 0x800) |
| 32 | return nullptr; | 56 | return nullptr; |
| 33 | return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); | 57 | return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); |
| 34 | } | 58 | } |
| @@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const { | |||
| 39 | return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); | 63 | return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); |
| 40 | } | 64 | } |
| 41 | 65 | ||
| 66 | VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | ||
| 67 | switch (id) { | ||
| 68 | case BisPartitionId::CalibrationFile: | ||
| 69 | return GetOrCreateDirectoryRelative(nand_root, "/prodinfof"); | ||
| 70 | case BisPartitionId::SafeMode: | ||
| 71 | return GetOrCreateDirectoryRelative(nand_root, "/safe"); | ||
| 72 | case BisPartitionId::System: | ||
| 73 | return GetOrCreateDirectoryRelative(nand_root, "/system"); | ||
| 74 | case BisPartitionId::User: | ||
| 75 | return GetOrCreateDirectoryRelative(nand_root, "/user"); | ||
| 76 | default: | ||
| 77 | return nullptr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | ||
| 82 | Core::Crypto::KeyManager keys; | ||
| 83 | Core::Crypto::PartitionDataManager pdm{ | ||
| 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( | ||
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | ||
| 86 | keys.PopulateFromPartitionData(pdm); | ||
| 87 | |||
| 88 | switch (id) { | ||
| 89 | case BisPartitionId::CalibrationBinary: | ||
| 90 | return pdm.GetDecryptedProdInfo(); | ||
| 91 | case BisPartitionId::BootConfigAndPackage2Part1: | ||
| 92 | case BisPartitionId::BootConfigAndPackage2Part2: | ||
| 93 | case BisPartitionId::BootConfigAndPackage2Part3: | ||
| 94 | case BisPartitionId::BootConfigAndPackage2Part4: | ||
| 95 | case BisPartitionId::BootConfigAndPackage2Part5: | ||
| 96 | case BisPartitionId::BootConfigAndPackage2Part6: { | ||
| 97 | const auto new_id = static_cast<u8>(id) - | ||
| 98 | static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) + | ||
| 99 | static_cast<u8>(Core::Crypto::Package2Type::NormalMain); | ||
| 100 | return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id)); | ||
| 101 | } | ||
| 102 | default: | ||
| 103 | return nullptr; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | VirtualDir BISFactory::GetImageDirectory() const { | ||
| 108 | return GetOrCreateDirectoryRelative(nand_root, "/user/Album"); | ||
| 109 | } | ||
| 110 | |||
| 111 | u64 BISFactory::GetSystemNANDFreeSpace() const { | ||
| 112 | const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); | ||
| 113 | if (sys_dir == nullptr) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | return GetSystemNANDTotalSpace() - sys_dir->GetSize(); | ||
| 117 | } | ||
| 118 | |||
| 119 | u64 BISFactory::GetSystemNANDTotalSpace() const { | ||
| 120 | return static_cast<u64>(Settings::values.nand_system_size); | ||
| 121 | } | ||
| 122 | |||
| 123 | u64 BISFactory::GetUserNANDFreeSpace() const { | ||
| 124 | const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user"); | ||
| 125 | if (usr_dir == nullptr) | ||
| 126 | return 0; | ||
| 127 | |||
| 128 | return GetUserNANDTotalSpace() - usr_dir->GetSize(); | ||
| 129 | } | ||
| 130 | |||
| 131 | u64 BISFactory::GetUserNANDTotalSpace() const { | ||
| 132 | return static_cast<u64>(Settings::values.nand_user_size); | ||
| 133 | } | ||
| 134 | |||
| 135 | u64 BISFactory::GetFullNANDTotalSpace() const { | ||
| 136 | return static_cast<u64>(Settings::values.nand_total_size); | ||
| 137 | } | ||
| 138 | |||
| 42 | } // namespace FileSys | 139 | } // namespace FileSys |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 453c11ad2..bdfe728c9 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -10,7 +10,25 @@ | |||
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | enum class BisPartitionId : u32 { | ||
| 14 | UserDataRoot = 20, | ||
| 15 | CalibrationBinary = 27, | ||
| 16 | CalibrationFile = 28, | ||
| 17 | BootConfigAndPackage2Part1 = 21, | ||
| 18 | BootConfigAndPackage2Part2 = 22, | ||
| 19 | BootConfigAndPackage2Part3 = 23, | ||
| 20 | BootConfigAndPackage2Part4 = 24, | ||
| 21 | BootConfigAndPackage2Part5 = 25, | ||
| 22 | BootConfigAndPackage2Part6 = 26, | ||
| 23 | SafeMode = 29, | ||
| 24 | System = 31, | ||
| 25 | SystemProperEncryption = 32, | ||
| 26 | SystemProperPartition = 33, | ||
| 27 | User = 30, | ||
| 28 | }; | ||
| 29 | |||
| 13 | class RegisteredCache; | 30 | class RegisteredCache; |
| 31 | class PlaceholderCache; | ||
| 14 | 32 | ||
| 15 | /// File system interface to the Built-In Storage | 33 | /// File system interface to the Built-In Storage |
| 16 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND | 34 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND |
| @@ -20,12 +38,29 @@ public: | |||
| 20 | explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); | 38 | explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); |
| 21 | ~BISFactory(); | 39 | ~BISFactory(); |
| 22 | 40 | ||
| 41 | VirtualDir GetSystemNANDContentDirectory() const; | ||
| 42 | VirtualDir GetUserNANDContentDirectory() const; | ||
| 43 | |||
| 23 | RegisteredCache* GetSystemNANDContents() const; | 44 | RegisteredCache* GetSystemNANDContents() const; |
| 24 | RegisteredCache* GetUserNANDContents() const; | 45 | RegisteredCache* GetUserNANDContents() const; |
| 25 | 46 | ||
| 47 | PlaceholderCache* GetSystemNANDPlaceholder() const; | ||
| 48 | PlaceholderCache* GetUserNANDPlaceholder() const; | ||
| 49 | |||
| 26 | VirtualDir GetModificationLoadRoot(u64 title_id) const; | 50 | VirtualDir GetModificationLoadRoot(u64 title_id) const; |
| 27 | VirtualDir GetModificationDumpRoot(u64 title_id) const; | 51 | VirtualDir GetModificationDumpRoot(u64 title_id) const; |
| 28 | 52 | ||
| 53 | VirtualDir OpenPartition(BisPartitionId id) const; | ||
| 54 | VirtualFile OpenPartitionStorage(BisPartitionId id) const; | ||
| 55 | |||
| 56 | VirtualDir GetImageDirectory() const; | ||
| 57 | |||
| 58 | u64 GetSystemNANDFreeSpace() const; | ||
| 59 | u64 GetSystemNANDTotalSpace() const; | ||
| 60 | u64 GetUserNANDFreeSpace() const; | ||
| 61 | u64 GetUserNANDTotalSpace() const; | ||
| 62 | u64 GetFullNANDTotalSpace() const; | ||
| 63 | |||
| 29 | private: | 64 | private: |
| 30 | VirtualDir nand_root; | 65 | VirtualDir nand_root; |
| 31 | VirtualDir load_root; | 66 | VirtualDir load_root; |
| @@ -33,6 +68,9 @@ private: | |||
| 33 | 68 | ||
| 34 | std::unique_ptr<RegisteredCache> sysnand_cache; | 69 | std::unique_ptr<RegisteredCache> sysnand_cache; |
| 35 | std::unique_ptr<RegisteredCache> usrnand_cache; | 70 | std::unique_ptr<RegisteredCache> usrnand_cache; |
| 71 | |||
| 72 | std::unique_ptr<PlaceholderCache> sysnand_placeholder; | ||
| 73 | std::unique_ptr<PlaceholderCache> usrnand_placeholder; | ||
| 36 | }; | 74 | }; |
| 37 | 75 | ||
| 38 | } // namespace FileSys | 76 | } // namespace FileSys |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 626ed0042..db54113a0 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -12,12 +12,16 @@ | |||
| 12 | #include "core/file_sys/content_archive.h" | 12 | #include "core/file_sys/content_archive.h" |
| 13 | #include "core/file_sys/nca_metadata.h" | 13 | #include "core/file_sys/nca_metadata.h" |
| 14 | #include "core/file_sys/partition_filesystem.h" | 14 | #include "core/file_sys/partition_filesystem.h" |
| 15 | #include "core/file_sys/romfs.h" | ||
| 15 | #include "core/file_sys/submission_package.h" | 16 | #include "core/file_sys/submission_package.h" |
| 17 | #include "core/file_sys/vfs_concat.h" | ||
| 16 | #include "core/file_sys/vfs_offset.h" | 18 | #include "core/file_sys/vfs_offset.h" |
| 19 | #include "core/file_sys/vfs_vector.h" | ||
| 17 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| 18 | 21 | ||
| 19 | namespace FileSys { | 22 | namespace FileSys { |
| 20 | 23 | ||
| 24 | constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000; | ||
| 21 | constexpr std::array partition_names{ | 25 | constexpr std::array partition_names{ |
| 22 | "update", | 26 | "update", |
| 23 | "normal", | 27 | "normal", |
| @@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const { | |||
| 175 | return file->GetContainingDirectory(); | 179 | return file->GetContainingDirectory(); |
| 176 | } | 180 | } |
| 177 | 181 | ||
| 182 | VirtualDir XCI::ConcatenatedPseudoDirectory() { | ||
| 183 | const auto out = std::make_shared<VectorVfsDirectory>(); | ||
| 184 | for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) { | ||
| 185 | const auto& part = GetPartition(part_id); | ||
| 186 | if (part == nullptr) | ||
| 187 | continue; | ||
| 188 | |||
| 189 | for (const auto& file : part->GetFiles()) | ||
| 190 | out->AddFile(file); | ||
| 191 | } | ||
| 192 | |||
| 193 | return out; | ||
| 194 | } | ||
| 195 | |||
| 196 | std::array<u8, 0x200> XCI::GetCertificate() const { | ||
| 197 | std::array<u8, 0x200> out; | ||
| 198 | file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET); | ||
| 199 | return out; | ||
| 200 | } | ||
| 201 | |||
| 178 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | 202 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { |
| 179 | const auto partition_index = static_cast<std::size_t>(part); | 203 | const auto partition_index = static_cast<std::size_t>(part); |
| 180 | const auto& partition = partitions[partition_index]; | 204 | const auto& partition = partitions[partition_index]; |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index a350496f7..3e6b92ff3 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -91,6 +91,8 @@ public: | |||
| 91 | VirtualDir GetLogoPartition() const; | 91 | VirtualDir GetLogoPartition() const; |
| 92 | 92 | ||
| 93 | u64 GetProgramTitleID() const; | 93 | u64 GetProgramTitleID() const; |
| 94 | u32 GetSystemUpdateVersion(); | ||
| 95 | u64 GetSystemUpdateTitleID() const; | ||
| 94 | 96 | ||
| 95 | bool HasProgramNCA() const; | 97 | bool HasProgramNCA() const; |
| 96 | VirtualFile GetProgramNCAFile() const; | 98 | VirtualFile GetProgramNCAFile() const; |
| @@ -106,6 +108,11 @@ public: | |||
| 106 | 108 | ||
| 107 | VirtualDir GetParentDirectory() const override; | 109 | VirtualDir GetParentDirectory() const override; |
| 108 | 110 | ||
| 111 | // Creates a directory that contains all the NCAs in the gamecard | ||
| 112 | VirtualDir ConcatenatedPseudoDirectory(); | ||
| 113 | |||
| 114 | std::array<u8, 0x200> GetCertificate() const; | ||
| 115 | |||
| 109 | private: | 116 | private: |
| 110 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); | 117 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); |
| 111 | 118 | ||
| @@ -120,6 +127,8 @@ private: | |||
| 120 | std::shared_ptr<NCA> program; | 127 | std::shared_ptr<NCA> program; |
| 121 | std::vector<std::shared_ptr<NCA>> ncas; | 128 | std::vector<std::shared_ptr<NCA>> ncas; |
| 122 | 129 | ||
| 130 | u64 update_normal_partition_end; | ||
| 131 | |||
| 123 | Core::Crypto::KeyManager keys; | 132 | Core::Crypto::KeyManager keys; |
| 124 | }; | 133 | }; |
| 125 | } // namespace FileSys | 134 | } // namespace FileSys |
diff --git a/src/core/file_sys/cheat_engine.cpp b/src/core/file_sys/cheat_engine.cpp deleted file mode 100644 index b06c2f20a..000000000 --- a/src/core/file_sys/cheat_engine.cpp +++ /dev/null | |||
| @@ -1,492 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <locale> | ||
| 6 | #include "common/hex_util.h" | ||
| 7 | #include "common/microprofile.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/core_timing.h" | ||
| 11 | #include "core/core_timing_util.h" | ||
| 12 | #include "core/file_sys/cheat_engine.h" | ||
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 15 | #include "core/hle/service/hid/hid.h" | ||
| 16 | #include "core/hle/service/sm/sm.h" | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); | ||
| 21 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | ||
| 22 | |||
| 23 | u64 Cheat::Address() const { | ||
| 24 | u64 out; | ||
| 25 | std::memcpy(&out, raw.data(), sizeof(u64)); | ||
| 26 | return Common::swap64(out) & 0xFFFFFFFFFF; | ||
| 27 | } | ||
| 28 | |||
| 29 | u64 Cheat::ValueWidth(u64 offset) const { | ||
| 30 | return Value(offset, width); | ||
| 31 | } | ||
| 32 | |||
| 33 | u64 Cheat::Value(u64 offset, u64 width) const { | ||
| 34 | u64 out; | ||
| 35 | std::memcpy(&out, raw.data() + offset, sizeof(u64)); | ||
| 36 | out = Common::swap64(out); | ||
| 37 | if (width == 8) | ||
| 38 | return out; | ||
| 39 | return out & ((1ull << (width * CHAR_BIT)) - 1); | ||
| 40 | } | ||
| 41 | |||
| 42 | u32 Cheat::KeypadValue() const { | ||
| 43 | u32 out; | ||
| 44 | std::memcpy(&out, raw.data(), sizeof(u32)); | ||
| 45 | return Common::swap32(out) & 0x0FFFFFFF; | ||
| 46 | } | ||
| 47 | |||
| 48 | void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, | ||
| 49 | VAddr heap_end, MemoryWriter writer, MemoryReader reader) { | ||
| 50 | this->main_region_begin = main_begin; | ||
| 51 | this->main_region_end = main_end; | ||
| 52 | this->heap_region_begin = heap_begin; | ||
| 53 | this->heap_region_end = heap_end; | ||
| 54 | this->writer = writer; | ||
| 55 | this->reader = reader; | ||
| 56 | } | ||
| 57 | |||
| 58 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | ||
| 59 | |||
| 60 | void CheatList::Execute() { | ||
| 61 | MICROPROFILE_SCOPE(Cheat_Engine); | ||
| 62 | |||
| 63 | std::fill(scratch.begin(), scratch.end(), 0); | ||
| 64 | in_standard = false; | ||
| 65 | for (std::size_t i = 0; i < master_list.size(); ++i) { | ||
| 66 | LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first); | ||
| 67 | current_block = i; | ||
| 68 | ExecuteBlock(master_list[i].second); | ||
| 69 | } | ||
| 70 | |||
| 71 | in_standard = true; | ||
| 72 | for (std::size_t i = 0; i < standard_list.size(); ++i) { | ||
| 73 | LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first); | ||
| 74 | current_block = i; | ||
| 75 | ExecuteBlock(standard_list[i].second); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard) | ||
| 80 | : master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {} | ||
| 81 | |||
| 82 | bool CheatList::EvaluateConditional(const Cheat& cheat) const { | ||
| 83 | using ComparisonFunction = bool (*)(u64, u64); | ||
| 84 | constexpr std::array<ComparisonFunction, 6> comparison_functions{ | ||
| 85 | [](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; }, | ||
| 86 | [](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; }, | ||
| 87 | [](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; }, | ||
| 88 | }; | ||
| 89 | |||
| 90 | if (cheat.type == CodeType::ConditionalInput) { | ||
| 91 | const auto applet_resource = | ||
| 92 | system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource(); | ||
| 93 | if (applet_resource == nullptr) { | ||
| 94 | LOG_WARNING( | ||
| 95 | Common_Filesystem, | ||
| 96 | "Attempted to evaluate input conditional, but applet resource is not initialized!"); | ||
| 97 | return false; | ||
| 98 | } | ||
| 99 | |||
| 100 | const auto press_state = | ||
| 101 | applet_resource | ||
| 102 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | ||
| 103 | .GetAndResetPressState(); | ||
| 104 | return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | ASSERT(cheat.type == CodeType::Conditional); | ||
| 108 | |||
| 109 | const auto offset = | ||
| 110 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 111 | ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6); | ||
| 112 | auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())]; | ||
| 113 | const auto addr = cheat.Address() + offset; | ||
| 114 | |||
| 115 | return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8)); | ||
| 116 | } | ||
| 117 | |||
| 118 | void CheatList::ProcessBlockPairs(const Block& block) { | ||
| 119 | block_pairs.clear(); | ||
| 120 | |||
| 121 | u64 scope = 0; | ||
| 122 | std::map<u64, u64> pairs; | ||
| 123 | |||
| 124 | for (std::size_t i = 0; i < block.size(); ++i) { | ||
| 125 | const auto& cheat = block[i]; | ||
| 126 | |||
| 127 | switch (cheat.type) { | ||
| 128 | case CodeType::Conditional: | ||
| 129 | case CodeType::ConditionalInput: | ||
| 130 | pairs.insert_or_assign(scope, i); | ||
| 131 | ++scope; | ||
| 132 | break; | ||
| 133 | case CodeType::EndConditional: { | ||
| 134 | --scope; | ||
| 135 | const auto idx = pairs.at(scope); | ||
| 136 | block_pairs.insert_or_assign(idx, i); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | case CodeType::Loop: { | ||
| 140 | if (cheat.end_of_loop) { | ||
| 141 | --scope; | ||
| 142 | const auto idx = pairs.at(scope); | ||
| 143 | block_pairs.insert_or_assign(idx, i); | ||
| 144 | } else { | ||
| 145 | pairs.insert_or_assign(scope, i); | ||
| 146 | ++scope; | ||
| 147 | } | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | void CheatList::WriteImmediate(const Cheat& cheat) { | ||
| 155 | const auto offset = | ||
| 156 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 157 | const auto& register_3 = scratch.at(cheat.register_3); | ||
| 158 | |||
| 159 | const auto addr = cheat.Address() + offset + register_3; | ||
| 160 | LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr, | ||
| 161 | cheat.Value(8, cheat.width)); | ||
| 162 | writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8)); | ||
| 163 | } | ||
| 164 | |||
| 165 | void CheatList::BeginConditional(const Cheat& cheat) { | ||
| 166 | if (EvaluateConditional(cheat)) { | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | |||
| 170 | const auto iter = block_pairs.find(current_index); | ||
| 171 | ASSERT(iter != block_pairs.end()); | ||
| 172 | current_index = iter->second - 1; | ||
| 173 | } | ||
| 174 | |||
| 175 | void CheatList::EndConditional(const Cheat& cheat) { | ||
| 176 | LOG_DEBUG(Common_Filesystem, "Ending conditional block."); | ||
| 177 | } | ||
| 178 | |||
| 179 | void CheatList::Loop(const Cheat& cheat) { | ||
| 180 | if (cheat.end_of_loop.Value()) | ||
| 181 | ASSERT(!cheat.end_of_loop.Value()); | ||
| 182 | |||
| 183 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 184 | const auto iter = block_pairs.find(current_index); | ||
| 185 | ASSERT(iter != block_pairs.end()); | ||
| 186 | ASSERT(iter->first < iter->second); | ||
| 187 | |||
| 188 | const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32))); | ||
| 189 | for (s32 i = initial_value; i >= 0; --i) { | ||
| 190 | register_3 = static_cast<u64>(i); | ||
| 191 | for (std::size_t c = iter->first + 1; c < iter->second; ++c) { | ||
| 192 | current_index = c; | ||
| 193 | ExecuteSingleCheat( | ||
| 194 | (in_standard ? standard_list : master_list)[current_block].second[c]); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | current_index = iter->second; | ||
| 199 | } | ||
| 200 | |||
| 201 | void CheatList::LoadImmediate(const Cheat& cheat) { | ||
| 202 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 203 | |||
| 204 | LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3, | ||
| 205 | cheat.Value(4, 8)); | ||
| 206 | register_3 = cheat.Value(4, 8); | ||
| 207 | } | ||
| 208 | |||
| 209 | void CheatList::LoadIndexed(const Cheat& cheat) { | ||
| 210 | const auto offset = | ||
| 211 | cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin; | ||
| 212 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 213 | |||
| 214 | const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address(); | ||
| 215 | LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}", | ||
| 216 | cheat.register_3, addr); | ||
| 217 | register_3 = reader(cheat.width, SanitizeAddress(addr)); | ||
| 218 | } | ||
| 219 | |||
| 220 | void CheatList::StoreIndexed(const Cheat& cheat) { | ||
| 221 | const auto& register_3 = scratch.at(cheat.register_3); | ||
| 222 | |||
| 223 | const auto addr = | ||
| 224 | register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0); | ||
| 225 | LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", | ||
| 226 | cheat.Value(4, cheat.width), addr); | ||
| 227 | writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4)); | ||
| 228 | } | ||
| 229 | |||
| 230 | void CheatList::RegisterArithmetic(const Cheat& cheat) { | ||
| 231 | using ArithmeticFunction = u64 (*)(u64, u64); | ||
| 232 | constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{ | ||
| 233 | [](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; }, | ||
| 234 | [](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; }, | ||
| 235 | [](u64 a, u64 b) { return a >> b; }, | ||
| 236 | }; | ||
| 237 | |||
| 238 | using ArithmeticOverflowCheck = bool (*)(u64, u64); | ||
| 239 | constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{ | ||
| 240 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b | ||
| 241 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b | ||
| 242 | [](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b | ||
| 243 | [](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b | ||
| 244 | [](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b | ||
| 245 | }; | ||
| 246 | |||
| 247 | static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks), | ||
| 248 | "Missing or have extra arithmetic overflow checks compared to functions!"); | ||
| 249 | |||
| 250 | auto& register_3 = scratch.at(cheat.register_3); | ||
| 251 | |||
| 252 | ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5); | ||
| 253 | auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())]; | ||
| 254 | auto* overflow_function = | ||
| 255 | arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())]; | ||
| 256 | LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}", | ||
| 257 | cheat.register_3, cheat.ValueWidth(4)); | ||
| 258 | |||
| 259 | if (overflow_function(register_3, cheat.ValueWidth(4))) { | ||
| 260 | LOG_WARNING(Common_Filesystem, | ||
| 261 | "overflow will occur when performing arithmetic operation={:02X} with operands " | ||
| 262 | "a={:016X}, b={:016X}!", | ||
| 263 | static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4)); | ||
| 264 | } | ||
| 265 | |||
| 266 | register_3 = function(register_3, cheat.ValueWidth(4)); | ||
| 267 | } | ||
| 268 | |||
| 269 | void CheatList::BeginConditionalInput(const Cheat& cheat) { | ||
| 270 | if (EvaluateConditional(cheat)) | ||
| 271 | return; | ||
| 272 | |||
| 273 | const auto iter = block_pairs.find(current_index); | ||
| 274 | ASSERT(iter != block_pairs.end()); | ||
| 275 | current_index = iter->second - 1; | ||
| 276 | } | ||
| 277 | |||
| 278 | VAddr CheatList::SanitizeAddress(VAddr in) const { | ||
| 279 | if ((in < main_region_begin || in >= main_region_end) && | ||
| 280 | (in < heap_region_begin || in >= heap_region_end)) { | ||
| 281 | LOG_ERROR(Common_Filesystem, | ||
| 282 | "Cheat attempting to access memory at invalid address={:016X}, if this persists, " | ||
| 283 | "the cheat may be incorrect. However, this may be normal early in execution if " | ||
| 284 | "the game has not properly set up yet.", | ||
| 285 | in); | ||
| 286 | return 0; ///< Invalid addresses will hard crash | ||
| 287 | } | ||
| 288 | |||
| 289 | return in; | ||
| 290 | } | ||
| 291 | |||
| 292 | void CheatList::ExecuteSingleCheat(const Cheat& cheat) { | ||
| 293 | using CheatOperationFunction = void (CheatList::*)(const Cheat&); | ||
| 294 | constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{ | ||
| 295 | &CheatList::WriteImmediate, &CheatList::BeginConditional, | ||
| 296 | &CheatList::EndConditional, &CheatList::Loop, | ||
| 297 | &CheatList::LoadImmediate, &CheatList::LoadIndexed, | ||
| 298 | &CheatList::StoreIndexed, &CheatList::RegisterArithmetic, | ||
| 299 | &CheatList::BeginConditionalInput, | ||
| 300 | }; | ||
| 301 | |||
| 302 | const auto index = static_cast<u8>(cheat.type.Value()); | ||
| 303 | ASSERT(index < sizeof(cheat_operation_functions)); | ||
| 304 | const auto op = cheat_operation_functions[index]; | ||
| 305 | (this->*op)(cheat); | ||
| 306 | } | ||
| 307 | |||
| 308 | void CheatList::ExecuteBlock(const Block& block) { | ||
| 309 | encountered_loops.clear(); | ||
| 310 | |||
| 311 | ProcessBlockPairs(block); | ||
| 312 | for (std::size_t i = 0; i < block.size(); ++i) { | ||
| 313 | current_index = i; | ||
| 314 | ExecuteSingleCheat(block[i]); | ||
| 315 | i = current_index; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | CheatParser::~CheatParser() = default; | ||
| 320 | |||
| 321 | CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master, | ||
| 322 | CheatList::ProgramSegment standard) const { | ||
| 323 | return {system, std::move(master), std::move(standard)}; | ||
| 324 | } | ||
| 325 | |||
| 326 | TextCheatParser::~TextCheatParser() = default; | ||
| 327 | |||
| 328 | CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const { | ||
| 329 | std::stringstream ss; | ||
| 330 | ss.write(reinterpret_cast<const char*>(data.data()), data.size()); | ||
| 331 | |||
| 332 | std::vector<std::string> lines; | ||
| 333 | std::string stream_line; | ||
| 334 | while (std::getline(ss, stream_line)) { | ||
| 335 | // Remove a trailing \r | ||
| 336 | if (!stream_line.empty() && stream_line.back() == '\r') | ||
| 337 | stream_line.pop_back(); | ||
| 338 | lines.push_back(std::move(stream_line)); | ||
| 339 | } | ||
| 340 | |||
| 341 | CheatList::ProgramSegment master_list; | ||
| 342 | CheatList::ProgramSegment standard_list; | ||
| 343 | |||
| 344 | for (std::size_t i = 0; i < lines.size(); ++i) { | ||
| 345 | auto line = lines[i]; | ||
| 346 | |||
| 347 | if (!line.empty() && (line[0] == '[' || line[0] == '{')) { | ||
| 348 | const auto master = line[0] == '{'; | ||
| 349 | const auto begin = master ? line.find('{') : line.find('['); | ||
| 350 | const auto end = master ? line.rfind('}') : line.rfind(']'); | ||
| 351 | |||
| 352 | ASSERT(begin != std::string::npos && end != std::string::npos); | ||
| 353 | |||
| 354 | const std::string patch_name{line.begin() + begin + 1, line.begin() + end}; | ||
| 355 | CheatList::Block block{}; | ||
| 356 | |||
| 357 | while (i < lines.size() - 1) { | ||
| 358 | line = lines[++i]; | ||
| 359 | if (!line.empty() && (line[0] == '[' || line[0] == '{')) { | ||
| 360 | --i; | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | |||
| 364 | if (line.size() < 8) | ||
| 365 | continue; | ||
| 366 | |||
| 367 | Cheat out{}; | ||
| 368 | out.raw = ParseSingleLineCheat(line); | ||
| 369 | block.push_back(out); | ||
| 370 | } | ||
| 371 | |||
| 372 | (master ? master_list : standard_list).emplace_back(patch_name, block); | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | return MakeCheatList(system, master_list, standard_list); | ||
| 377 | } | ||
| 378 | |||
| 379 | std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const { | ||
| 380 | std::array<u8, 16> out{}; | ||
| 381 | |||
| 382 | if (line.size() < 8) | ||
| 383 | return out; | ||
| 384 | |||
| 385 | const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8}); | ||
| 386 | std::memcpy(out.data(), word1.data(), sizeof(u32)); | ||
| 387 | |||
| 388 | if (line.size() < 17 || line[8] != ' ') | ||
| 389 | return out; | ||
| 390 | |||
| 391 | const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8}); | ||
| 392 | std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32)); | ||
| 393 | |||
| 394 | if (line.size() < 26 || line[17] != ' ') { | ||
| 395 | // Perform shifting in case value is truncated early. | ||
| 396 | const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); | ||
| 397 | if (type == CodeType::Loop || type == CodeType::LoadImmediate || | ||
| 398 | type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) { | ||
| 399 | std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32)); | ||
| 400 | std::memset(out.data() + 4, 0, sizeof(u32)); | ||
| 401 | } | ||
| 402 | |||
| 403 | return out; | ||
| 404 | } | ||
| 405 | |||
| 406 | const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8}); | ||
| 407 | std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32)); | ||
| 408 | |||
| 409 | if (line.size() < 35 || line[26] != ' ') { | ||
| 410 | // Perform shifting in case value is truncated early. | ||
| 411 | const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4); | ||
| 412 | if (type == CodeType::WriteImmediate || type == CodeType::Conditional) { | ||
| 413 | std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32)); | ||
| 414 | std::memset(out.data() + 8, 0, sizeof(u32)); | ||
| 415 | } | ||
| 416 | |||
| 417 | return out; | ||
| 418 | } | ||
| 419 | |||
| 420 | const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8}); | ||
| 421 | std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32)); | ||
| 422 | |||
| 423 | return out; | ||
| 424 | } | ||
| 425 | |||
| 426 | namespace { | ||
| 427 | u64 MemoryReadImpl(u32 width, VAddr addr) { | ||
| 428 | switch (width) { | ||
| 429 | case 1: | ||
| 430 | return Memory::Read8(addr); | ||
| 431 | case 2: | ||
| 432 | return Memory::Read16(addr); | ||
| 433 | case 4: | ||
| 434 | return Memory::Read32(addr); | ||
| 435 | case 8: | ||
| 436 | return Memory::Read64(addr); | ||
| 437 | default: | ||
| 438 | UNREACHABLE(); | ||
| 439 | return 0; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | void MemoryWriteImpl(u32 width, VAddr addr, u64 value) { | ||
| 444 | switch (width) { | ||
| 445 | case 1: | ||
| 446 | Memory::Write8(addr, static_cast<u8>(value)); | ||
| 447 | break; | ||
| 448 | case 2: | ||
| 449 | Memory::Write16(addr, static_cast<u16>(value)); | ||
| 450 | break; | ||
| 451 | case 4: | ||
| 452 | Memory::Write32(addr, static_cast<u32>(value)); | ||
| 453 | break; | ||
| 454 | case 8: | ||
| 455 | Memory::Write64(addr, value); | ||
| 456 | break; | ||
| 457 | default: | ||
| 458 | UNREACHABLE(); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | } // Anonymous namespace | ||
| 462 | |||
| 463 | CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_, | ||
| 464 | const std::string& build_id, VAddr code_region_start, | ||
| 465 | VAddr code_region_end) | ||
| 466 | : cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} { | ||
| 467 | event = core_timing.RegisterEvent( | ||
| 468 | "CheatEngine::FrameCallback::" + build_id, | ||
| 469 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | ||
| 470 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | ||
| 471 | |||
| 472 | const auto& vm_manager = system.CurrentProcess()->VMManager(); | ||
| 473 | for (auto& list : this->cheats) { | ||
| 474 | list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(), | ||
| 475 | code_region_end, vm_manager.GetHeapRegionEndAddress(), | ||
| 476 | &MemoryWriteImpl, &MemoryReadImpl); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | CheatEngine::~CheatEngine() { | ||
| 481 | core_timing.UnscheduleEvent(event, 0); | ||
| 482 | } | ||
| 483 | |||
| 484 | void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | ||
| 485 | for (auto& list : cheats) { | ||
| 486 | list.Execute(); | ||
| 487 | } | ||
| 488 | |||
| 489 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | ||
| 490 | } | ||
| 491 | |||
| 492 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/cheat_engine.h b/src/core/file_sys/cheat_engine.h deleted file mode 100644 index ac22a82cb..000000000 --- a/src/core/file_sys/cheat_engine.h +++ /dev/null | |||
| @@ -1,234 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <set> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Core::Timing { | ||
| 18 | class CoreTiming; | ||
| 19 | struct EventType; | ||
| 20 | } // namespace Core::Timing | ||
| 21 | |||
| 22 | namespace FileSys { | ||
| 23 | |||
| 24 | enum class CodeType : u32 { | ||
| 25 | // 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY | ||
| 26 | // Writes a T sized value Y to the address A added to the value of register R in memory domain M | ||
| 27 | WriteImmediate = 0, | ||
| 28 | |||
| 29 | // 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY | ||
| 30 | // Compares the T sized value Y to the value at address A in memory domain M using the | ||
| 31 | // conditional function C. If success, continues execution. If failure, jumps to the matching | ||
| 32 | // EndConditional statement. | ||
| 33 | Conditional = 1, | ||
| 34 | |||
| 35 | // 20000000 | ||
| 36 | // Terminates a Conditional or ConditionalInput block. | ||
| 37 | EndConditional = 2, | ||
| 38 | |||
| 39 | // 300R0000 VVVVVVVV | ||
| 40 | // Starts looping V times, storing the current count in register R. | ||
| 41 | // Loop block is terminated with a matching 310R0000. | ||
| 42 | Loop = 3, | ||
| 43 | |||
| 44 | // 400R0000 VVVVVVVV VVVVVVVV | ||
| 45 | // Sets the value of register R to the value V. | ||
| 46 | LoadImmediate = 4, | ||
| 47 | |||
| 48 | // 5TMRI0AA AAAAAAAA | ||
| 49 | // Sets the value of register R to the value of width T at address A in memory domain M, with | ||
| 50 | // the current value of R added to the address if I == 1. | ||
| 51 | LoadIndexed = 5, | ||
| 52 | |||
| 53 | // 6T0RIFG0 VVVVVVVV VVVVVVVV | ||
| 54 | // Writes the value V of width T to the memory address stored in register R. Adds the value of | ||
| 55 | // register G to the final calculation if F is nonzero. Increments the value of register R by T | ||
| 56 | // after operation if I is nonzero. | ||
| 57 | StoreIndexed = 6, | ||
| 58 | |||
| 59 | // 7T0RA000 VVVVVVVV | ||
| 60 | // Performs the arithmetic operation A on the value in register R and the value V of width T, | ||
| 61 | // storing the result in register R. | ||
| 62 | RegisterArithmetic = 7, | ||
| 63 | |||
| 64 | // 8KKKKKKK | ||
| 65 | // Checks to see if any of the buttons defined by the bitmask K are pressed. If any are, | ||
| 66 | // execution continues. If none are, execution skips to the next EndConditional command. | ||
| 67 | ConditionalInput = 8, | ||
| 68 | }; | ||
| 69 | |||
| 70 | enum class MemoryType : u32 { | ||
| 71 | // Addressed relative to start of main NSO | ||
| 72 | MainNSO = 0, | ||
| 73 | |||
| 74 | // Addressed relative to start of heap | ||
| 75 | Heap = 1, | ||
| 76 | }; | ||
| 77 | |||
| 78 | enum class ArithmeticOp : u32 { | ||
| 79 | Add = 0, | ||
| 80 | Sub = 1, | ||
| 81 | Mult = 2, | ||
| 82 | LShift = 3, | ||
| 83 | RShift = 4, | ||
| 84 | }; | ||
| 85 | |||
| 86 | enum class ComparisonOp : u32 { | ||
| 87 | GreaterThan = 1, | ||
| 88 | GreaterThanEqual = 2, | ||
| 89 | LessThan = 3, | ||
| 90 | LessThanEqual = 4, | ||
| 91 | Equal = 5, | ||
| 92 | Inequal = 6, | ||
| 93 | }; | ||
| 94 | |||
| 95 | union Cheat { | ||
| 96 | std::array<u8, 16> raw; | ||
| 97 | |||
| 98 | BitField<4, 4, CodeType> type; | ||
| 99 | BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes. | ||
| 100 | BitField<0, 4, u32> end_of_loop; | ||
| 101 | BitField<12, 4, MemoryType> memory_type; | ||
| 102 | BitField<8, 4, u32> register_3; | ||
| 103 | BitField<8, 4, ComparisonOp> comparison_op; | ||
| 104 | BitField<20, 4, u32> load_from_register; | ||
| 105 | BitField<20, 4, u32> increment_register; | ||
| 106 | BitField<20, 4, ArithmeticOp> arithmetic_op; | ||
| 107 | BitField<16, 4, u32> add_additional_register; | ||
| 108 | BitField<28, 4, u32> register_6; | ||
| 109 | |||
| 110 | u64 Address() const; | ||
| 111 | u64 ValueWidth(u64 offset) const; | ||
| 112 | u64 Value(u64 offset, u64 width) const; | ||
| 113 | u32 KeypadValue() const; | ||
| 114 | }; | ||
| 115 | |||
| 116 | class CheatParser; | ||
| 117 | |||
| 118 | // Represents a full collection of cheats for a game. The Execute function should be called every | ||
| 119 | // interval that all cheats should be executed. Clients should not directly instantiate this class | ||
| 120 | // (hence private constructor), they should instead receive an instance from CheatParser, which | ||
| 121 | // guarantees the list is always in an acceptable state. | ||
| 122 | class CheatList { | ||
| 123 | public: | ||
| 124 | friend class CheatParser; | ||
| 125 | |||
| 126 | using Block = std::vector<Cheat>; | ||
| 127 | using ProgramSegment = std::vector<std::pair<std::string, Block>>; | ||
| 128 | |||
| 129 | // (width in bytes, address, value) | ||
| 130 | using MemoryWriter = void (*)(u32, VAddr, u64); | ||
| 131 | // (width in bytes, address) -> value | ||
| 132 | using MemoryReader = u64 (*)(u32, VAddr); | ||
| 133 | |||
| 134 | void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end, | ||
| 135 | MemoryWriter writer, MemoryReader reader); | ||
| 136 | |||
| 137 | void Execute(); | ||
| 138 | |||
| 139 | private: | ||
| 140 | CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard); | ||
| 141 | |||
| 142 | void ProcessBlockPairs(const Block& block); | ||
| 143 | void ExecuteSingleCheat(const Cheat& cheat); | ||
| 144 | |||
| 145 | void ExecuteBlock(const Block& block); | ||
| 146 | |||
| 147 | bool EvaluateConditional(const Cheat& cheat) const; | ||
| 148 | |||
| 149 | // Individual cheat operations | ||
| 150 | void WriteImmediate(const Cheat& cheat); | ||
| 151 | void BeginConditional(const Cheat& cheat); | ||
| 152 | void EndConditional(const Cheat& cheat); | ||
| 153 | void Loop(const Cheat& cheat); | ||
| 154 | void LoadImmediate(const Cheat& cheat); | ||
| 155 | void LoadIndexed(const Cheat& cheat); | ||
| 156 | void StoreIndexed(const Cheat& cheat); | ||
| 157 | void RegisterArithmetic(const Cheat& cheat); | ||
| 158 | void BeginConditionalInput(const Cheat& cheat); | ||
| 159 | |||
| 160 | VAddr SanitizeAddress(VAddr in) const; | ||
| 161 | |||
| 162 | // Master Codes are defined as codes that cannot be disabled and are run prior to all | ||
| 163 | // others. | ||
| 164 | ProgramSegment master_list; | ||
| 165 | // All other codes | ||
| 166 | ProgramSegment standard_list; | ||
| 167 | |||
| 168 | bool in_standard = false; | ||
| 169 | |||
| 170 | // 16 (0x0-0xF) scratch registers that can be used by cheats | ||
| 171 | std::array<u64, 16> scratch{}; | ||
| 172 | |||
| 173 | MemoryWriter writer = nullptr; | ||
| 174 | MemoryReader reader = nullptr; | ||
| 175 | |||
| 176 | u64 main_region_begin{}; | ||
| 177 | u64 heap_region_begin{}; | ||
| 178 | u64 main_region_end{}; | ||
| 179 | u64 heap_region_end{}; | ||
| 180 | |||
| 181 | u64 current_block{}; | ||
| 182 | // The current index of the cheat within the current Block | ||
| 183 | u64 current_index{}; | ||
| 184 | |||
| 185 | // The 'stack' of the program. When a conditional or loop statement is encountered, its index is | ||
| 186 | // pushed onto this queue. When a end block is encountered, the condition is checked. | ||
| 187 | std::map<u64, u64> block_pairs; | ||
| 188 | |||
| 189 | std::set<u64> encountered_loops; | ||
| 190 | |||
| 191 | const Core::System* system; | ||
| 192 | }; | ||
| 193 | |||
| 194 | // Intermediary class that parses a text file or other disk format for storing cheats into a | ||
| 195 | // CheatList object, that can be used for execution. | ||
| 196 | class CheatParser { | ||
| 197 | public: | ||
| 198 | virtual ~CheatParser(); | ||
| 199 | |||
| 200 | virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0; | ||
| 201 | |||
| 202 | protected: | ||
| 203 | CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master, | ||
| 204 | CheatList::ProgramSegment standard) const; | ||
| 205 | }; | ||
| 206 | |||
| 207 | // CheatParser implementation that parses text files | ||
| 208 | class TextCheatParser final : public CheatParser { | ||
| 209 | public: | ||
| 210 | ~TextCheatParser() override; | ||
| 211 | |||
| 212 | CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override; | ||
| 213 | |||
| 214 | private: | ||
| 215 | std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const; | ||
| 216 | }; | ||
| 217 | |||
| 218 | // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming | ||
| 219 | class CheatEngine final { | ||
| 220 | public: | ||
| 221 | CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id, | ||
| 222 | VAddr code_region_start, VAddr code_region_end); | ||
| 223 | ~CheatEngine(); | ||
| 224 | |||
| 225 | private: | ||
| 226 | void FrameCallback(u64 userdata, s64 cycles_late); | ||
| 227 | |||
| 228 | std::vector<CheatList> cheats; | ||
| 229 | |||
| 230 | Core::Timing::EventType* event; | ||
| 231 | Core::Timing::CoreTiming& core_timing; | ||
| 232 | }; | ||
| 233 | |||
| 234 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index ce5c69b41..ea5c92f61 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const { | |||
| 528 | return header.title_id; | 528 | return header.title_id; |
| 529 | } | 529 | } |
| 530 | 530 | ||
| 531 | std::array<u8, 16> NCA::GetRightsId() const { | ||
| 532 | return header.rights_id; | ||
| 533 | } | ||
| 534 | |||
| 535 | u32 NCA::GetSDKVersion() const { | ||
| 536 | return header.sdk_version; | ||
| 537 | } | ||
| 538 | |||
| 531 | bool NCA::IsUpdate() const { | 539 | bool NCA::IsUpdate() const { |
| 532 | return is_update; | 540 | return is_update; |
| 533 | } | 541 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 15b9e6624..e249079b5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -112,6 +112,8 @@ public: | |||
| 112 | 112 | ||
| 113 | NCAContentType GetType() const; | 113 | NCAContentType GetType() const; |
| 114 | u64 GetTitleId() const; | 114 | u64 GetTitleId() const; |
| 115 | std::array<u8, 0x10> GetRightsId() const; | ||
| 116 | u32 GetSDKVersion() const; | ||
| 115 | bool IsUpdate() const; | 117 | bool IsUpdate() const; |
| 116 | 118 | ||
| 117 | VirtualFile GetRomFS() const; | 119 | VirtualFile GetRomFS() const; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index a8f80e2c6..df0ecb15c 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/service/filesystem/filesystem.h" | 22 | #include "core/hle/service/filesystem/filesystem.h" |
| 23 | #include "core/loader/loader.h" | 23 | #include "core/loader/loader.h" |
| 24 | #include "core/loader/nso.h" | 24 | #include "core/loader/nso.h" |
| 25 | #include "core/memory/cheat_engine.h" | ||
| 25 | #include "core/settings.h" | 26 | #include "core/settings.h" |
| 26 | 27 | ||
| 27 | namespace FileSys { | 28 | namespace FileSys { |
| @@ -63,7 +64,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 63 | 64 | ||
| 64 | if (Settings::values.dump_exefs) { | 65 | if (Settings::values.dump_exefs) { |
| 65 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | 66 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); |
| 66 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | 67 | const auto dump_dir = |
| 68 | Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||
| 67 | if (dump_dir != nullptr) { | 69 | if (dump_dir != nullptr) { |
| 68 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | 70 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); |
| 69 | VfsRawCopyD(exefs, exefs_dir); | 71 | VfsRawCopyD(exefs, exefs_dir); |
| @@ -88,7 +90,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | // LayeredExeFS | 92 | // LayeredExeFS |
| 91 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 93 | const auto load_dir = |
| 94 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 92 | if (load_dir != nullptr && load_dir->GetSize() > 0) { | 95 | if (load_dir != nullptr && load_dir->GetSize() > 0) { |
| 93 | auto patch_dirs = load_dir->GetSubdirectories(); | 96 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 94 | std::sort( | 97 | std::sort( |
| @@ -174,7 +177,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 174 | if (Settings::values.dump_nso) { | 177 | if (Settings::values.dump_nso) { |
| 175 | LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, | 178 | LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, |
| 176 | title_id); | 179 | title_id); |
| 177 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | 180 | const auto dump_dir = |
| 181 | Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||
| 178 | if (dump_dir != nullptr) { | 182 | if (dump_dir != nullptr) { |
| 179 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); | 183 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); |
| 180 | const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); | 184 | const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); |
| @@ -186,7 +190,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 186 | 190 | ||
| 187 | LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); | 191 | LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); |
| 188 | 192 | ||
| 189 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 193 | const auto load_dir = |
| 194 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 195 | if (load_dir == nullptr) { | ||
| 196 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 197 | return nso; | ||
| 198 | } | ||
| 199 | |||
| 190 | auto patch_dirs = load_dir->GetSubdirectories(); | 200 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 191 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 201 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 192 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 202 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -224,7 +234,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 224 | 234 | ||
| 225 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | 235 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); |
| 226 | 236 | ||
| 227 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 237 | const auto load_dir = |
| 238 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 239 | if (load_dir == nullptr) { | ||
| 240 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 241 | return false; | ||
| 242 | } | ||
| 243 | |||
| 228 | auto patch_dirs = load_dir->GetSubdirectories(); | 244 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 229 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 245 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 230 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 246 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -232,9 +248,10 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 232 | return !CollectPatches(patch_dirs, build_id).empty(); | 248 | return !CollectPatches(patch_dirs, build_id).empty(); |
| 233 | } | 249 | } |
| 234 | 250 | ||
| 235 | static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id, | 251 | namespace { |
| 236 | const std::array<u8, 0x20>& build_id_, | 252 | std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder( |
| 237 | const VirtualDir& base_path, bool upper) { | 253 | const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_, |
| 254 | const VirtualDir& base_path, bool upper) { | ||
| 238 | const auto build_id_raw = Common::HexToString(build_id_, upper); | 255 | const auto build_id_raw = Common::HexToString(build_id_, upper); |
| 239 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | 256 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); |
| 240 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | 257 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); |
| @@ -252,31 +269,39 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst | |||
| 252 | return std::nullopt; | 269 | return std::nullopt; |
| 253 | } | 270 | } |
| 254 | 271 | ||
| 255 | TextCheatParser parser; | 272 | Memory::TextCheatParser parser; |
| 256 | return parser.Parse(system, data); | 273 | return parser.Parse( |
| 274 | system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size())); | ||
| 257 | } | 275 | } |
| 258 | 276 | ||
| 259 | std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | 277 | } // Anonymous namespace |
| 260 | const std::array<u8, 32>& build_id_) const { | 278 | |
| 261 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 279 | std::vector<Memory::CheatEntry> PatchManager::CreateCheatList( |
| 280 | const Core::System& system, const std::array<u8, 32>& build_id_) const { | ||
| 281 | const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 282 | if (load_dir == nullptr) { | ||
| 283 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 284 | return {}; | ||
| 285 | } | ||
| 286 | |||
| 262 | auto patch_dirs = load_dir->GetSubdirectories(); | 287 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 263 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 288 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 264 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 289 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| 265 | 290 | ||
| 266 | std::vector<CheatList> out; | 291 | std::vector<Memory::CheatEntry> out; |
| 267 | out.reserve(patch_dirs.size()); | ||
| 268 | for (const auto& subdir : patch_dirs) { | 292 | for (const auto& subdir : patch_dirs) { |
| 269 | auto cheats_dir = subdir->GetSubdirectory("cheats"); | 293 | auto cheats_dir = subdir->GetSubdirectory("cheats"); |
| 270 | if (cheats_dir != nullptr) { | 294 | if (cheats_dir != nullptr) { |
| 271 | auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); | 295 | auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); |
| 272 | if (res.has_value()) { | 296 | if (res.has_value()) { |
| 273 | out.push_back(std::move(*res)); | 297 | std::copy(res->begin(), res->end(), std::back_inserter(out)); |
| 274 | continue; | 298 | continue; |
| 275 | } | 299 | } |
| 276 | 300 | ||
| 277 | res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); | 301 | res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); |
| 278 | if (res.has_value()) | 302 | if (res.has_value()) { |
| 279 | out.push_back(std::move(*res)); | 303 | std::copy(res->begin(), res->end(), std::back_inserter(out)); |
| 304 | } | ||
| 280 | } | 305 | } |
| 281 | } | 306 | } |
| 282 | 307 | ||
| @@ -284,7 +309,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | |||
| 284 | } | 309 | } |
| 285 | 310 | ||
| 286 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 311 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 287 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 312 | const auto load_dir = |
| 313 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 288 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || | 314 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || |
| 289 | load_dir == nullptr || load_dir->GetSize() <= 0) { | 315 | load_dir == nullptr || load_dir->GetSize() <= 0) { |
| 290 | return; | 316 | return; |
| @@ -393,6 +419,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | |||
| 393 | 419 | ||
| 394 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | 420 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( |
| 395 | VirtualFile update_raw) const { | 421 | VirtualFile update_raw) const { |
| 422 | if (title_id == 0) | ||
| 423 | return {}; | ||
| 396 | std::map<std::string, std::string, std::less<>> out; | 424 | std::map<std::string, std::string, std::less<>> out; |
| 397 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 425 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 398 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 426 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| @@ -423,7 +451,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 423 | } | 451 | } |
| 424 | 452 | ||
| 425 | // General Mods (LayeredFS and IPS) | 453 | // General Mods (LayeredFS and IPS) |
| 426 | const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 454 | const auto mod_dir = |
| 455 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 427 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { | 456 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { |
| 428 | for (const auto& mod : mod_dir->GetSubdirectories()) { | 457 | for (const auto& mod : mod_dir->GetSubdirectories()) { |
| 429 | std::string types; | 458 | std::string types; |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index a363c6577..e857e6e82 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -8,9 +8,9 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/cheat_engine.h" | ||
| 12 | #include "core/file_sys/nca_metadata.h" | 11 | #include "core/file_sys/nca_metadata.h" |
| 13 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 13 | #include "core/memory/dmnt_cheat_types.h" | ||
| 14 | 14 | ||
| 15 | namespace Core { | 15 | namespace Core { |
| 16 | class System; | 16 | class System; |
| @@ -51,8 +51,8 @@ public: | |||
| 51 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | 51 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; |
| 52 | 52 | ||
| 53 | // Creates a CheatList object with all | 53 | // Creates a CheatList object with all |
| 54 | std::vector<CheatList> CreateCheatList(const Core::System& system, | 54 | std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system, |
| 55 | const std::array<u8, 0x20>& build_id) const; | 55 | const std::array<u8, 0x20>& build_id) const; |
| 56 | 56 | ||
| 57 | // Currently tracked RomFS patches: | 57 | // Currently tracked RomFS patches: |
| 58 | // - Game Updates | 58 | // - Game Updates |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 3725b10f7..ac3fbd849 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <random> | ||
| 6 | #include <regex> | 7 | #include <regex> |
| 7 | #include <mbedtls/sha256.h> | 8 | #include <mbedtls/sha256.h> |
| 8 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| @@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) { | |||
| 48 | static bool FollowsNcaIdFormat(std::string_view name) { | 49 | static bool FollowsNcaIdFormat(std::string_view name) { |
| 49 | static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | | 50 | static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | |
| 50 | std::regex_constants::icase); | 51 | std::regex_constants::icase); |
| 51 | return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); | 52 | static const std::regex nca_id_cnmt_regex( |
| 53 | "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase); | ||
| 54 | return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) || | ||
| 55 | (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex)); | ||
| 52 | } | 56 | } |
| 53 | 57 | ||
| 54 | static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, | 58 | static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, |
| 55 | bool within_two_digit) { | 59 | bool within_two_digit, bool cnmt_suffix) { |
| 56 | if (!within_two_digit) { | 60 | if (!within_two_digit) |
| 57 | return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper)); | 61 | return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca", |
| 58 | } | 62 | Common::HexToString(nca_id, second_hex_upper)); |
| 59 | 63 | ||
| 60 | Core::Crypto::SHA256Hash hash{}; | 64 | Core::Crypto::SHA256Hash hash{}; |
| 61 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); | 65 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); |
| 62 | return fmt::format("/000000{:02X}/{}.nca", hash[0], | 66 | return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0], |
| 63 | Common::HexToString(nca_id, second_hex_upper)); | 67 | Common::HexToString(nca_id, second_hex_upper)); |
| 64 | } | 68 | } |
| 65 | 69 | ||
| @@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const { | |||
| 127 | return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); | 131 | return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); |
| 128 | } | 132 | } |
| 129 | 133 | ||
| 134 | PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} | ||
| 135 | |||
| 136 | bool PlaceholderCache::Create(const NcaID& id, u64 size) const { | ||
| 137 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 138 | |||
| 139 | if (dir->GetFileRelative(path) != nullptr) { | ||
| 140 | return false; | ||
| 141 | } | ||
| 142 | |||
| 143 | Core::Crypto::SHA256Hash hash{}; | ||
| 144 | mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||
| 145 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 146 | |||
| 147 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 148 | |||
| 149 | if (dir2 == nullptr) | ||
| 150 | return false; | ||
| 151 | |||
| 152 | const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 153 | |||
| 154 | if (file == nullptr) | ||
| 155 | return false; | ||
| 156 | |||
| 157 | return file->Resize(size); | ||
| 158 | } | ||
| 159 | |||
| 160 | bool PlaceholderCache::Delete(const NcaID& id) const { | ||
| 161 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 162 | |||
| 163 | if (dir->GetFileRelative(path) == nullptr) { | ||
| 164 | return false; | ||
| 165 | } | ||
| 166 | |||
| 167 | Core::Crypto::SHA256Hash hash{}; | ||
| 168 | mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||
| 169 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 170 | |||
| 171 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 172 | |||
| 173 | const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 174 | |||
| 175 | return res; | ||
| 176 | } | ||
| 177 | |||
| 178 | bool PlaceholderCache::Exists(const NcaID& id) const { | ||
| 179 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 180 | |||
| 181 | return dir->GetFileRelative(path) != nullptr; | ||
| 182 | } | ||
| 183 | |||
| 184 | bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const { | ||
| 185 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 186 | const auto file = dir->GetFileRelative(path); | ||
| 187 | |||
| 188 | if (file == nullptr) | ||
| 189 | return false; | ||
| 190 | |||
| 191 | return file->WriteBytes(data, offset) == data.size(); | ||
| 192 | } | ||
| 193 | |||
| 194 | bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder, | ||
| 195 | const NcaID& install) const { | ||
| 196 | const auto path = GetRelativePathFromNcaID(placeholder, false, true, false); | ||
| 197 | const auto file = dir->GetFileRelative(path); | ||
| 198 | |||
| 199 | if (file == nullptr) | ||
| 200 | return false; | ||
| 201 | |||
| 202 | const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install); | ||
| 203 | |||
| 204 | if (res != InstallResult::Success) | ||
| 205 | return false; | ||
| 206 | |||
| 207 | return Delete(placeholder); | ||
| 208 | } | ||
| 209 | |||
| 210 | bool PlaceholderCache::CleanAll() const { | ||
| 211 | return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName()); | ||
| 212 | } | ||
| 213 | |||
| 214 | std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const { | ||
| 215 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 216 | const auto file = dir->GetFileRelative(path); | ||
| 217 | |||
| 218 | if (file == nullptr) | ||
| 219 | return std::nullopt; | ||
| 220 | |||
| 221 | NCA nca{file}; | ||
| 222 | |||
| 223 | if (nca.GetStatus() != Loader::ResultStatus::Success && | ||
| 224 | nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 225 | return std::nullopt; | ||
| 226 | } | ||
| 227 | |||
| 228 | const auto rights_id = nca.GetRightsId(); | ||
| 229 | if (rights_id == NcaID{}) | ||
| 230 | return std::nullopt; | ||
| 231 | |||
| 232 | return rights_id; | ||
| 233 | } | ||
| 234 | |||
| 235 | u64 PlaceholderCache::Size(const NcaID& id) const { | ||
| 236 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 237 | const auto file = dir->GetFileRelative(path); | ||
| 238 | |||
| 239 | if (file == nullptr) | ||
| 240 | return 0; | ||
| 241 | |||
| 242 | return file->GetSize(); | ||
| 243 | } | ||
| 244 | |||
| 245 | bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const { | ||
| 246 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 247 | const auto file = dir->GetFileRelative(path); | ||
| 248 | |||
| 249 | if (file == nullptr) | ||
| 250 | return false; | ||
| 251 | |||
| 252 | return file->Resize(new_size); | ||
| 253 | } | ||
| 254 | |||
| 255 | std::vector<NcaID> PlaceholderCache::List() const { | ||
| 256 | std::vector<NcaID> out; | ||
| 257 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 258 | for (const auto& file : sdir->GetFiles()) { | ||
| 259 | const auto name = file->GetName(); | ||
| 260 | if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && | ||
| 261 | name[35] == 'a') { | ||
| 262 | out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | return out; | ||
| 267 | } | ||
| 268 | |||
| 269 | NcaID PlaceholderCache::Generate() { | ||
| 270 | std::random_device device; | ||
| 271 | std::mt19937 gen(device()); | ||
| 272 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||
| 273 | |||
| 274 | NcaID out{}; | ||
| 275 | |||
| 276 | const auto v1 = distribution(gen); | ||
| 277 | const auto v2 = distribution(gen); | ||
| 278 | std::memcpy(out.data(), &v1, sizeof(u64)); | ||
| 279 | std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64)); | ||
| 280 | |||
| 281 | return out; | ||
| 282 | } | ||
| 283 | |||
| 130 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | 284 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, |
| 131 | std::string_view path) const { | 285 | std::string_view path) const { |
| 132 | const auto file = dir->GetFileRelative(path); | 286 | const auto file = dir->GetFileRelative(path); |
| @@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | |||
| 169 | 323 | ||
| 170 | VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | 324 | VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { |
| 171 | VirtualFile file; | 325 | VirtualFile file; |
| 172 | // Try all four modes of file storage: | 326 | // Try all five relevant modes of file storage: |
| 173 | // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir) | 327 | // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix) |
| 174 | // 00: /000000**/{:032X}.nca | 328 | // 000: /000000**/{:032X}.nca |
| 175 | // 01: /{:032X}.nca | 329 | // 010: /{:032X}.nca |
| 176 | // 10: /000000**/{:032x}.nca | 330 | // 100: /000000**/{:032x}.nca |
| 177 | // 11: /{:032x}.nca | 331 | // 110: /{:032x}.nca |
| 178 | for (u8 i = 0; i < 4; ++i) { | 332 | // 111: /{:032x}.cnmt.nca |
| 179 | const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); | 333 | for (u8 i = 0; i < 8; ++i) { |
| 334 | if ((i % 2) == 1 && i != 7) | ||
| 335 | continue; | ||
| 336 | const auto path = | ||
| 337 | GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001); | ||
| 180 | file = OpenFileOrDirectoryConcat(dir, path); | 338 | file = OpenFileOrDirectoryConcat(dir, path); |
| 181 | if (file != nullptr) | 339 | if (file != nullptr) |
| 182 | return file; | 340 | return file; |
| @@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti | |||
| 472 | memcpy(id.data(), hash.data(), 16); | 630 | memcpy(id.data(), hash.data(), 16); |
| 473 | } | 631 | } |
| 474 | 632 | ||
| 475 | std::string path = GetRelativePathFromNcaID(id, false, true); | 633 | std::string path = GetRelativePathFromNcaID(id, false, true, false); |
| 476 | 634 | ||
| 477 | if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { | 635 | if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { |
| 478 | LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); | 636 | LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 4398d63e1..d1eec240e 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -25,6 +25,8 @@ enum class NCAContentType : u8; | |||
| 25 | enum class TitleType : u8; | 25 | enum class TitleType : u8; |
| 26 | 26 | ||
| 27 | struct ContentRecord; | 27 | struct ContentRecord; |
| 28 | struct MetaRecord; | ||
| 29 | class RegisteredCache; | ||
| 28 | 30 | ||
| 29 | using NcaID = std::array<u8, 0x10>; | 31 | using NcaID = std::array<u8, 0x10>; |
| 30 | using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | 32 | using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; |
| @@ -89,6 +91,27 @@ protected: | |||
| 89 | Core::Crypto::KeyManager keys; | 91 | Core::Crypto::KeyManager keys; |
| 90 | }; | 92 | }; |
| 91 | 93 | ||
| 94 | class PlaceholderCache { | ||
| 95 | public: | ||
| 96 | explicit PlaceholderCache(VirtualDir dir); | ||
| 97 | |||
| 98 | bool Create(const NcaID& id, u64 size) const; | ||
| 99 | bool Delete(const NcaID& id) const; | ||
| 100 | bool Exists(const NcaID& id) const; | ||
| 101 | bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const; | ||
| 102 | bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const; | ||
| 103 | bool CleanAll() const; | ||
| 104 | std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const; | ||
| 105 | u64 Size(const NcaID& id) const; | ||
| 106 | bool SetSize(const NcaID& id, u64 new_size) const; | ||
| 107 | std::vector<NcaID> List() const; | ||
| 108 | |||
| 109 | static NcaID Generate(); | ||
| 110 | |||
| 111 | private: | ||
| 112 | VirtualDir dir; | ||
| 113 | }; | ||
| 114 | |||
| 92 | /* | 115 | /* |
| 93 | * A class that catalogues NCAs in the registered directory structure. | 116 | * A class that catalogues NCAs in the registered directory structure. |
| 94 | * Nintendo's registered format follows this structure: | 117 | * Nintendo's registered format follows this structure: |
| @@ -103,6 +126,8 @@ protected: | |||
| 103 | * when 4GB splitting can be ignored.) | 126 | * when 4GB splitting can be ignored.) |
| 104 | */ | 127 | */ |
| 105 | class RegisteredCache : public ContentProvider { | 128 | class RegisteredCache : public ContentProvider { |
| 129 | friend class PlaceholderCache; | ||
| 130 | |||
| 106 | public: | 131 | public: |
| 107 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 132 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 108 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 133 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index b2ccb2926..84cd4684c 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/card_image.h" | ||
| 10 | #include "core/file_sys/content_archive.h" | 11 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/nca_metadata.h" | 12 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| @@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { | |||
| 34 | this->update_raw = std::move(update_raw); | 35 | this->update_raw = std::move(update_raw); |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | 38 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const { |
| 38 | if (!updatable) | 39 | if (!updatable) |
| 39 | return MakeResult<VirtualFile>(file); | 40 | return MakeResult<VirtualFile>(file); |
| 40 | 41 | ||
| @@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | |||
| 43 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); | 44 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { | 47 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, |
| 48 | ContentRecordType type) const { | ||
| 47 | std::shared_ptr<NCA> res; | 49 | std::shared_ptr<NCA> res; |
| 48 | 50 | ||
| 49 | switch (storage) { | 51 | switch (storage) { |
| @@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte | |||
| 51 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); | 53 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); |
| 52 | break; | 54 | break; |
| 53 | case StorageId::NandSystem: | 55 | case StorageId::NandSystem: |
| 54 | res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); | 56 | res = |
| 57 | Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry( | ||
| 58 | title_id, type); | ||
| 55 | break; | 59 | break; |
| 56 | case StorageId::NandUser: | 60 | case StorageId::NandUser: |
| 57 | res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); | 61 | res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry( |
| 62 | title_id, type); | ||
| 58 | break; | 63 | break; |
| 59 | case StorageId::SdCard: | 64 | case StorageId::SdCard: |
| 60 | res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); | 65 | res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry( |
| 66 | title_id, type); | ||
| 61 | break; | 67 | break; |
| 62 | default: | 68 | default: |
| 63 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); | 69 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 7724c0b23..da63a313a 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -33,8 +33,8 @@ public: | |||
| 33 | ~RomFSFactory(); | 33 | ~RomFSFactory(); |
| 34 | 34 | ||
| 35 | void SetPackedUpdate(VirtualFile update_raw); | 35 | void SetPackedUpdate(VirtualFile update_raw); |
| 36 | ResultVal<VirtualFile> OpenCurrentProcess(); | 36 | ResultVal<VirtualFile> OpenCurrentProcess() const; |
| 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); | 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; |
| 38 | 38 | ||
| 39 | private: | 39 | private: |
| 40 | VirtualFile file; | 40 | VirtualFile file; |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 7974b031d..f77cc02ac 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -15,22 +15,8 @@ namespace FileSys { | |||
| 15 | 15 | ||
| 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; |
| 17 | 17 | ||
| 18 | std::string SaveDataDescriptor::DebugInfo() const { | 18 | namespace { |
| 19 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " | 19 | void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { |
| 20 | "rank={}, index={}]", | ||
| 21 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||
| 22 | static_cast<u8>(rank), index); | ||
| 23 | } | ||
| 24 | |||
| 25 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { | ||
| 26 | // Delete all temporary storages | ||
| 27 | // On hardware, it is expected that temporary storage be empty at first use. | ||
| 28 | dir->DeleteSubdirectoryRecursive("temp"); | ||
| 29 | } | ||
| 30 | |||
| 31 | SaveDataFactory::~SaveDataFactory() = default; | ||
| 32 | |||
| 33 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) { | ||
| 34 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 20 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 35 | if (meta.zero_1 != 0) { | 21 | if (meta.zero_1 != 0) { |
| 36 | LOG_WARNING(Service_FS, | 22 | LOG_WARNING(Service_FS, |
| @@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat | |||
| 65 | "non-zero ({:016X}{:016X})", | 51 | "non-zero ({:016X}{:016X})", |
| 66 | meta.user_id[1], meta.user_id[0]); | 52 | meta.user_id[1], meta.user_id[0]); |
| 67 | } | 53 | } |
| 54 | } | ||
| 55 | } // Anonymous namespace | ||
| 68 | 56 | ||
| 69 | std::string save_directory = | 57 | std::string SaveDataDescriptor::DebugInfo() const { |
| 70 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 58 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " |
| 59 | "save_id={:016X}, " | ||
| 60 | "rank={}, index={}]", | ||
| 61 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||
| 62 | static_cast<u8>(rank), index); | ||
| 63 | } | ||
| 71 | 64 | ||
| 72 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. | 65 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { |
| 73 | // But, user_ids don't match so this works for now. | 66 | // Delete all temporary storages |
| 67 | // On hardware, it is expected that temporary storage be empty at first use. | ||
| 68 | dir->DeleteSubdirectoryRecursive("temp"); | ||
| 69 | } | ||
| 74 | 70 | ||
| 75 | auto out = dir->GetDirectoryRelative(save_directory); | 71 | SaveDataFactory::~SaveDataFactory() = default; |
| 72 | |||
| 73 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | ||
| 74 | const SaveDataDescriptor& meta) const { | ||
| 75 | PrintSaveDataDescriptorWarnings(meta); | ||
| 76 | |||
| 77 | const auto save_directory = | ||
| 78 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||
| 79 | |||
| 80 | auto out = dir->CreateDirectoryRelative(save_directory); | ||
| 76 | 81 | ||
| 82 | // Return an error if the save data doesn't actually exist. | ||
| 77 | if (out == nullptr) { | 83 | if (out == nullptr) { |
| 78 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not | 84 | // TODO(DarkLordZach): Find out correct error code. |
| 79 | // already exist. This is a hack, as we do not understand yet how this works on hardware. | 85 | return ResultCode(-1); |
| 80 | // Without a save data directory, many games will assert on boot. This should not have any | ||
| 81 | // bad side-effects. | ||
| 82 | out = dir->CreateDirectoryRelative(save_directory); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 88 | return MakeResult<VirtualDir>(std::move(out)); | ||
| 89 | } | ||
| 90 | |||
| 91 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, | ||
| 92 | const SaveDataDescriptor& meta) const { | ||
| 93 | |||
| 94 | const auto save_directory = | ||
| 95 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||
| 96 | |||
| 97 | auto out = dir->GetDirectoryRelative(save_directory); | ||
| 98 | |||
| 85 | // Return an error if the save data doesn't actually exist. | 99 | // Return an error if the save data doesn't actually exist. |
| 86 | if (out == nullptr) { | 100 | if (out == nullptr) { |
| 87 | // TODO(Subv): Find out correct error code. | 101 | // TODO(Subv): Find out correct error code. |
| @@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, | |||
| 152 | } | 166 | } |
| 153 | 167 | ||
| 154 | void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, | 168 | void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, |
| 155 | SaveDataSize new_value) { | 169 | SaveDataSize new_value) const { |
| 156 | const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); | 170 | const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); |
| 157 | const auto dir = GetOrCreateDirectoryRelative(this->dir, path); | 171 | const auto dir = GetOrCreateDirectoryRelative(this->dir, path); |
| 158 | 172 | ||
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index b73654571..991e57aa1 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -64,7 +64,8 @@ public: | |||
| 64 | explicit SaveDataFactory(VirtualDir dir); | 64 | explicit SaveDataFactory(VirtualDir dir); |
| 65 | ~SaveDataFactory(); | 65 | ~SaveDataFactory(); |
| 66 | 66 | ||
| 67 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); | 67 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; |
| 68 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | ||
| 68 | 69 | ||
| 69 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | 70 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; |
| 70 | 71 | ||
| @@ -73,7 +74,8 @@ public: | |||
| 73 | u128 user_id, u64 save_id); | 74 | u128 user_id, u64 save_id); |
| 74 | 75 | ||
| 75 | SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; | 76 | SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; |
| 76 | void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); | 77 | void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, |
| 78 | SaveDataSize new_value) const; | ||
| 77 | 79 | ||
| 78 | private: | 80 | private: |
| 79 | VirtualDir dir; | 81 | VirtualDir dir; |
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index bd3a57058..5113a1ca6 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/file_sys/registered_cache.h" | 6 | #include "core/file_sys/registered_cache.h" |
| 7 | #include "core/file_sys/sdmc_factory.h" | 7 | #include "core/file_sys/sdmc_factory.h" |
| 8 | #include "core/file_sys/xts_archive.h" | 8 | #include "core/file_sys/xts_archive.h" |
| 9 | #include "core/settings.h" | ||
| 9 | 10 | ||
| 10 | namespace FileSys { | 11 | namespace FileSys { |
| 11 | 12 | ||
| @@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_) | |||
| 14 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), | 15 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), |
| 15 | [](const VirtualFile& file, const NcaID& id) { | 16 | [](const VirtualFile& file, const NcaID& id) { |
| 16 | return NAX{file, id}.GetDecrypted(); | 17 | return NAX{file, id}.GetDecrypted(); |
| 17 | })) {} | 18 | })), |
| 19 | placeholder(std::make_unique<PlaceholderCache>( | ||
| 20 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} | ||
| 18 | 21 | ||
| 19 | SDMCFactory::~SDMCFactory() = default; | 22 | SDMCFactory::~SDMCFactory() = default; |
| 20 | 23 | ||
| 21 | ResultVal<VirtualDir> SDMCFactory::Open() { | 24 | ResultVal<VirtualDir> SDMCFactory::Open() const { |
| 22 | return MakeResult<VirtualDir>(dir); | 25 | return MakeResult<VirtualDir>(dir); |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 28 | VirtualDir SDMCFactory::GetSDMCContentDirectory() const { | ||
| 29 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); | ||
| 30 | } | ||
| 31 | |||
| 25 | RegisteredCache* SDMCFactory::GetSDMCContents() const { | 32 | RegisteredCache* SDMCFactory::GetSDMCContents() const { |
| 26 | return contents.get(); | 33 | return contents.get(); |
| 27 | } | 34 | } |
| 28 | 35 | ||
| 36 | PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const { | ||
| 37 | return placeholder.get(); | ||
| 38 | } | ||
| 39 | |||
| 40 | VirtualDir SDMCFactory::GetImageDirectory() const { | ||
| 41 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); | ||
| 42 | } | ||
| 43 | |||
| 44 | u64 SDMCFactory::GetSDMCFreeSpace() const { | ||
| 45 | return GetSDMCTotalSpace() - dir->GetSize(); | ||
| 46 | } | ||
| 47 | |||
| 48 | u64 SDMCFactory::GetSDMCTotalSpace() const { | ||
| 49 | return static_cast<u64>(Settings::values.sdmc_size); | ||
| 50 | } | ||
| 51 | |||
| 29 | } // namespace FileSys | 52 | } // namespace FileSys |
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 42794ba5b..42dc4e08a 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | class RegisteredCache; | 13 | class RegisteredCache; |
| 14 | class PlaceholderCache; | ||
| 14 | 15 | ||
| 15 | /// File system interface to the SDCard archive | 16 | /// File system interface to the SDCard archive |
| 16 | class SDMCFactory { | 17 | class SDMCFactory { |
| @@ -18,13 +19,23 @@ public: | |||
| 18 | explicit SDMCFactory(VirtualDir dir); | 19 | explicit SDMCFactory(VirtualDir dir); |
| 19 | ~SDMCFactory(); | 20 | ~SDMCFactory(); |
| 20 | 21 | ||
| 21 | ResultVal<VirtualDir> Open(); | 22 | ResultVal<VirtualDir> Open() const; |
| 23 | |||
| 24 | VirtualDir GetSDMCContentDirectory() const; | ||
| 25 | |||
| 22 | RegisteredCache* GetSDMCContents() const; | 26 | RegisteredCache* GetSDMCContents() const; |
| 27 | PlaceholderCache* GetSDMCPlaceholder() const; | ||
| 28 | |||
| 29 | VirtualDir GetImageDirectory() const; | ||
| 30 | |||
| 31 | u64 GetSDMCFreeSpace() const; | ||
| 32 | u64 GetSDMCTotalSpace() const; | ||
| 23 | 33 | ||
| 24 | private: | 34 | private: |
| 25 | VirtualDir dir; | 35 | VirtualDir dir; |
| 26 | 36 | ||
| 27 | std::unique_ptr<RegisteredCache> contents; | 37 | std::unique_ptr<RegisteredCache> contents; |
| 38 | std::unique_ptr<PlaceholderCache> placeholder; | ||
| 28 | }; | 39 | }; |
| 29 | 40 | ||
| 30 | } // namespace FileSys | 41 | } // namespace FileSys |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 8b3b14e25..ef3084681 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/file_sys/content_archive.h" | 14 | #include "core/file_sys/content_archive.h" |
| 15 | #include "core/file_sys/nca_metadata.h" | 15 | #include "core/file_sys/nca_metadata.h" |
| 16 | #include "core/file_sys/partition_filesystem.h" | 16 | #include "core/file_sys/partition_filesystem.h" |
| 17 | #include "core/file_sys/program_metadata.h" | ||
| 17 | #include "core/file_sys/submission_package.h" | 18 | #include "core/file_sys/submission_package.h" |
| 18 | #include "core/loader/loader.h" | 19 | #include "core/loader/loader.h" |
| 19 | 20 | ||
| @@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const { | |||
| 78 | } | 79 | } |
| 79 | 80 | ||
| 80 | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | 81 | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { |
| 82 | if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { | ||
| 83 | return Loader::ResultStatus::Success; | ||
| 84 | } | ||
| 85 | |||
| 81 | const auto iter = program_status.find(title_id); | 86 | const auto iter = program_status.find(title_id); |
| 82 | if (iter == program_status.end()) | 87 | if (iter == program_status.end()) |
| 83 | return Loader::ResultStatus::ErrorNSPMissingProgramNCA; | 88 | return Loader::ResultStatus::ErrorNSPMissingProgramNCA; |
| @@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | |||
| 85 | } | 90 | } |
| 86 | 91 | ||
| 87 | u64 NSP::GetFirstTitleID() const { | 92 | u64 NSP::GetFirstTitleID() const { |
| 93 | if (IsExtractedType()) { | ||
| 94 | return GetProgramTitleID(); | ||
| 95 | } | ||
| 96 | |||
| 88 | if (program_status.empty()) | 97 | if (program_status.empty()) |
| 89 | return 0; | 98 | return 0; |
| 90 | return program_status.begin()->first; | 99 | return program_status.begin()->first; |
| 91 | } | 100 | } |
| 92 | 101 | ||
| 93 | u64 NSP::GetProgramTitleID() const { | 102 | u64 NSP::GetProgramTitleID() const { |
| 103 | if (IsExtractedType()) { | ||
| 104 | if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | ProgramMetadata meta; | ||
| 109 | if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { | ||
| 110 | return meta.GetTitleID(); | ||
| 111 | } else { | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 94 | const auto out = GetFirstTitleID(); | 116 | const auto out = GetFirstTitleID(); |
| 95 | if ((out & 0x800) == 0) | 117 | if ((out & 0x800) == 0) |
| 96 | return out; | 118 | return out; |
| @@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const { | |||
| 102 | } | 124 | } |
| 103 | 125 | ||
| 104 | std::vector<u64> NSP::GetTitleIDs() const { | 126 | std::vector<u64> NSP::GetTitleIDs() const { |
| 127 | if (IsExtractedType()) { | ||
| 128 | return {GetProgramTitleID()}; | ||
| 129 | } | ||
| 130 | |||
| 105 | std::vector<u64> out; | 131 | std::vector<u64> out; |
| 106 | out.reserve(ncas.size()); | 132 | out.reserve(ncas.size()); |
| 107 | for (const auto& kv : ncas) | 133 | for (const auto& kv : ncas) |
| @@ -222,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | |||
| 222 | 248 | ||
| 223 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | 249 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { |
| 224 | for (const auto& outer_file : files) { | 250 | for (const auto& outer_file : files) { |
| 225 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | 251 | if (outer_file->GetName().size() < 9 || |
| 252 | outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||
| 226 | continue; | 253 | continue; |
| 227 | } | 254 | } |
| 228 | 255 | ||
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index a7c55e116..0c0f7ed6e 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -70,7 +70,7 @@ public: | |||
| 70 | 70 | ||
| 71 | protected: | 71 | protected: |
| 72 | void Get(Kernel::HLERequestContext& ctx) { | 72 | void Get(Kernel::HLERequestContext& ctx) { |
| 73 | LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); | 73 | LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); |
| 74 | ProfileBase profile_base{}; | 74 | ProfileBase profile_base{}; |
| 75 | ProfileData data{}; | 75 | ProfileData data{}; |
| 76 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { | 76 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { |
| @@ -89,7 +89,7 @@ protected: | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | void GetBase(Kernel::HLERequestContext& ctx) { | 91 | void GetBase(Kernel::HLERequestContext& ctx) { |
| 92 | LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); | 92 | LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); |
| 93 | ProfileBase profile_base{}; | 93 | ProfileBase profile_base{}; |
| 94 | if (profile_manager.GetProfileBase(user_id, profile_base)) { | 94 | if (profile_manager.GetProfileBase(user_id, profile_base)) { |
| 95 | IPC::ResponseBuilder rb{ctx, 16}; | 95 | IPC::ResponseBuilder rb{ctx, 16}; |
| @@ -263,7 +263,7 @@ private: | |||
| 263 | }; | 263 | }; |
| 264 | 264 | ||
| 265 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { | 265 | void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { |
| 266 | LOG_INFO(Service_ACC, "called"); | 266 | LOG_DEBUG(Service_ACC, "called"); |
| 267 | IPC::ResponseBuilder rb{ctx, 3}; | 267 | IPC::ResponseBuilder rb{ctx, 3}; |
| 268 | rb.Push(RESULT_SUCCESS); | 268 | rb.Push(RESULT_SUCCESS); |
| 269 | rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); | 269 | rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); |
| @@ -272,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { | |||
| 272 | void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { | 272 | void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { |
| 273 | IPC::RequestParser rp{ctx}; | 273 | IPC::RequestParser rp{ctx}; |
| 274 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | 274 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); |
| 275 | LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); | 275 | LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); |
| 276 | 276 | ||
| 277 | IPC::ResponseBuilder rb{ctx, 3}; | 277 | IPC::ResponseBuilder rb{ctx, 3}; |
| 278 | rb.Push(RESULT_SUCCESS); | 278 | rb.Push(RESULT_SUCCESS); |
| @@ -280,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { | |||
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { | 282 | void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { |
| 283 | LOG_INFO(Service_ACC, "called"); | 283 | LOG_DEBUG(Service_ACC, "called"); |
| 284 | ctx.WriteBuffer(profile_manager->GetAllUsers()); | 284 | ctx.WriteBuffer(profile_manager->GetAllUsers()); |
| 285 | IPC::ResponseBuilder rb{ctx, 2}; | 285 | IPC::ResponseBuilder rb{ctx, 2}; |
| 286 | rb.Push(RESULT_SUCCESS); | 286 | rb.Push(RESULT_SUCCESS); |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { | 289 | void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { |
| 290 | LOG_INFO(Service_ACC, "called"); | 290 | LOG_DEBUG(Service_ACC, "called"); |
| 291 | ctx.WriteBuffer(profile_manager->GetOpenUsers()); | 291 | ctx.WriteBuffer(profile_manager->GetOpenUsers()); |
| 292 | IPC::ResponseBuilder rb{ctx, 2}; | 292 | IPC::ResponseBuilder rb{ctx, 2}; |
| 293 | rb.Push(RESULT_SUCCESS); | 293 | rb.Push(RESULT_SUCCESS); |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { | 296 | void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { |
| 297 | LOG_INFO(Service_ACC, "called"); | 297 | LOG_DEBUG(Service_ACC, "called"); |
| 298 | IPC::ResponseBuilder rb{ctx, 6}; | 298 | IPC::ResponseBuilder rb{ctx, 6}; |
| 299 | rb.Push(RESULT_SUCCESS); | 299 | rb.Push(RESULT_SUCCESS); |
| 300 | rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); | 300 | rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index aa2c83937..797c9a06f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -232,12 +232,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { | |||
| 232 | 232 | ||
| 233 | IDebugFunctions::~IDebugFunctions() = default; | 233 | IDebugFunctions::~IDebugFunctions() = default; |
| 234 | 234 | ||
| 235 | ISelfController::ISelfController(Core::System& system_, | 235 | ISelfController::ISelfController(Core::System& system, |
| 236 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger_) | 236 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger) |
| 237 | : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { | 237 | : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) { |
| 238 | // clang-format off | 238 | // clang-format off |
| 239 | static const FunctionInfo functions[] = { | 239 | static const FunctionInfo functions[] = { |
| 240 | {0, nullptr, "Exit"}, | 240 | {0, &ISelfController::Exit, "Exit"}, |
| 241 | {1, &ISelfController::LockExit, "LockExit"}, | 241 | {1, &ISelfController::LockExit, "LockExit"}, |
| 242 | {2, &ISelfController::UnlockExit, "UnlockExit"}, | 242 | {2, &ISelfController::UnlockExit, "UnlockExit"}, |
| 243 | {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, | 243 | {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, |
| @@ -282,7 +282,7 @@ ISelfController::ISelfController(Core::System& system_, | |||
| 282 | 282 | ||
| 283 | RegisterHandlers(functions); | 283 | RegisterHandlers(functions); |
| 284 | 284 | ||
| 285 | auto& kernel = system_.Kernel(); | 285 | auto& kernel = system.Kernel(); |
| 286 | launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, | 286 | launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 287 | "ISelfController:LaunchableEvent"); | 287 | "ISelfController:LaunchableEvent"); |
| 288 | 288 | ||
| @@ -298,15 +298,28 @@ ISelfController::ISelfController(Core::System& system_, | |||
| 298 | 298 | ||
| 299 | ISelfController::~ISelfController() = default; | 299 | ISelfController::~ISelfController() = default; |
| 300 | 300 | ||
| 301 | void ISelfController::Exit(Kernel::HLERequestContext& ctx) { | ||
| 302 | LOG_DEBUG(Service_AM, "called"); | ||
| 303 | |||
| 304 | system.Shutdown(); | ||
| 305 | |||
| 306 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 307 | rb.Push(RESULT_SUCCESS); | ||
| 308 | } | ||
| 309 | |||
| 301 | void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { | 310 | void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { |
| 302 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 311 | LOG_DEBUG(Service_AM, "called"); |
| 312 | |||
| 313 | system.SetExitLock(true); | ||
| 303 | 314 | ||
| 304 | IPC::ResponseBuilder rb{ctx, 2}; | 315 | IPC::ResponseBuilder rb{ctx, 2}; |
| 305 | rb.Push(RESULT_SUCCESS); | 316 | rb.Push(RESULT_SUCCESS); |
| 306 | } | 317 | } |
| 307 | 318 | ||
| 308 | void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { | 319 | void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { |
| 309 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 320 | LOG_DEBUG(Service_AM, "called"); |
| 321 | |||
| 322 | system.SetExitLock(false); | ||
| 310 | 323 | ||
| 311 | IPC::ResponseBuilder rb{ctx, 2}; | 324 | IPC::ResponseBuilder rb{ctx, 2}; |
| 312 | rb.Push(RESULT_SUCCESS); | 325 | rb.Push(RESULT_SUCCESS); |
| @@ -550,6 +563,10 @@ void AppletMessageQueue::OperationModeChanged() { | |||
| 550 | on_operation_mode_changed.writable->Signal(); | 563 | on_operation_mode_changed.writable->Signal(); |
| 551 | } | 564 | } |
| 552 | 565 | ||
| 566 | void AppletMessageQueue::RequestExit() { | ||
| 567 | PushMessage(AppletMessage::ExitRequested); | ||
| 568 | } | ||
| 569 | |||
| 553 | ICommonStateGetter::ICommonStateGetter(Core::System& system, | 570 | ICommonStateGetter::ICommonStateGetter(Core::System& system, |
| 554 | std::shared_ptr<AppletMessageQueue> msg_queue) | 571 | std::shared_ptr<AppletMessageQueue> msg_queue) |
| 555 | : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { | 572 | : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { |
| @@ -1066,7 +1083,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1066 | 1083 | ||
| 1067 | RegisterHandlers(functions); | 1084 | RegisterHandlers(functions); |
| 1068 | 1085 | ||
| 1069 | auto& kernel = Core::System::GetInstance().Kernel(); | 1086 | auto& kernel = system.Kernel(); |
| 1070 | gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( | 1087 | gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( |
| 1071 | kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); | 1088 | kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); |
| 1072 | } | 1089 | } |
| @@ -1143,13 +1160,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( | |||
| 1143 | 1160 | ||
| 1144 | void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | 1161 | void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { |
| 1145 | IPC::RequestParser rp{ctx}; | 1162 | IPC::RequestParser rp{ctx}; |
| 1146 | u128 uid = rp.PopRaw<u128>(); // What does this do? | 1163 | u128 user_id = rp.PopRaw<u128>(); |
| 1147 | LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); | 1164 | |
| 1165 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); | ||
| 1166 | |||
| 1167 | FileSys::SaveDataDescriptor descriptor{}; | ||
| 1168 | descriptor.title_id = Core::CurrentProcess()->GetTitleID(); | ||
| 1169 | descriptor.user_id = user_id; | ||
| 1170 | descriptor.type = FileSys::SaveDataType::SaveData; | ||
| 1171 | const auto res = system.GetFileSystemController().CreateSaveData( | ||
| 1172 | FileSys::SaveDataSpaceId::NandUser, descriptor); | ||
| 1148 | 1173 | ||
| 1149 | IPC::ResponseBuilder rb{ctx, 4}; | 1174 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1150 | rb.Push(RESULT_SUCCESS); | 1175 | rb.Push(res.Code()); |
| 1151 | rb.Push<u64>(0); | 1176 | rb.Push<u64>(0); |
| 1152 | } // namespace Service::AM | 1177 | } |
| 1153 | 1178 | ||
| 1154 | void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { | 1179 | void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { |
| 1155 | // Takes an input u32 Result, no output. | 1180 | // Takes an input u32 Result, no output. |
| @@ -1261,8 +1286,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { | |||
| 1261 | "new_journal={:016X}", | 1286 | "new_journal={:016X}", |
| 1262 | static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); | 1287 | static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); |
| 1263 | 1288 | ||
| 1264 | const auto title_id = system.CurrentProcess()->GetTitleID(); | 1289 | system.GetFileSystemController().WriteSaveDataSize( |
| 1265 | FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); | 1290 | type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size}); |
| 1266 | 1291 | ||
| 1267 | IPC::ResponseBuilder rb{ctx, 4}; | 1292 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1268 | rb.Push(RESULT_SUCCESS); | 1293 | rb.Push(RESULT_SUCCESS); |
| @@ -1281,8 +1306,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { | |||
| 1281 | LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), | 1306 | LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), |
| 1282 | user_id[1], user_id[0]); | 1307 | user_id[1], user_id[0]); |
| 1283 | 1308 | ||
| 1284 | const auto title_id = system.CurrentProcess()->GetTitleID(); | 1309 | const auto size = system.GetFileSystemController().ReadSaveDataSize( |
| 1285 | const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); | 1310 | type, system.CurrentProcess()->GetTitleID(), user_id); |
| 1286 | 1311 | ||
| 1287 | IPC::ResponseBuilder rb{ctx, 6}; | 1312 | IPC::ResponseBuilder rb{ctx, 6}; |
| 1288 | rb.Push(RESULT_SUCCESS); | 1313 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 28f870302..a3baeb673 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -45,6 +45,7 @@ class AppletMessageQueue { | |||
| 45 | public: | 45 | public: |
| 46 | enum class AppletMessage : u32 { | 46 | enum class AppletMessage : u32 { |
| 47 | NoMessage = 0, | 47 | NoMessage = 0, |
| 48 | ExitRequested = 4, | ||
| 48 | FocusStateChanged = 15, | 49 | FocusStateChanged = 15, |
| 49 | OperationModeChanged = 30, | 50 | OperationModeChanged = 30, |
| 50 | PerformanceModeChanged = 31, | 51 | PerformanceModeChanged = 31, |
| @@ -59,6 +60,7 @@ public: | |||
| 59 | AppletMessage PopMessage(); | 60 | AppletMessage PopMessage(); |
| 60 | std::size_t GetMessageCount() const; | 61 | std::size_t GetMessageCount() const; |
| 61 | void OperationModeChanged(); | 62 | void OperationModeChanged(); |
| 63 | void RequestExit(); | ||
| 62 | 64 | ||
| 63 | private: | 65 | private: |
| 64 | std::queue<AppletMessage> messages; | 66 | std::queue<AppletMessage> messages; |
| @@ -123,6 +125,7 @@ public: | |||
| 123 | ~ISelfController() override; | 125 | ~ISelfController() override; |
| 124 | 126 | ||
| 125 | private: | 127 | private: |
| 128 | void Exit(Kernel::HLERequestContext& ctx); | ||
| 126 | void LockExit(Kernel::HLERequestContext& ctx); | 129 | void LockExit(Kernel::HLERequestContext& ctx); |
| 127 | void UnlockExit(Kernel::HLERequestContext& ctx); | 130 | void UnlockExit(Kernel::HLERequestContext& ctx); |
| 128 | void EnterFatalSection(Kernel::HLERequestContext& ctx); | 131 | void EnterFatalSection(Kernel::HLERequestContext& ctx); |
| @@ -151,6 +154,8 @@ private: | |||
| 151 | u32 idle_time_detection_extension = 0; | 154 | u32 idle_time_detection_extension = 0; |
| 152 | u64 num_fatal_sections_entered = 0; | 155 | u64 num_fatal_sections_entered = 0; |
| 153 | bool is_auto_sleep_disabled = false; | 156 | bool is_auto_sleep_disabled = false; |
| 157 | |||
| 158 | Core::System& system; | ||
| 154 | }; | 159 | }; |
| 155 | 160 | ||
| 156 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 161 | 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 @@ | |||
| 9 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 10 | 10 | ||
| 11 | namespace Service { | 11 | namespace Service { |
| 12 | namespace FileSystem { | ||
| 13 | class FileSystemController; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace NVFlinger { | 16 | namespace NVFlinger { |
| 13 | class NVFlinger; | 17 | class NVFlinger; |
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | namespace AM { | 20 | namespace AM { |
| 17 | 21 | ||
| 22 | class AppletMessageQueue; | ||
| 23 | |||
| 18 | class AppletAE final : public ServiceFramework<AppletAE> { | 24 | class AppletAE final : public ServiceFramework<AppletAE> { |
| 19 | public: | 25 | public: |
| 20 | explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, | 26 | explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, |
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 22c05419d..758da792d 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h | |||
| @@ -9,12 +9,18 @@ | |||
| 9 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 10 | 10 | ||
| 11 | namespace Service { | 11 | namespace Service { |
| 12 | namespace FileSystem { | ||
| 13 | class FileSystemController; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace NVFlinger { | 16 | namespace NVFlinger { |
| 13 | class NVFlinger; | 17 | class NVFlinger; |
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | namespace AM { | 20 | namespace AM { |
| 17 | 21 | ||
| 22 | class AppletMessageQueue; | ||
| 23 | |||
| 18 | class AppletOE final : public ServiceFramework<AppletOE> { | 24 | class AppletOE final : public ServiceFramework<AppletOE> { |
| 19 | public: | 25 | public: |
| 20 | explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, | 26 | explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index d3e97776b..e9cf1e840 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -29,9 +29,9 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { | |||
| 29 | return (title_id & DLC_BASE_TITLE_ID_MASK) == base; | 29 | return (title_id & DLC_BASE_TITLE_ID_MASK) == base; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | static std::vector<u64> AccumulateAOCTitleIDs() { | 32 | static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) { |
| 33 | std::vector<u64> add_on_content; | 33 | std::vector<u64> add_on_content; |
| 34 | const auto& rcu = Core::System::GetInstance().GetContentProvider(); | 34 | const auto& rcu = system.GetContentProvider(); |
| 35 | const auto list = | 35 | const auto list = |
| 36 | rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 36 | rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); |
| 37 | std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), | 37 | std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), |
| @@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() { | |||
| 47 | return add_on_content; | 47 | return add_on_content; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { | 50 | AOC_U::AOC_U(Core::System& system) |
| 51 | : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) { | ||
| 51 | // clang-format off | 52 | // clang-format off |
| 52 | static const FunctionInfo functions[] = { | 53 | static const FunctionInfo functions[] = { |
| 53 | {0, nullptr, "CountAddOnContentByApplicationId"}, | 54 | {0, nullptr, "CountAddOnContentByApplicationId"}, |
| @@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs | |||
| 65 | 66 | ||
| 66 | RegisterHandlers(functions); | 67 | RegisterHandlers(functions); |
| 67 | 68 | ||
| 68 | auto& kernel = Core::System::GetInstance().Kernel(); | 69 | auto& kernel = system.Kernel(); |
| 69 | aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, | 70 | aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 70 | "GetAddOnContentListChanged:Event"); | 71 | "GetAddOnContentListChanged:Event"); |
| 71 | } | 72 | } |
| @@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 86 | IPC::ResponseBuilder rb{ctx, 3}; | 87 | IPC::ResponseBuilder rb{ctx, 3}; |
| 87 | rb.Push(RESULT_SUCCESS); | 88 | rb.Push(RESULT_SUCCESS); |
| 88 | 89 | ||
| 89 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 90 | const auto current = system.CurrentProcess()->GetTitleID(); |
| 90 | 91 | ||
| 91 | const auto& disabled = Settings::values.disabled_addons[current]; | 92 | const auto& disabled = Settings::values.disabled_addons[current]; |
| 92 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { | 93 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { |
| @@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 113 | LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, | 114 | LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, |
| 114 | process_id); | 115 | process_id); |
| 115 | 116 | ||
| 116 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 117 | const auto current = system.CurrentProcess()->GetTitleID(); |
| 117 | 118 | ||
| 118 | std::vector<u32> out; | 119 | std::vector<u32> out; |
| 119 | const auto& disabled = Settings::values.disabled_addons[current]; | 120 | const auto& disabled = Settings::values.disabled_addons[current]; |
| @@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { | |||
| 159 | IPC::ResponseBuilder rb{ctx, 4}; | 160 | IPC::ResponseBuilder rb{ctx, 4}; |
| 160 | rb.Push(RESULT_SUCCESS); | 161 | rb.Push(RESULT_SUCCESS); |
| 161 | 162 | ||
| 162 | const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 163 | const auto title_id = system.CurrentProcess()->GetTitleID(); |
| 163 | FileSys::PatchManager pm{title_id}; | 164 | FileSys::PatchManager pm{title_id}; |
| 164 | 165 | ||
| 165 | const auto res = pm.GetControlMetadata(); | 166 | const auto res = pm.GetControlMetadata(); |
| @@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { | |||
| 196 | rb.PushCopyObjects(aoc_change_event.readable); | 197 | rb.PushCopyObjects(aoc_change_event.readable); |
| 197 | } | 198 | } |
| 198 | 199 | ||
| 199 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 200 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 200 | std::make_shared<AOC_U>()->InstallAsService(service_manager); | 201 | std::make_shared<AOC_U>(system)->InstallAsService(service_manager); |
| 201 | } | 202 | } |
| 202 | 203 | ||
| 203 | } // namespace Service::AOC | 204 | } // namespace Service::AOC |
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 5effea730..848b2f416 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h | |||
| @@ -14,7 +14,7 @@ namespace Service::AOC { | |||
| 14 | 14 | ||
| 15 | class AOC_U final : public ServiceFramework<AOC_U> { | 15 | class AOC_U final : public ServiceFramework<AOC_U> { |
| 16 | public: | 16 | public: |
| 17 | AOC_U(); | 17 | explicit AOC_U(Core::System& system); |
| 18 | ~AOC_U() override; | 18 | ~AOC_U() override; |
| 19 | 19 | ||
| 20 | private: | 20 | private: |
| @@ -26,9 +26,10 @@ private: | |||
| 26 | 26 | ||
| 27 | std::vector<u64> add_on_content; | 27 | std::vector<u64> add_on_content; |
| 28 | Kernel::EventPair aoc_change_event; | 28 | Kernel::EventPair aoc_change_event; |
| 29 | Core::System& system; | ||
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | /// Registers all AOC services with the specified service manager. | 32 | /// Registers all AOC services with the specified service manager. |
| 32 | void InstallInterfaces(SM::ServiceManager& service_manager); | 33 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 33 | 34 | ||
| 34 | } // namespace Service::AOC | 35 | } // namespace Service::AOC |
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 3c7ca2c44..afce581e5 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp | |||
| @@ -16,7 +16,7 @@ namespace Service::BtDrv { | |||
| 16 | 16 | ||
| 17 | class Bt final : public ServiceFramework<Bt> { | 17 | class Bt final : public ServiceFramework<Bt> { |
| 18 | public: | 18 | public: |
| 19 | explicit Bt() : ServiceFramework{"bt"} { | 19 | explicit Bt(Core::System& system) : ServiceFramework{"bt"} { |
| 20 | // clang-format off | 20 | // clang-format off |
| 21 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 22 | {0, nullptr, "LeClientReadCharacteristic"}, | 22 | {0, nullptr, "LeClientReadCharacteristic"}, |
| @@ -33,7 +33,7 @@ public: | |||
| 33 | // clang-format on | 33 | // clang-format on |
| 34 | RegisterHandlers(functions); | 34 | RegisterHandlers(functions); |
| 35 | 35 | ||
| 36 | auto& kernel = Core::System::GetInstance().Kernel(); | 36 | auto& kernel = system.Kernel(); |
| 37 | register_event = Kernel::WritableEvent::CreateEventPair( | 37 | register_event = Kernel::WritableEvent::CreateEventPair( |
| 38 | kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); | 38 | kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); |
| 39 | } | 39 | } |
| @@ -163,9 +163,9 @@ public: | |||
| 163 | } | 163 | } |
| 164 | }; | 164 | }; |
| 165 | 165 | ||
| 166 | void InstallInterfaces(SM::ServiceManager& sm) { | 166 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { |
| 167 | std::make_shared<BtDrv>()->InstallAsService(sm); | 167 | std::make_shared<BtDrv>()->InstallAsService(sm); |
| 168 | std::make_shared<Bt>()->InstallAsService(sm); | 168 | std::make_shared<Bt>(system)->InstallAsService(sm); |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | } // namespace Service::BtDrv | 171 | } // namespace Service::BtDrv |
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h index 164e56f43..191410dbc 100644 --- a/src/core/hle/service/btdrv/btdrv.h +++ b/src/core/hle/service/btdrv/btdrv.h | |||
| @@ -8,9 +8,13 @@ namespace Service::SM { | |||
| 8 | class ServiceManager; | 8 | class ServiceManager; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Service::BtDrv { | 15 | namespace Service::BtDrv { |
| 12 | 16 | ||
| 13 | /// Registers all BtDrv services with the specified service manager. | 17 | /// Registers all BtDrv services with the specified service manager. |
| 14 | void InstallInterfaces(SM::ServiceManager& sm); | 18 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); |
| 15 | 19 | ||
| 16 | } // namespace Service::BtDrv | 20 | } // namespace Service::BtDrv |
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index b439ee7ec..920fc6ff7 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp | |||
| @@ -17,7 +17,7 @@ namespace Service::BTM { | |||
| 17 | 17 | ||
| 18 | class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { | 18 | class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { |
| 19 | public: | 19 | public: |
| 20 | explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { | 20 | explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} { |
| 21 | // clang-format off | 21 | // clang-format off |
| 22 | static const FunctionInfo functions[] = { | 22 | static const FunctionInfo functions[] = { |
| 23 | {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, | 23 | {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, |
| @@ -56,7 +56,7 @@ public: | |||
| 56 | // clang-format on | 56 | // clang-format on |
| 57 | RegisterHandlers(functions); | 57 | RegisterHandlers(functions); |
| 58 | 58 | ||
| 59 | auto& kernel = Core::System::GetInstance().Kernel(); | 59 | auto& kernel = system.Kernel(); |
| 60 | scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, | 60 | scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 61 | "IBtmUserCore:ScanEvent"); | 61 | "IBtmUserCore:ScanEvent"); |
| 62 | connection_event = Kernel::WritableEvent::CreateEventPair( | 62 | connection_event = Kernel::WritableEvent::CreateEventPair( |
| @@ -108,7 +108,7 @@ private: | |||
| 108 | 108 | ||
| 109 | class BTM_USR final : public ServiceFramework<BTM_USR> { | 109 | class BTM_USR final : public ServiceFramework<BTM_USR> { |
| 110 | public: | 110 | public: |
| 111 | explicit BTM_USR() : ServiceFramework{"btm:u"} { | 111 | explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) { |
| 112 | // clang-format off | 112 | // clang-format off |
| 113 | static const FunctionInfo functions[] = { | 113 | static const FunctionInfo functions[] = { |
| 114 | {0, &BTM_USR::GetCore, "GetCore"}, | 114 | {0, &BTM_USR::GetCore, "GetCore"}, |
| @@ -123,8 +123,10 @@ private: | |||
| 123 | 123 | ||
| 124 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 124 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 125 | rb.Push(RESULT_SUCCESS); | 125 | rb.Push(RESULT_SUCCESS); |
| 126 | rb.PushIpcInterface<IBtmUserCore>(); | 126 | rb.PushIpcInterface<IBtmUserCore>(system); |
| 127 | } | 127 | } |
| 128 | |||
| 129 | Core::System& system; | ||
| 128 | }; | 130 | }; |
| 129 | 131 | ||
| 130 | class BTM final : public ServiceFramework<BTM> { | 132 | class BTM final : public ServiceFramework<BTM> { |
| @@ -268,11 +270,11 @@ private: | |||
| 268 | } | 270 | } |
| 269 | }; | 271 | }; |
| 270 | 272 | ||
| 271 | void InstallInterfaces(SM::ServiceManager& sm) { | 273 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { |
| 272 | std::make_shared<BTM>()->InstallAsService(sm); | 274 | std::make_shared<BTM>()->InstallAsService(sm); |
| 273 | std::make_shared<BTM_DBG>()->InstallAsService(sm); | 275 | std::make_shared<BTM_DBG>()->InstallAsService(sm); |
| 274 | std::make_shared<BTM_SYS>()->InstallAsService(sm); | 276 | std::make_shared<BTM_SYS>()->InstallAsService(sm); |
| 275 | std::make_shared<BTM_USR>()->InstallAsService(sm); | 277 | std::make_shared<BTM_USR>(system)->InstallAsService(sm); |
| 276 | } | 278 | } |
| 277 | 279 | ||
| 278 | } // namespace Service::BTM | 280 | } // namespace Service::BTM |
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index e6425a7e3..c6b878043 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h | |||
| @@ -8,8 +8,12 @@ namespace Service::SM { | |||
| 8 | class ServiceManager; | 8 | class ServiceManager; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | }; | ||
| 14 | |||
| 11 | namespace Service::BTM { | 15 | namespace Service::BTM { |
| 12 | 16 | ||
| 13 | void InstallInterfaces(SM::ServiceManager& sm); | 17 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); |
| 14 | 18 | ||
| 15 | } // namespace Service::BTM | 19 | } // namespace Service::BTM |
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 01fa06ad3..b2ebf6240 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -20,8 +20,8 @@ | |||
| 20 | 20 | ||
| 21 | namespace Service::Fatal { | 21 | namespace Service::Fatal { |
| 22 | 22 | ||
| 23 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 23 | Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) |
| 24 | : ServiceFramework(name), module(std::move(module)) {} | 24 | : ServiceFramework(name), module(std::move(module)), system(system) {} |
| 25 | 25 | ||
| 26 | Module::Interface::~Interface() = default; | 26 | Module::Interface::~Interface() = default; |
| 27 | 27 | ||
| @@ -64,7 +64,8 @@ enum class FatalType : u32 { | |||
| 64 | ErrorScreen = 2, | 64 | ErrorScreen = 2, |
| 65 | }; | 65 | }; |
| 66 | 66 | ||
| 67 | static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { | 67 | static void GenerateErrorReport(Core::System& system, ResultCode error_code, |
| 68 | const FatalInfo& info) { | ||
| 68 | const auto title_id = Core::CurrentProcess()->GetTitleID(); | 69 | const auto title_id = Core::CurrentProcess()->GetTitleID(); |
| 69 | std::string crash_report = fmt::format( | 70 | std::string crash_report = fmt::format( |
| 70 | "Yuzu {}-{} crash report\n" | 71 | "Yuzu {}-{} crash report\n" |
| @@ -101,18 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { | |||
| 101 | 102 | ||
| 102 | LOG_ERROR(Service_Fatal, "{}", crash_report); | 103 | LOG_ERROR(Service_Fatal, "{}", crash_report); |
| 103 | 104 | ||
| 104 | Core::System::GetInstance().GetReporter().SaveCrashReport( | 105 | system.GetReporter().SaveCrashReport( |
| 105 | title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, | 106 | title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, |
| 106 | info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, | 107 | info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, |
| 107 | info.backtrace_size, info.ArchAsString(), info.unk10); | 108 | info.backtrace_size, info.ArchAsString(), info.unk10); |
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { | 111 | static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type, |
| 112 | const FatalInfo& info) { | ||
| 111 | LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", | 113 | LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", |
| 112 | static_cast<u32>(fatal_type), error_code.raw); | 114 | static_cast<u32>(fatal_type), error_code.raw); |
| 113 | switch (fatal_type) { | 115 | switch (fatal_type) { |
| 114 | case FatalType::ErrorReportAndScreen: | 116 | case FatalType::ErrorReportAndScreen: |
| 115 | GenerateErrorReport(error_code, info); | 117 | GenerateErrorReport(system, error_code, info); |
| 116 | [[fallthrough]]; | 118 | [[fallthrough]]; |
| 117 | case FatalType::ErrorScreen: | 119 | case FatalType::ErrorScreen: |
| 118 | // Since we have no fatal:u error screen. We should just kill execution instead | 120 | // Since we have no fatal:u error screen. We should just kill execution instead |
| @@ -120,7 +122,7 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F | |||
| 120 | break; | 122 | break; |
| 121 | // Should not throw a fatal screen but should generate an error report | 123 | // Should not throw a fatal screen but should generate an error report |
| 122 | case FatalType::ErrorReport: | 124 | case FatalType::ErrorReport: |
| 123 | GenerateErrorReport(error_code, info); | 125 | GenerateErrorReport(system, error_code, info); |
| 124 | break; | 126 | break; |
| 125 | } | 127 | } |
| 126 | } | 128 | } |
| @@ -130,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { | |||
| 130 | IPC::RequestParser rp{ctx}; | 132 | IPC::RequestParser rp{ctx}; |
| 131 | const auto error_code = rp.Pop<ResultCode>(); | 133 | const auto error_code = rp.Pop<ResultCode>(); |
| 132 | 134 | ||
| 133 | ThrowFatalError(error_code, FatalType::ErrorScreen, {}); | 135 | ThrowFatalError(system, error_code, FatalType::ErrorScreen, {}); |
| 134 | IPC::ResponseBuilder rb{ctx, 2}; | 136 | IPC::ResponseBuilder rb{ctx, 2}; |
| 135 | rb.Push(RESULT_SUCCESS); | 137 | rb.Push(RESULT_SUCCESS); |
| 136 | } | 138 | } |
| @@ -141,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { | |||
| 141 | const auto error_code = rp.Pop<ResultCode>(); | 143 | const auto error_code = rp.Pop<ResultCode>(); |
| 142 | const auto fatal_type = rp.PopEnum<FatalType>(); | 144 | const auto fatal_type = rp.PopEnum<FatalType>(); |
| 143 | 145 | ||
| 144 | ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy | 146 | ThrowFatalError(system, error_code, fatal_type, |
| 147 | {}); // No info is passed with ThrowFatalWithPolicy | ||
| 145 | IPC::ResponseBuilder rb{ctx, 2}; | 148 | IPC::ResponseBuilder rb{ctx, 2}; |
| 146 | rb.Push(RESULT_SUCCESS); | 149 | rb.Push(RESULT_SUCCESS); |
| 147 | } | 150 | } |
| @@ -157,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) | |||
| 157 | ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); | 160 | ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); |
| 158 | std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); | 161 | std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); |
| 159 | 162 | ||
| 160 | ThrowFatalError(error_code, fatal_type, info); | 163 | ThrowFatalError(system, error_code, fatal_type, info); |
| 161 | IPC::ResponseBuilder rb{ctx, 2}; | 164 | IPC::ResponseBuilder rb{ctx, 2}; |
| 162 | rb.Push(RESULT_SUCCESS); | 165 | rb.Push(RESULT_SUCCESS); |
| 163 | } | 166 | } |
| 164 | 167 | ||
| 165 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 168 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 166 | auto module = std::make_shared<Module>(); | 169 | auto module = std::make_shared<Module>(); |
| 167 | std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); | 170 | std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager); |
| 168 | std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); | 171 | std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager); |
| 169 | } | 172 | } |
| 170 | 173 | ||
| 171 | } // namespace Service::Fatal | 174 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 09371ff7f..bd9339dfc 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h | |||
| @@ -6,13 +6,17 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Service::Fatal { | 13 | namespace Service::Fatal { |
| 10 | 14 | ||
| 11 | class Module final { | 15 | class Module final { |
| 12 | public: | 16 | public: |
| 13 | class Interface : public ServiceFramework<Interface> { | 17 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 18 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 19 | explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); |
| 16 | ~Interface() override; | 20 | ~Interface() override; |
| 17 | 21 | ||
| 18 | void ThrowFatal(Kernel::HLERequestContext& ctx); | 22 | void ThrowFatal(Kernel::HLERequestContext& ctx); |
| @@ -21,9 +25,10 @@ public: | |||
| 21 | 25 | ||
| 22 | protected: | 26 | protected: |
| 23 | std::shared_ptr<Module> module; | 27 | std::shared_ptr<Module> module; |
| 28 | Core::System& system; | ||
| 24 | }; | 29 | }; |
| 25 | }; | 30 | }; |
| 26 | 31 | ||
| 27 | void InstallInterfaces(SM::ServiceManager& service_manager); | 32 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 28 | 33 | ||
| 29 | } // namespace Service::Fatal | 34 | } // namespace Service::Fatal |
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 9e5f872ff..066ccf6b0 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::Fatal { | 7 | namespace Service::Fatal { |
| 8 | 8 | ||
| 9 | Fatal_P::Fatal_P(std::shared_ptr<Module> module) | 9 | Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system) |
| 10 | : Module::Interface(std::move(module), "fatal:p") {} | 10 | : Module::Interface(std::move(module), system, "fatal:p") {} |
| 11 | 11 | ||
| 12 | Fatal_P::~Fatal_P() = default; | 12 | Fatal_P::~Fatal_P() = default; |
| 13 | 13 | ||
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index 6e9c5979f..c6d953cb5 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h | |||
| @@ -10,7 +10,7 @@ namespace Service::Fatal { | |||
| 10 | 10 | ||
| 11 | class Fatal_P final : public Module::Interface { | 11 | class Fatal_P final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Fatal_P(std::shared_ptr<Module> module); | 13 | explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system); |
| 14 | ~Fatal_P() override; | 14 | ~Fatal_P() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 1572a2051..8d72ed485 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp | |||
| @@ -6,7 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::Fatal { | 7 | namespace Service::Fatal { |
| 8 | 8 | ||
| 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { | 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module, Core::System& system) |
| 10 | : Module::Interface(std::move(module), system, "fatal:u") { | ||
| 10 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 11 | {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, | 12 | {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, |
| 12 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, | 13 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, |
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 72cb6d076..34c5c7f95 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h | |||
| @@ -10,7 +10,7 @@ namespace Service::Fatal { | |||
| 10 | 10 | ||
| 11 | class Fatal_U final : public Module::Interface { | 11 | class Fatal_U final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Fatal_U(std::shared_ptr<Module> module); | 13 | explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system); |
| 14 | ~Fatal_U() override; | 14 | ~Fatal_U() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 8ce110dd1..14cd0e322 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/bis_factory.h" | 10 | #include "core/file_sys/bis_factory.h" |
| 11 | #include "core/file_sys/card_image.h" | ||
| 11 | #include "core/file_sys/control_metadata.h" | 12 | #include "core/file_sys/control_metadata.h" |
| 12 | #include "core/file_sys/errors.h" | 13 | #include "core/file_sys/errors.h" |
| 13 | #include "core/file_sys/mode.h" | 14 | #include "core/file_sys/mode.h" |
| @@ -25,14 +26,10 @@ | |||
| 25 | #include "core/hle/service/filesystem/fsp_pr.h" | 26 | #include "core/hle/service/filesystem/fsp_pr.h" |
| 26 | #include "core/hle/service/filesystem/fsp_srv.h" | 27 | #include "core/hle/service/filesystem/fsp_srv.h" |
| 27 | #include "core/loader/loader.h" | 28 | #include "core/loader/loader.h" |
| 29 | #include "core/settings.h" | ||
| 28 | 30 | ||
| 29 | namespace Service::FileSystem { | 31 | namespace Service::FileSystem { |
| 30 | 32 | ||
| 31 | // Size of emulated sd card free space, reported in bytes. | ||
| 32 | // Just using 32GB because thats reasonable | ||
| 33 | // TODO(DarkLordZach): Eventually make this configurable in settings. | ||
| 34 | constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | ||
| 35 | |||
| 36 | // A default size for normal/journal save data size if application control metadata cannot be found. | 33 | // A default size for normal/journal save data size if application control metadata cannot be found. |
| 37 | // This should be large enough to satisfy even the most extreme requirements (~4.2GB) | 34 | // This should be large enough to satisfy even the most extreme requirements (~4.2GB) |
| 38 | constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; | 35 | constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; |
| @@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s | |||
| 226 | return MakeResult(dir); | 223 | return MakeResult(dir); |
| 227 | } | 224 | } |
| 228 | 225 | ||
| 229 | u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||
| 230 | if (backing->IsWritable()) | ||
| 231 | return EMULATED_SD_REPORTED_SIZE; | ||
| 232 | |||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | 226 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( |
| 237 | const std::string& path_) const { | 227 | const std::string& path_) const { |
| 238 | std::string path(FileUtil::SanitizePath(path_)); | 228 | std::string path(FileUtil::SanitizePath(path_)); |
| @@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | |||
| 251 | return FileSys::ERROR_PATH_NOT_FOUND; | 241 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 252 | } | 242 | } |
| 253 | 243 | ||
| 254 | /** | 244 | FileSystemController::FileSystemController() = default; |
| 255 | * Map of registered file systems, identified by type. Once an file system is registered here, it | 245 | |
| 256 | * is never removed until UnregisterFileSystems is called. | 246 | FileSystemController::~FileSystemController() = default; |
| 257 | */ | ||
| 258 | static std::unique_ptr<FileSys::RomFSFactory> romfs_factory; | ||
| 259 | static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; | ||
| 260 | static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; | ||
| 261 | static std::unique_ptr<FileSys::BISFactory> bis_factory; | ||
| 262 | 247 | ||
| 263 | ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { | 248 | ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { |
| 264 | ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS"); | ||
| 265 | romfs_factory = std::move(factory); | 249 | romfs_factory = std::move(factory); |
| 266 | LOG_DEBUG(Service_FS, "Registered RomFS"); | 250 | LOG_DEBUG(Service_FS, "Registered RomFS"); |
| 267 | return RESULT_SUCCESS; | 251 | return RESULT_SUCCESS; |
| 268 | } | 252 | } |
| 269 | 253 | ||
| 270 | ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { | 254 | ResultCode FileSystemController::RegisterSaveData( |
| 271 | ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data"); | 255 | std::unique_ptr<FileSys::SaveDataFactory>&& factory) { |
| 256 | ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); | ||
| 272 | save_data_factory = std::move(factory); | 257 | save_data_factory = std::move(factory); |
| 273 | LOG_DEBUG(Service_FS, "Registered save data"); | 258 | LOG_DEBUG(Service_FS, "Registered save data"); |
| 274 | return RESULT_SUCCESS; | 259 | return RESULT_SUCCESS; |
| 275 | } | 260 | } |
| 276 | 261 | ||
| 277 | ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { | 262 | ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { |
| 278 | ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); | 263 | ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); |
| 279 | sdmc_factory = std::move(factory); | 264 | sdmc_factory = std::move(factory); |
| 280 | LOG_DEBUG(Service_FS, "Registered SDMC"); | 265 | LOG_DEBUG(Service_FS, "Registered SDMC"); |
| 281 | return RESULT_SUCCESS; | 266 | return RESULT_SUCCESS; |
| 282 | } | 267 | } |
| 283 | 268 | ||
| 284 | ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { | 269 | ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { |
| 285 | ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); | 270 | ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); |
| 286 | bis_factory = std::move(factory); | 271 | bis_factory = std::move(factory); |
| 287 | LOG_DEBUG(Service_FS, "Registered BIS"); | 272 | LOG_DEBUG(Service_FS, "Registered BIS"); |
| 288 | return RESULT_SUCCESS; | 273 | return RESULT_SUCCESS; |
| 289 | } | 274 | } |
| 290 | 275 | ||
| 291 | void SetPackedUpdate(FileSys::VirtualFile update_raw) { | 276 | void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { |
| 292 | LOG_TRACE(Service_FS, "Setting packed update for romfs"); | 277 | LOG_TRACE(Service_FS, "Setting packed update for romfs"); |
| 293 | 278 | ||
| 294 | if (romfs_factory == nullptr) | 279 | if (romfs_factory == nullptr) |
| @@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) { | |||
| 297 | romfs_factory->SetPackedUpdate(std::move(update_raw)); | 282 | romfs_factory->SetPackedUpdate(std::move(update_raw)); |
| 298 | } | 283 | } |
| 299 | 284 | ||
| 300 | ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { | 285 | ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const { |
| 301 | LOG_TRACE(Service_FS, "Opening RomFS for current process"); | 286 | LOG_TRACE(Service_FS, "Opening RomFS for current process"); |
| 302 | 287 | ||
| 303 | if (romfs_factory == nullptr) { | 288 | if (romfs_factory == nullptr) { |
| @@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { | |||
| 308 | return romfs_factory->OpenCurrentProcess(); | 293 | return romfs_factory->OpenCurrentProcess(); |
| 309 | } | 294 | } |
| 310 | 295 | ||
| 311 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | 296 | ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( |
| 312 | FileSys::ContentRecordType type) { | 297 | u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { |
| 313 | LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", | 298 | LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", |
| 314 | title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); | 299 | title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); |
| 315 | 300 | ||
| @@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora | |||
| 321 | return romfs_factory->Open(title_id, storage_id, type); | 306 | return romfs_factory->Open(title_id, storage_id, type); |
| 322 | } | 307 | } |
| 323 | 308 | ||
| 324 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | 309 | ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( |
| 325 | const FileSys::SaveDataDescriptor& descriptor) { | 310 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { |
| 311 | LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", | ||
| 312 | static_cast<u8>(space), save_struct.DebugInfo()); | ||
| 313 | |||
| 314 | if (save_data_factory == nullptr) { | ||
| 315 | return FileSys::ERROR_ENTITY_NOT_FOUND; | ||
| 316 | } | ||
| 317 | |||
| 318 | return save_data_factory->Create(space, save_struct); | ||
| 319 | } | ||
| 320 | |||
| 321 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( | ||
| 322 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { | ||
| 326 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | 323 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", |
| 327 | static_cast<u8>(space), descriptor.DebugInfo()); | 324 | static_cast<u8>(space), descriptor.DebugInfo()); |
| 328 | 325 | ||
| @@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | |||
| 333 | return save_data_factory->Open(space, descriptor); | 330 | return save_data_factory->Open(space, descriptor); |
| 334 | } | 331 | } |
| 335 | 332 | ||
| 336 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { | 333 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( |
| 334 | FileSys::SaveDataSpaceId space) const { | ||
| 337 | LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); | 335 | LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); |
| 338 | 336 | ||
| 339 | if (save_data_factory == nullptr) { | 337 | if (save_data_factory == nullptr) { |
| @@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) | |||
| 343 | return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); | 341 | return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); |
| 344 | } | 342 | } |
| 345 | 343 | ||
| 346 | ResultVal<FileSys::VirtualDir> OpenSDMC() { | 344 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const { |
| 347 | LOG_TRACE(Service_FS, "Opening SDMC"); | 345 | LOG_TRACE(Service_FS, "Opening SDMC"); |
| 348 | 346 | ||
| 349 | if (sdmc_factory == nullptr) { | 347 | if (sdmc_factory == nullptr) { |
| @@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { | |||
| 353 | return sdmc_factory->Open(); | 351 | return sdmc_factory->Open(); |
| 354 | } | 352 | } |
| 355 | 353 | ||
| 356 | FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { | 354 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition( |
| 355 | FileSys::BisPartitionId id) const { | ||
| 356 | LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id)); | ||
| 357 | |||
| 358 | if (bis_factory == nullptr) { | ||
| 359 | return FileSys::ERROR_ENTITY_NOT_FOUND; | ||
| 360 | } | ||
| 361 | |||
| 362 | auto part = bis_factory->OpenPartition(id); | ||
| 363 | if (part == nullptr) { | ||
| 364 | return FileSys::ERROR_INVALID_ARGUMENT; | ||
| 365 | } | ||
| 366 | |||
| 367 | return MakeResult<FileSys::VirtualDir>(std::move(part)); | ||
| 368 | } | ||
| 369 | |||
| 370 | ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage( | ||
| 371 | FileSys::BisPartitionId id) const { | ||
| 372 | LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id)); | ||
| 373 | |||
| 374 | if (bis_factory == nullptr) { | ||
| 375 | return FileSys::ERROR_ENTITY_NOT_FOUND; | ||
| 376 | } | ||
| 377 | |||
| 378 | auto part = bis_factory->OpenPartitionStorage(id); | ||
| 379 | if (part == nullptr) { | ||
| 380 | return FileSys::ERROR_INVALID_ARGUMENT; | ||
| 381 | } | ||
| 382 | |||
| 383 | return MakeResult<FileSys::VirtualFile>(std::move(part)); | ||
| 384 | } | ||
| 385 | |||
| 386 | u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const { | ||
| 387 | switch (id) { | ||
| 388 | case FileSys::StorageId::None: | ||
| 389 | case FileSys::StorageId::GameCard: | ||
| 390 | return 0; | ||
| 391 | case FileSys::StorageId::SdCard: | ||
| 392 | if (sdmc_factory == nullptr) | ||
| 393 | return 0; | ||
| 394 | return sdmc_factory->GetSDMCFreeSpace(); | ||
| 395 | case FileSys::StorageId::Host: | ||
| 396 | if (bis_factory == nullptr) | ||
| 397 | return 0; | ||
| 398 | return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace(); | ||
| 399 | case FileSys::StorageId::NandSystem: | ||
| 400 | if (bis_factory == nullptr) | ||
| 401 | return 0; | ||
| 402 | return bis_factory->GetSystemNANDFreeSpace(); | ||
| 403 | case FileSys::StorageId::NandUser: | ||
| 404 | if (bis_factory == nullptr) | ||
| 405 | return 0; | ||
| 406 | return bis_factory->GetUserNANDFreeSpace(); | ||
| 407 | } | ||
| 408 | |||
| 409 | return 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { | ||
| 413 | switch (id) { | ||
| 414 | case FileSys::StorageId::None: | ||
| 415 | case FileSys::StorageId::GameCard: | ||
| 416 | return 0; | ||
| 417 | case FileSys::StorageId::SdCard: | ||
| 418 | if (sdmc_factory == nullptr) | ||
| 419 | return 0; | ||
| 420 | return sdmc_factory->GetSDMCTotalSpace(); | ||
| 421 | case FileSys::StorageId::Host: | ||
| 422 | if (bis_factory == nullptr) | ||
| 423 | return 0; | ||
| 424 | return bis_factory->GetFullNANDTotalSpace(); | ||
| 425 | case FileSys::StorageId::NandSystem: | ||
| 426 | if (bis_factory == nullptr) | ||
| 427 | return 0; | ||
| 428 | return bis_factory->GetSystemNANDTotalSpace(); | ||
| 429 | case FileSys::StorageId::NandUser: | ||
| 430 | if (bis_factory == nullptr) | ||
| 431 | return 0; | ||
| 432 | return bis_factory->GetUserNANDTotalSpace(); | ||
| 433 | } | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 438 | FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, | ||
| 439 | u64 title_id, u128 user_id) const { | ||
| 357 | if (save_data_factory == nullptr) { | 440 | if (save_data_factory == nullptr) { |
| 358 | return {0, 0}; | 441 | return {0, 0}; |
| 359 | } | 442 | } |
| @@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, | |||
| 385 | return value; | 468 | return value; |
| 386 | } | 469 | } |
| 387 | 470 | ||
| 388 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | 471 | void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, |
| 389 | FileSys::SaveDataSize new_value) { | 472 | FileSys::SaveDataSize new_value) const { |
| 390 | if (save_data_factory != nullptr) | 473 | if (save_data_factory != nullptr) |
| 391 | save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); | 474 | save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); |
| 392 | } | 475 | } |
| 393 | 476 | ||
| 394 | FileSys::RegisteredCache* GetSystemNANDContents() { | 477 | void FileSystemController::SetGameCard(FileSys::VirtualFile file) { |
| 478 | gamecard = std::make_unique<FileSys::XCI>(file); | ||
| 479 | const auto dir = gamecard->ConcatenatedPseudoDirectory(); | ||
| 480 | gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir); | ||
| 481 | gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir); | ||
| 482 | } | ||
| 483 | |||
| 484 | FileSys::XCI* FileSystemController::GetGameCard() const { | ||
| 485 | return gamecard.get(); | ||
| 486 | } | ||
| 487 | |||
| 488 | FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const { | ||
| 489 | return gamecard_registered.get(); | ||
| 490 | } | ||
| 491 | |||
| 492 | FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const { | ||
| 493 | return gamecard_placeholder.get(); | ||
| 494 | } | ||
| 495 | |||
| 496 | FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const { | ||
| 395 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); | 497 | LOG_TRACE(Service_FS, "Opening System NAND Contents"); |
| 396 | 498 | ||
| 397 | if (bis_factory == nullptr) | 499 | if (bis_factory == nullptr) |
| @@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() { | |||
| 400 | return bis_factory->GetSystemNANDContents(); | 502 | return bis_factory->GetSystemNANDContents(); |
| 401 | } | 503 | } |
| 402 | 504 | ||
| 403 | FileSys::RegisteredCache* GetUserNANDContents() { | 505 | FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const { |
| 404 | LOG_TRACE(Service_FS, "Opening User NAND Contents"); | 506 | LOG_TRACE(Service_FS, "Opening User NAND Contents"); |
| 405 | 507 | ||
| 406 | if (bis_factory == nullptr) | 508 | if (bis_factory == nullptr) |
| @@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() { | |||
| 409 | return bis_factory->GetUserNANDContents(); | 511 | return bis_factory->GetUserNANDContents(); |
| 410 | } | 512 | } |
| 411 | 513 | ||
| 412 | FileSys::RegisteredCache* GetSDMCContents() { | 514 | FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const { |
| 413 | LOG_TRACE(Service_FS, "Opening SDMC Contents"); | 515 | LOG_TRACE(Service_FS, "Opening SDMC Contents"); |
| 414 | 516 | ||
| 415 | if (sdmc_factory == nullptr) | 517 | if (sdmc_factory == nullptr) |
| @@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() { | |||
| 418 | return sdmc_factory->GetSDMCContents(); | 520 | return sdmc_factory->GetSDMCContents(); |
| 419 | } | 521 | } |
| 420 | 522 | ||
| 421 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { | 523 | FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const { |
| 524 | LOG_TRACE(Service_FS, "Opening System NAND Placeholder"); | ||
| 525 | |||
| 526 | if (bis_factory == nullptr) | ||
| 527 | return nullptr; | ||
| 528 | |||
| 529 | return bis_factory->GetSystemNANDPlaceholder(); | ||
| 530 | } | ||
| 531 | |||
| 532 | FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const { | ||
| 533 | LOG_TRACE(Service_FS, "Opening User NAND Placeholder"); | ||
| 534 | |||
| 535 | if (bis_factory == nullptr) | ||
| 536 | return nullptr; | ||
| 537 | |||
| 538 | return bis_factory->GetUserNANDPlaceholder(); | ||
| 539 | } | ||
| 540 | |||
| 541 | FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const { | ||
| 542 | LOG_TRACE(Service_FS, "Opening SDMC Placeholder"); | ||
| 543 | |||
| 544 | if (sdmc_factory == nullptr) | ||
| 545 | return nullptr; | ||
| 546 | |||
| 547 | return sdmc_factory->GetSDMCPlaceholder(); | ||
| 548 | } | ||
| 549 | |||
| 550 | FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage( | ||
| 551 | FileSys::StorageId id) const { | ||
| 552 | switch (id) { | ||
| 553 | case FileSys::StorageId::None: | ||
| 554 | case FileSys::StorageId::Host: | ||
| 555 | UNIMPLEMENTED(); | ||
| 556 | return nullptr; | ||
| 557 | case FileSys::StorageId::GameCard: | ||
| 558 | return GetGameCardContents(); | ||
| 559 | case FileSys::StorageId::NandSystem: | ||
| 560 | return GetSystemNANDContents(); | ||
| 561 | case FileSys::StorageId::NandUser: | ||
| 562 | return GetUserNANDContents(); | ||
| 563 | case FileSys::StorageId::SdCard: | ||
| 564 | return GetSDMCContents(); | ||
| 565 | } | ||
| 566 | |||
| 567 | return nullptr; | ||
| 568 | } | ||
| 569 | |||
| 570 | FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage( | ||
| 571 | FileSys::StorageId id) const { | ||
| 572 | switch (id) { | ||
| 573 | case FileSys::StorageId::None: | ||
| 574 | case FileSys::StorageId::Host: | ||
| 575 | UNIMPLEMENTED(); | ||
| 576 | return nullptr; | ||
| 577 | case FileSys::StorageId::GameCard: | ||
| 578 | return GetGameCardPlaceholder(); | ||
| 579 | case FileSys::StorageId::NandSystem: | ||
| 580 | return GetSystemNANDPlaceholder(); | ||
| 581 | case FileSys::StorageId::NandUser: | ||
| 582 | return GetUserNANDPlaceholder(); | ||
| 583 | case FileSys::StorageId::SdCard: | ||
| 584 | return GetSDMCPlaceholder(); | ||
| 585 | } | ||
| 586 | |||
| 587 | return nullptr; | ||
| 588 | } | ||
| 589 | |||
| 590 | FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const { | ||
| 591 | LOG_TRACE(Service_FS, "Opening system NAND content directory"); | ||
| 592 | |||
| 593 | if (bis_factory == nullptr) | ||
| 594 | return nullptr; | ||
| 595 | |||
| 596 | return bis_factory->GetSystemNANDContentDirectory(); | ||
| 597 | } | ||
| 598 | |||
| 599 | FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const { | ||
| 600 | LOG_TRACE(Service_FS, "Opening user NAND content directory"); | ||
| 601 | |||
| 602 | if (bis_factory == nullptr) | ||
| 603 | return nullptr; | ||
| 604 | |||
| 605 | return bis_factory->GetUserNANDContentDirectory(); | ||
| 606 | } | ||
| 607 | |||
| 608 | FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const { | ||
| 609 | LOG_TRACE(Service_FS, "Opening SDMC content directory"); | ||
| 610 | |||
| 611 | if (sdmc_factory == nullptr) | ||
| 612 | return nullptr; | ||
| 613 | |||
| 614 | return sdmc_factory->GetSDMCContentDirectory(); | ||
| 615 | } | ||
| 616 | |||
| 617 | FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const { | ||
| 618 | LOG_TRACE(Service_FS, "Opening NAND image directory"); | ||
| 619 | |||
| 620 | if (bis_factory == nullptr) | ||
| 621 | return nullptr; | ||
| 622 | |||
| 623 | return bis_factory->GetImageDirectory(); | ||
| 624 | } | ||
| 625 | |||
| 626 | FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const { | ||
| 627 | LOG_TRACE(Service_FS, "Opening SDMC image directory"); | ||
| 628 | |||
| 629 | if (sdmc_factory == nullptr) | ||
| 630 | return nullptr; | ||
| 631 | |||
| 632 | return sdmc_factory->GetImageDirectory(); | ||
| 633 | } | ||
| 634 | |||
| 635 | FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const { | ||
| 636 | switch (id) { | ||
| 637 | case ContentStorageId::System: | ||
| 638 | return GetSystemNANDContentDirectory(); | ||
| 639 | case ContentStorageId::User: | ||
| 640 | return GetUserNANDContentDirectory(); | ||
| 641 | case ContentStorageId::SdCard: | ||
| 642 | return GetSDMCContentDirectory(); | ||
| 643 | } | ||
| 644 | |||
| 645 | return nullptr; | ||
| 646 | } | ||
| 647 | |||
| 648 | FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const { | ||
| 649 | switch (id) { | ||
| 650 | case ImageDirectoryId::NAND: | ||
| 651 | return GetNANDImageDirectory(); | ||
| 652 | case ImageDirectoryId::SdCard: | ||
| 653 | return GetSDMCImageDirectory(); | ||
| 654 | } | ||
| 655 | |||
| 656 | return nullptr; | ||
| 657 | } | ||
| 658 | |||
| 659 | FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const { | ||
| 422 | LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); | 660 | LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); |
| 423 | 661 | ||
| 424 | if (bis_factory == nullptr) | 662 | if (bis_factory == nullptr) |
| @@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { | |||
| 427 | return bis_factory->GetModificationLoadRoot(title_id); | 665 | return bis_factory->GetModificationLoadRoot(title_id); |
| 428 | } | 666 | } |
| 429 | 667 | ||
| 430 | FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { | 668 | FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { |
| 431 | LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); | 669 | LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); |
| 432 | 670 | ||
| 433 | if (bis_factory == nullptr) | 671 | if (bis_factory == nullptr) |
| @@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { | |||
| 436 | return bis_factory->GetModificationDumpRoot(title_id); | 674 | return bis_factory->GetModificationDumpRoot(title_id); |
| 437 | } | 675 | } |
| 438 | 676 | ||
| 439 | void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | 677 | void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { |
| 440 | if (overwrite) { | 678 | if (overwrite) { |
| 441 | bis_factory = nullptr; | 679 | bis_factory = nullptr; |
| 442 | save_data_factory = nullptr; | 680 | save_data_factory = nullptr; |
| @@ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { | |||
| 473 | } | 711 | } |
| 474 | 712 | ||
| 475 | void InstallInterfaces(Core::System& system) { | 713 | void InstallInterfaces(Core::System& system) { |
| 476 | romfs_factory = nullptr; | ||
| 477 | CreateFactories(*system.GetFilesystem(), false); | ||
| 478 | std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); | 714 | std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); |
| 479 | std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); | 715 | std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); |
| 480 | std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager()); | 716 | std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) |
| 717 | ->InstallAsService(system.ServiceManager()); | ||
| 481 | } | 718 | } |
| 482 | 719 | ||
| 483 | } // namespace Service::FileSystem | 720 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 3849dd89e..3e0c03ec0 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -14,10 +14,13 @@ namespace FileSys { | |||
| 14 | class BISFactory; | 14 | class BISFactory; |
| 15 | class RegisteredCache; | 15 | class RegisteredCache; |
| 16 | class RegisteredCacheUnion; | 16 | class RegisteredCacheUnion; |
| 17 | class PlaceholderCache; | ||
| 17 | class RomFSFactory; | 18 | class RomFSFactory; |
| 18 | class SaveDataFactory; | 19 | class SaveDataFactory; |
| 19 | class SDMCFactory; | 20 | class SDMCFactory; |
| 21 | class XCI; | ||
| 20 | 22 | ||
| 23 | enum class BisPartitionId : u32; | ||
| 21 | enum class ContentRecordType : u8; | 24 | enum class ContentRecordType : u8; |
| 22 | enum class Mode : u32; | 25 | enum class Mode : u32; |
| 23 | enum class SaveDataSpaceId : u8; | 26 | enum class SaveDataSpaceId : u8; |
| @@ -36,34 +39,91 @@ class ServiceManager; | |||
| 36 | 39 | ||
| 37 | namespace FileSystem { | 40 | namespace FileSystem { |
| 38 | 41 | ||
| 39 | ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); | 42 | enum class ContentStorageId : u32 { |
| 40 | ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); | 43 | System, |
| 41 | ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); | 44 | User, |
| 42 | ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); | 45 | SdCard, |
| 43 | 46 | }; | |
| 44 | void SetPackedUpdate(FileSys::VirtualFile update_raw); | ||
| 45 | ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); | ||
| 46 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | ||
| 47 | FileSys::ContentRecordType type); | ||
| 48 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||
| 49 | const FileSys::SaveDataDescriptor& descriptor); | ||
| 50 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | ||
| 51 | ResultVal<FileSys::VirtualDir> OpenSDMC(); | ||
| 52 | |||
| 53 | FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); | ||
| 54 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||
| 55 | FileSys::SaveDataSize new_value); | ||
| 56 | 47 | ||
| 57 | FileSys::RegisteredCache* GetSystemNANDContents(); | 48 | enum class ImageDirectoryId : u32 { |
| 58 | FileSys::RegisteredCache* GetUserNANDContents(); | 49 | NAND, |
| 59 | FileSys::RegisteredCache* GetSDMCContents(); | 50 | SdCard, |
| 51 | }; | ||
| 60 | 52 | ||
| 61 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); | 53 | class FileSystemController { |
| 62 | FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); | 54 | public: |
| 55 | FileSystemController(); | ||
| 56 | ~FileSystemController(); | ||
| 57 | |||
| 58 | ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); | ||
| 59 | ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); | ||
| 60 | ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); | ||
| 61 | ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); | ||
| 62 | |||
| 63 | void SetPackedUpdate(FileSys::VirtualFile update_raw); | ||
| 64 | ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; | ||
| 65 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | ||
| 66 | FileSys::ContentRecordType type) const; | ||
| 67 | ResultVal<FileSys::VirtualDir> CreateSaveData( | ||
| 68 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | ||
| 69 | ResultVal<FileSys::VirtualDir> OpenSaveData( | ||
| 70 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | ||
| 71 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; | ||
| 72 | ResultVal<FileSys::VirtualDir> OpenSDMC() const; | ||
| 73 | ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; | ||
| 74 | ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const; | ||
| 75 | |||
| 76 | u64 GetFreeSpaceSize(FileSys::StorageId id) const; | ||
| 77 | u64 GetTotalSpaceSize(FileSys::StorageId id) const; | ||
| 78 | |||
| 79 | FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, | ||
| 80 | u128 user_id) const; | ||
| 81 | void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, | ||
| 82 | FileSys::SaveDataSize new_value) const; | ||
| 83 | |||
| 84 | void SetGameCard(FileSys::VirtualFile file); | ||
| 85 | FileSys::XCI* GetGameCard() const; | ||
| 86 | |||
| 87 | FileSys::RegisteredCache* GetSystemNANDContents() const; | ||
| 88 | FileSys::RegisteredCache* GetUserNANDContents() const; | ||
| 89 | FileSys::RegisteredCache* GetSDMCContents() const; | ||
| 90 | FileSys::RegisteredCache* GetGameCardContents() const; | ||
| 91 | |||
| 92 | FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const; | ||
| 93 | FileSys::PlaceholderCache* GetUserNANDPlaceholder() const; | ||
| 94 | FileSys::PlaceholderCache* GetSDMCPlaceholder() const; | ||
| 95 | FileSys::PlaceholderCache* GetGameCardPlaceholder() const; | ||
| 96 | |||
| 97 | FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const; | ||
| 98 | FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const; | ||
| 99 | |||
| 100 | FileSys::VirtualDir GetSystemNANDContentDirectory() const; | ||
| 101 | FileSys::VirtualDir GetUserNANDContentDirectory() const; | ||
| 102 | FileSys::VirtualDir GetSDMCContentDirectory() const; | ||
| 103 | |||
| 104 | FileSys::VirtualDir GetNANDImageDirectory() const; | ||
| 105 | FileSys::VirtualDir GetSDMCImageDirectory() const; | ||
| 106 | |||
| 107 | FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; | ||
| 108 | FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; | ||
| 109 | |||
| 110 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; | ||
| 111 | FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; | ||
| 112 | |||
| 113 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function | ||
| 114 | // above is called. | ||
| 115 | void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); | ||
| 63 | 116 | ||
| 64 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function | 117 | private: |
| 65 | // above is called. | 118 | std::unique_ptr<FileSys::RomFSFactory> romfs_factory; |
| 66 | void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); | 119 | std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; |
| 120 | std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; | ||
| 121 | std::unique_ptr<FileSys::BISFactory> bis_factory; | ||
| 122 | |||
| 123 | std::unique_ptr<FileSys::XCI> gamecard; | ||
| 124 | std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; | ||
| 125 | std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; | ||
| 126 | }; | ||
| 67 | 127 | ||
| 68 | void InstallInterfaces(Core::System& system); | 128 | void InstallInterfaces(Core::System& system); |
| 69 | 129 | ||
| @@ -160,12 +220,6 @@ public: | |||
| 160 | ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | 220 | ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); |
| 161 | 221 | ||
| 162 | /** | 222 | /** |
| 163 | * Get the free space | ||
| 164 | * @return The number of free bytes in the archive | ||
| 165 | */ | ||
| 166 | u64 GetFreeSpaceSize() const; | ||
| 167 | |||
| 168 | /** | ||
| 169 | * Get the type of the specified path | 223 | * Get the type of the specified path |
| 170 | * @return The type of the specified path or error code | 224 | * @return The type of the specified path or error code |
| 171 | */ | 225 | */ |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index d3cd46a9b..eb982ad49 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| 20 | #include "core/file_sys/nca_metadata.h" | 20 | #include "core/file_sys/nca_metadata.h" |
| 21 | #include "core/file_sys/patch_manager.h" | 21 | #include "core/file_sys/patch_manager.h" |
| 22 | #include "core/file_sys/romfs_factory.h" | ||
| 22 | #include "core/file_sys/savedata_factory.h" | 23 | #include "core/file_sys/savedata_factory.h" |
| 23 | #include "core/file_sys/system_archive/system_archive.h" | 24 | #include "core/file_sys/system_archive/system_archive.h" |
| 24 | #include "core/file_sys/vfs.h" | 25 | #include "core/file_sys/vfs.h" |
| @@ -30,6 +31,18 @@ | |||
| 30 | 31 | ||
| 31 | namespace Service::FileSystem { | 32 | namespace Service::FileSystem { |
| 32 | 33 | ||
| 34 | struct SizeGetter { | ||
| 35 | std::function<u64()> get_free_size; | ||
| 36 | std::function<u64()> get_total_size; | ||
| 37 | |||
| 38 | static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { | ||
| 39 | return { | ||
| 40 | [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, | ||
| 41 | [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | }; | ||
| 45 | |||
| 33 | enum class FileSystemType : u8 { | 46 | enum class FileSystemType : u8 { |
| 34 | Invalid0 = 0, | 47 | Invalid0 = 0, |
| 35 | Invalid1 = 1, | 48 | Invalid1 = 1, |
| @@ -289,8 +302,8 @@ private: | |||
| 289 | 302 | ||
| 290 | class IFileSystem final : public ServiceFramework<IFileSystem> { | 303 | class IFileSystem final : public ServiceFramework<IFileSystem> { |
| 291 | public: | 304 | public: |
| 292 | explicit IFileSystem(FileSys::VirtualDir backend) | 305 | explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size) |
| 293 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | 306 | : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) { |
| 294 | static const FunctionInfo functions[] = { | 307 | static const FunctionInfo functions[] = { |
| 295 | {0, &IFileSystem::CreateFile, "CreateFile"}, | 308 | {0, &IFileSystem::CreateFile, "CreateFile"}, |
| 296 | {1, &IFileSystem::DeleteFile, "DeleteFile"}, | 309 | {1, &IFileSystem::DeleteFile, "DeleteFile"}, |
| @@ -467,14 +480,31 @@ public: | |||
| 467 | rb.Push(RESULT_SUCCESS); | 480 | rb.Push(RESULT_SUCCESS); |
| 468 | } | 481 | } |
| 469 | 482 | ||
| 483 | void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) { | ||
| 484 | LOG_DEBUG(Service_FS, "called"); | ||
| 485 | |||
| 486 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 487 | rb.Push(RESULT_SUCCESS); | ||
| 488 | rb.Push(size.get_free_size()); | ||
| 489 | } | ||
| 490 | |||
| 491 | void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) { | ||
| 492 | LOG_DEBUG(Service_FS, "called"); | ||
| 493 | |||
| 494 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 495 | rb.Push(RESULT_SUCCESS); | ||
| 496 | rb.Push(size.get_total_size()); | ||
| 497 | } | ||
| 498 | |||
| 470 | private: | 499 | private: |
| 471 | VfsDirectoryServiceWrapper backend; | 500 | VfsDirectoryServiceWrapper backend; |
| 501 | SizeGetter size; | ||
| 472 | }; | 502 | }; |
| 473 | 503 | ||
| 474 | class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { | 504 | class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { |
| 475 | public: | 505 | public: |
| 476 | explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) | 506 | explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc) |
| 477 | : ServiceFramework("ISaveDataInfoReader") { | 507 | : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) { |
| 478 | static const FunctionInfo functions[] = { | 508 | static const FunctionInfo functions[] = { |
| 479 | {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, | 509 | {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, |
| 480 | }; | 510 | }; |
| @@ -520,8 +550,13 @@ private: | |||
| 520 | } | 550 | } |
| 521 | 551 | ||
| 522 | void FindAllSaves(FileSys::SaveDataSpaceId space) { | 552 | void FindAllSaves(FileSys::SaveDataSpaceId space) { |
| 523 | const auto save_root = OpenSaveDataSpace(space); | 553 | const auto save_root = fsc.OpenSaveDataSpace(space); |
| 524 | ASSERT(save_root.Succeeded()); | 554 | |
| 555 | if (save_root.Failed() || *save_root == nullptr) { | ||
| 556 | LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", | ||
| 557 | static_cast<u8>(space)); | ||
| 558 | return; | ||
| 559 | } | ||
| 525 | 560 | ||
| 526 | for (const auto& type : (*save_root)->GetSubdirectories()) { | 561 | for (const auto& type : (*save_root)->GetSubdirectories()) { |
| 527 | if (type->GetName() == "save") { | 562 | if (type->GetName() == "save") { |
| @@ -610,11 +645,13 @@ private: | |||
| 610 | }; | 645 | }; |
| 611 | static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); | 646 | static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); |
| 612 | 647 | ||
| 648 | FileSystemController& fsc; | ||
| 613 | std::vector<SaveDataInfo> info; | 649 | std::vector<SaveDataInfo> info; |
| 614 | u64 next_entry_index = 0; | 650 | u64 next_entry_index = 0; |
| 615 | }; | 651 | }; |
| 616 | 652 | ||
| 617 | FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) { | 653 | FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) |
| 654 | : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { | ||
| 618 | // clang-format off | 655 | // clang-format off |
| 619 | static const FunctionInfo functions[] = { | 656 | static const FunctionInfo functions[] = { |
| 620 | {0, nullptr, "OpenFileSystem"}, | 657 | {0, nullptr, "OpenFileSystem"}, |
| @@ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { | |||
| 754 | void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | 791 | void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { |
| 755 | LOG_DEBUG(Service_FS, "called"); | 792 | LOG_DEBUG(Service_FS, "called"); |
| 756 | 793 | ||
| 757 | IFileSystem filesystem(OpenSDMC().Unwrap()); | 794 | IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), |
| 795 | SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); | ||
| 758 | 796 | ||
| 759 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 797 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 760 | rb.Push(RESULT_SUCCESS); | 798 | rb.Push(RESULT_SUCCESS); |
| @@ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 768 | auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); | 806 | auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); |
| 769 | u128 uid = rp.PopRaw<u128>(); | 807 | u128 uid = rp.PopRaw<u128>(); |
| 770 | 808 | ||
| 771 | LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}", | 809 | LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), |
| 772 | save_struct.DebugInfo(), uid[1], uid[0]); | 810 | uid[1], uid[0]); |
| 811 | |||
| 812 | fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct); | ||
| 773 | 813 | ||
| 774 | IPC::ResponseBuilder rb{ctx, 2}; | 814 | IPC::ResponseBuilder rb{ctx, 2}; |
| 775 | rb.Push(RESULT_SUCCESS); | 815 | rb.Push(RESULT_SUCCESS); |
| @@ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 786 | IPC::RequestParser rp{ctx}; | 826 | IPC::RequestParser rp{ctx}; |
| 787 | const auto parameters = rp.PopRaw<Parameters>(); | 827 | const auto parameters = rp.PopRaw<Parameters>(); |
| 788 | 828 | ||
| 789 | auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | 829 | auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); |
| 790 | if (dir.Failed()) { | 830 | if (dir.Failed()) { |
| 791 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | 831 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; |
| 792 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 832 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
| 793 | return; | 833 | return; |
| 794 | } | 834 | } |
| 795 | 835 | ||
| 796 | IFileSystem filesystem(std::move(dir.Unwrap())); | 836 | FileSys::StorageId id; |
| 837 | if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { | ||
| 838 | id = FileSys::StorageId::NandUser; | ||
| 839 | } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || | ||
| 840 | parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { | ||
| 841 | id = FileSys::StorageId::SdCard; | ||
| 842 | } else { | ||
| 843 | id = FileSys::StorageId::NandSystem; | ||
| 844 | } | ||
| 845 | |||
| 846 | IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); | ||
| 797 | 847 | ||
| 798 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 848 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 799 | rb.Push(RESULT_SUCCESS); | 849 | rb.Push(RESULT_SUCCESS); |
| @@ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& | |||
| 812 | 862 | ||
| 813 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 863 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 814 | rb.Push(RESULT_SUCCESS); | 864 | rb.Push(RESULT_SUCCESS); |
| 815 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); | 865 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); |
| 816 | } | 866 | } |
| 817 | 867 | ||
| 818 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 868 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { |
| @@ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | |||
| 836 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | 886 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { |
| 837 | LOG_DEBUG(Service_FS, "called"); | 887 | LOG_DEBUG(Service_FS, "called"); |
| 838 | 888 | ||
| 839 | auto romfs = OpenRomFSCurrentProcess(); | 889 | auto romfs = fsc.OpenRomFSCurrentProcess(); |
| 840 | if (romfs.Failed()) { | 890 | if (romfs.Failed()) { |
| 841 | // TODO (bunnei): Find the right error code to use here | 891 | // TODO (bunnei): Find the right error code to use here |
| 842 | LOG_CRITICAL(Service_FS, "no file system interface available!"); | 892 | LOG_CRITICAL(Service_FS, "no file system interface available!"); |
| @@ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { | |||
| 861 | LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", | 911 | LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", |
| 862 | static_cast<u8>(storage_id), unknown, title_id); | 912 | static_cast<u8>(storage_id), unknown, title_id); |
| 863 | 913 | ||
| 864 | auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); | 914 | auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); |
| 865 | 915 | ||
| 866 | if (data.Failed()) { | 916 | if (data.Failed()) { |
| 867 | const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); | 917 | const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index b5486a193..d52b55999 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -32,7 +32,7 @@ enum class LogMode : u32 { | |||
| 32 | 32 | ||
| 33 | class FSP_SRV final : public ServiceFramework<FSP_SRV> { | 33 | class FSP_SRV final : public ServiceFramework<FSP_SRV> { |
| 34 | public: | 34 | public: |
| 35 | explicit FSP_SRV(const Core::Reporter& reporter); | 35 | explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); |
| 36 | ~FSP_SRV() override; | 36 | ~FSP_SRV() override; |
| 37 | 37 | ||
| 38 | private: | 38 | private: |
| @@ -51,6 +51,8 @@ private: | |||
| 51 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); | 51 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); |
| 52 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); | 52 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); |
| 53 | 53 | ||
| 54 | FileSystemController& fsc; | ||
| 55 | |||
| 54 | FileSys::VirtualFile romfs; | 56 | FileSys::VirtualFile romfs; |
| 55 | u64 current_process_id = 0; | 57 | u64 current_process_id = 0; |
| 56 | u32 access_log_program_index = 0; | 58 | u32 access_log_program_index = 0; |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index d1ec12ef9..42b4ee861 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -149,7 +149,8 @@ private: | |||
| 149 | 149 | ||
| 150 | class INotificationService final : public ServiceFramework<INotificationService> { | 150 | class INotificationService final : public ServiceFramework<INotificationService> { |
| 151 | public: | 151 | public: |
| 152 | INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) { | 152 | INotificationService(Common::UUID uuid, Core::System& system) |
| 153 | : ServiceFramework("INotificationService"), uuid(uuid) { | ||
| 153 | // clang-format off | 154 | // clang-format off |
| 154 | static const FunctionInfo functions[] = { | 155 | static const FunctionInfo functions[] = { |
| 155 | {0, &INotificationService::GetEvent, "GetEvent"}, | 156 | {0, &INotificationService::GetEvent, "GetEvent"}, |
| @@ -159,6 +160,9 @@ public: | |||
| 159 | // clang-format on | 160 | // clang-format on |
| 160 | 161 | ||
| 161 | RegisterHandlers(functions); | 162 | RegisterHandlers(functions); |
| 163 | |||
| 164 | notification_event = Kernel::WritableEvent::CreateEventPair( | ||
| 165 | system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent"); | ||
| 162 | } | 166 | } |
| 163 | 167 | ||
| 164 | private: | 168 | private: |
| @@ -167,13 +171,6 @@ private: | |||
| 167 | 171 | ||
| 168 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 172 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 169 | rb.Push(RESULT_SUCCESS); | 173 | rb.Push(RESULT_SUCCESS); |
| 170 | |||
| 171 | if (!is_event_created) { | ||
| 172 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 173 | notification_event = Kernel::WritableEvent::CreateEventPair( | ||
| 174 | kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent"); | ||
| 175 | is_event_created = true; | ||
| 176 | } | ||
| 177 | rb.PushCopyObjects(notification_event.readable); | 174 | rb.PushCopyObjects(notification_event.readable); |
| 178 | } | 175 | } |
| 179 | 176 | ||
| @@ -261,21 +258,21 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx | |||
| 261 | 258 | ||
| 262 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 259 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 263 | rb.Push(RESULT_SUCCESS); | 260 | rb.Push(RESULT_SUCCESS); |
| 264 | rb.PushIpcInterface<INotificationService>(uuid); | 261 | rb.PushIpcInterface<INotificationService>(uuid, system); |
| 265 | } | 262 | } |
| 266 | 263 | ||
| 267 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 264 | Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) |
| 268 | : ServiceFramework(name), module(std::move(module)) {} | 265 | : ServiceFramework(name), module(std::move(module)), system(system) {} |
| 269 | 266 | ||
| 270 | Module::Interface::~Interface() = default; | 267 | Module::Interface::~Interface() = default; |
| 271 | 268 | ||
| 272 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 269 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 273 | auto module = std::make_shared<Module>(); | 270 | auto module = std::make_shared<Module>(); |
| 274 | std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); | 271 | std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager); |
| 275 | std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager); | 272 | std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager); |
| 276 | std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager); | 273 | std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager); |
| 277 | std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager); | 274 | std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager); |
| 278 | std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager); | 275 | std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager); |
| 279 | } | 276 | } |
| 280 | 277 | ||
| 281 | } // namespace Service::Friend | 278 | } // namespace Service::Friend |
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index 38d05fa8e..24f3fc969 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h | |||
| @@ -6,13 +6,17 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Service::Friend { | 13 | namespace Service::Friend { |
| 10 | 14 | ||
| 11 | class Module final { | 15 | class Module final { |
| 12 | public: | 16 | public: |
| 13 | class Interface : public ServiceFramework<Interface> { | 17 | class Interface : public ServiceFramework<Interface> { |
| 14 | public: | 18 | public: |
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 19 | explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); |
| 16 | ~Interface() override; | 20 | ~Interface() override; |
| 17 | 21 | ||
| 18 | void CreateFriendService(Kernel::HLERequestContext& ctx); | 22 | void CreateFriendService(Kernel::HLERequestContext& ctx); |
| @@ -20,10 +24,11 @@ public: | |||
| 20 | 24 | ||
| 21 | protected: | 25 | protected: |
| 22 | std::shared_ptr<Module> module; | 26 | std::shared_ptr<Module> module; |
| 27 | Core::System& system; | ||
| 23 | }; | 28 | }; |
| 24 | }; | 29 | }; |
| 25 | 30 | ||
| 26 | /// Registers all Friend services with the specified service manager. | 31 | /// Registers all Friend services with the specified service manager. |
| 27 | void InstallInterfaces(SM::ServiceManager& service_manager); | 32 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 28 | 33 | ||
| 29 | } // namespace Service::Friend | 34 | } // namespace Service::Friend |
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp index 5b384f733..58155f652 100644 --- a/src/core/hle/service/friend/interface.cpp +++ b/src/core/hle/service/friend/interface.cpp | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::Friend { | 7 | namespace Service::Friend { |
| 8 | 8 | ||
| 9 | Friend::Friend(std::shared_ptr<Module> module, const char* name) | 9 | Friend::Friend(std::shared_ptr<Module> module, Core::System& system, const char* name) |
| 10 | : Interface(std::move(module), name) { | 10 | : Interface(std::move(module), system, name) { |
| 11 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 12 | {0, &Friend::CreateFriendService, "CreateFriendService"}, | 12 | {0, &Friend::CreateFriendService, "CreateFriendService"}, |
| 13 | {1, &Friend::CreateNotificationService, "CreateNotificationService"}, | 13 | {1, &Friend::CreateNotificationService, "CreateNotificationService"}, |
diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h index 1963def39..465a35770 100644 --- a/src/core/hle/service/friend/interface.h +++ b/src/core/hle/service/friend/interface.h | |||
| @@ -10,7 +10,7 @@ namespace Service::Friend { | |||
| 10 | 10 | ||
| 11 | class Friend final : public Module::Interface { | 11 | class Friend final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit Friend(std::shared_ptr<Module> module, const char* name); | 13 | explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name); |
| 14 | ~Friend() override; | 14 | ~Friend() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 0993a7815..8091db9d7 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::HID { | 7 | namespace Service::HID { |
| 8 | 8 | ||
| 9 | ControllerBase::ControllerBase() = default; | 9 | ControllerBase::ControllerBase(Core::System& system) : system(system) {} |
| 10 | ControllerBase::~ControllerBase() = default; | 10 | ControllerBase::~ControllerBase() = default; |
| 11 | 11 | ||
| 12 | void ControllerBase::ActivateController() { | 12 | 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 { | |||
| 11 | class CoreTiming; | 11 | class CoreTiming; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Service::HID { | 18 | namespace Service::HID { |
| 15 | class ControllerBase { | 19 | class ControllerBase { |
| 16 | public: | 20 | public: |
| 17 | ControllerBase(); | 21 | explicit ControllerBase(Core::System& system); |
| 18 | virtual ~ControllerBase(); | 22 | virtual ~ControllerBase(); |
| 19 | 23 | ||
| 20 | // Called when the controller is initialized | 24 | // Called when the controller is initialized |
| @@ -46,5 +50,7 @@ protected: | |||
| 46 | s64_le entry_count; | 50 | s64_le entry_count; |
| 47 | }; | 51 | }; |
| 48 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); | 52 | static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); |
| 53 | |||
| 54 | Core::System& system; | ||
| 49 | }; | 55 | }; |
| 50 | } // namespace Service::HID | 56 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index c5c2e032a..8e8263f5b 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -14,7 +14,8 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | |||
| 14 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 14 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; | 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; |
| 16 | 16 | ||
| 17 | Controller_DebugPad::Controller_DebugPad() = default; | 17 | Controller_DebugPad::Controller_DebugPad(Core::System& system) |
| 18 | : ControllerBase(system), system(system) {} | ||
| 18 | Controller_DebugPad::~Controller_DebugPad() = default; | 19 | Controller_DebugPad::~Controller_DebugPad() = default; |
| 19 | 20 | ||
| 20 | void Controller_DebugPad::OnInit() {} | 21 | 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 @@ | |||
| 16 | namespace Service::HID { | 16 | namespace Service::HID { |
| 17 | class Controller_DebugPad final : public ControllerBase { | 17 | class Controller_DebugPad final : public ControllerBase { |
| 18 | public: | 18 | public: |
| 19 | Controller_DebugPad(); | 19 | explicit Controller_DebugPad(Core::System& system); |
| 20 | ~Controller_DebugPad() override; | 20 | ~Controller_DebugPad() override; |
| 21 | 21 | ||
| 22 | // Called when the controller is initialized | 22 | // Called when the controller is initialized |
| @@ -89,5 +89,6 @@ private: | |||
| 89 | buttons; | 89 | buttons; |
| 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> | 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> |
| 91 | analogs; | 91 | analogs; |
| 92 | Core::System& system; | ||
| 92 | }; | 93 | }; |
| 93 | } // namespace Service::HID | 94 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index a179252e3..80da0a0d3 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -10,7 +10,8 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; |
| 12 | 12 | ||
| 13 | Controller_Gesture::Controller_Gesture() = default; | 13 | Controller_Gesture::Controller_Gesture(Core::System& system) |
| 14 | : ControllerBase(system), system(system) {} | ||
| 14 | Controller_Gesture::~Controller_Gesture() = default; | 15 | Controller_Gesture::~Controller_Gesture() = default; |
| 15 | 16 | ||
| 16 | void Controller_Gesture::OnInit() {} | 17 | 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 @@ | |||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | class Controller_Gesture final : public ControllerBase { | 13 | class Controller_Gesture final : public ControllerBase { |
| 14 | public: | 14 | public: |
| 15 | Controller_Gesture(); | 15 | explicit Controller_Gesture(Core::System& system); |
| 16 | ~Controller_Gesture() override; | 16 | ~Controller_Gesture() override; |
| 17 | 17 | ||
| 18 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| @@ -59,5 +59,6 @@ private: | |||
| 59 | std::array<GestureState, 17> gesture_states; | 59 | std::array<GestureState, 17> gesture_states; |
| 60 | }; | 60 | }; |
| 61 | SharedMemory shared_memory{}; | 61 | SharedMemory shared_memory{}; |
| 62 | Core::System& system; | ||
| 62 | }; | 63 | }; |
| 63 | } // namespace Service::HID | 64 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 92d7bfb52..e587b2e15 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -12,7 +12,8 @@ namespace Service::HID { | |||
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 13 | constexpr u8 KEYS_PER_BYTE = 8; | 13 | constexpr u8 KEYS_PER_BYTE = 8; |
| 14 | 14 | ||
| 15 | Controller_Keyboard::Controller_Keyboard() = default; | 15 | Controller_Keyboard::Controller_Keyboard(Core::System& system) |
| 16 | : ControllerBase(system), system(system) {} | ||
| 16 | Controller_Keyboard::~Controller_Keyboard() = default; | 17 | Controller_Keyboard::~Controller_Keyboard() = default; |
| 17 | 18 | ||
| 18 | void Controller_Keyboard::OnInit() {} | 19 | 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 @@ | |||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| 16 | class Controller_Keyboard final : public ControllerBase { | 16 | class Controller_Keyboard final : public ControllerBase { |
| 17 | public: | 17 | public: |
| 18 | Controller_Keyboard(); | 18 | explicit Controller_Keyboard(Core::System& system); |
| 19 | ~Controller_Keyboard() override; | 19 | ~Controller_Keyboard() override; |
| 20 | 20 | ||
| 21 | // Called when the controller is initialized | 21 | // Called when the controller is initialized |
| @@ -53,5 +53,6 @@ private: | |||
| 53 | keyboard_keys; | 53 | keyboard_keys; |
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> | 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> |
| 55 | keyboard_mods; | 55 | keyboard_mods; |
| 56 | Core::System& system; | ||
| 56 | }; | 57 | }; |
| 57 | } // namespace Service::HID | 58 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 11ab096d9..88f2ca4c1 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | namespace Service::HID { | 11 | namespace Service::HID { |
| 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; |
| 13 | 13 | ||
| 14 | Controller_Mouse::Controller_Mouse() = default; | 14 | Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {} |
| 15 | Controller_Mouse::~Controller_Mouse() = default; | 15 | Controller_Mouse::~Controller_Mouse() = default; |
| 16 | 16 | ||
| 17 | void Controller_Mouse::OnInit() {} | 17 | 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 @@ | |||
| 14 | namespace Service::HID { | 14 | namespace Service::HID { |
| 15 | class Controller_Mouse final : public ControllerBase { | 15 | class Controller_Mouse final : public ControllerBase { |
| 16 | public: | 16 | public: |
| 17 | Controller_Mouse(); | 17 | explicit Controller_Mouse(Core::System& system); |
| 18 | ~Controller_Mouse() override; | 18 | ~Controller_Mouse() override; |
| 19 | 19 | ||
| 20 | // Called when the controller is initialized | 20 | // Called when the controller is initialized |
| @@ -53,5 +53,6 @@ private: | |||
| 53 | std::unique_ptr<Input::MouseDevice> mouse_device; | 53 | std::unique_ptr<Input::MouseDevice> mouse_device; |
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> | 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> |
| 55 | mouse_button_devices; | 55 | mouse_button_devices; |
| 56 | Core::System& system; | ||
| 56 | }; | 57 | }; |
| 57 | } // namespace Service::HID | 58 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index e47fe8188..44b668fbf 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -93,7 +93,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { | |||
| 93 | }; | 93 | }; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | Controller_NPad::Controller_NPad() = default; | 96 | Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} |
| 97 | Controller_NPad::~Controller_NPad() = default; | 97 | Controller_NPad::~Controller_NPad() = default; |
| 98 | 98 | ||
| 99 | void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | 99 | void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { |
| @@ -168,9 +168,11 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | void Controller_NPad::OnInit() { | 170 | void Controller_NPad::OnInit() { |
| 171 | auto& kernel = Core::System::GetInstance().Kernel(); | 171 | auto& kernel = system.Kernel(); |
| 172 | styleset_changed_event = Kernel::WritableEvent::CreateEventPair( | 172 | for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { |
| 173 | kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged"); | 173 | styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( |
| 174 | kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i)); | ||
| 175 | } | ||
| 174 | 176 | ||
| 175 | if (!IsControllerActivated()) { | 177 | if (!IsControllerActivated()) { |
| 176 | return; | 178 | return; |
| @@ -453,7 +455,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 453 | had_controller_update = true; | 455 | had_controller_update = true; |
| 454 | } | 456 | } |
| 455 | if (had_controller_update) { | 457 | if (had_controller_update) { |
| 456 | styleset_changed_event.writable->Signal(); | 458 | styleset_changed_events[i].writable->Signal(); |
| 457 | } | 459 | } |
| 458 | } | 460 | } |
| 459 | } | 461 | } |
| @@ -468,7 +470,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { | |||
| 468 | } | 470 | } |
| 469 | 471 | ||
| 470 | void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { | 472 | void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { |
| 471 | styleset_changed_event.writable->Signal(); | ||
| 472 | hold_type = joy_hold_type; | 473 | hold_type = joy_hold_type; |
| 473 | } | 474 | } |
| 474 | 475 | ||
| @@ -479,7 +480,10 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { | |||
| 479 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { | 480 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { |
| 480 | const std::size_t npad_index = NPadIdToIndex(npad_id); | 481 | const std::size_t npad_index = NPadIdToIndex(npad_id); |
| 481 | ASSERT(npad_index < shared_memory_entries.size()); | 482 | ASSERT(npad_index < shared_memory_entries.size()); |
| 482 | shared_memory_entries[npad_index].pad_assignment = assignment_mode; | 483 | if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { |
| 484 | styleset_changed_events[npad_index].writable->Signal(); | ||
| 485 | shared_memory_entries[npad_index].pad_assignment = assignment_mode; | ||
| 486 | } | ||
| 483 | } | 487 | } |
| 484 | 488 | ||
| 485 | void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | 489 | void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, |
| @@ -498,11 +502,13 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | |||
| 498 | last_processed_vibration = vibrations.back(); | 502 | last_processed_vibration = vibrations.back(); |
| 499 | } | 503 | } |
| 500 | 504 | ||
| 501 | Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { | 505 | Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent( |
| 506 | u32 npad_id) const { | ||
| 502 | // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should | 507 | // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should |
| 503 | // be signalled at least once, and signaled after a new controller is connected? | 508 | // be signalled at least once, and signaled after a new controller is connected? |
| 504 | styleset_changed_event.writable->Signal(); | 509 | const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; |
| 505 | return styleset_changed_event.readable; | 510 | styleset_event.writable->Signal(); |
| 511 | return styleset_event.readable; | ||
| 506 | } | 512 | } |
| 507 | 513 | ||
| 508 | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | 514 | 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? | |||
| 20 | 20 | ||
| 21 | class Controller_NPad final : public ControllerBase { | 21 | class Controller_NPad final : public ControllerBase { |
| 22 | public: | 22 | public: |
| 23 | Controller_NPad(); | 23 | explicit Controller_NPad(Core::System& system); |
| 24 | ~Controller_NPad() override; | 24 | ~Controller_NPad() override; |
| 25 | 25 | ||
| 26 | // Called when the controller is initialized | 26 | // Called when the controller is initialized |
| @@ -109,7 +109,7 @@ public: | |||
| 109 | void VibrateController(const std::vector<u32>& controller_ids, | 109 | void VibrateController(const std::vector<u32>& controller_ids, |
| 110 | const std::vector<Vibration>& vibrations); | 110 | const std::vector<Vibration>& vibrations); |
| 111 | 111 | ||
| 112 | Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; | 112 | Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; |
| 113 | Vibration GetLastVibration() const; | 113 | Vibration GetLastVibration() const; |
| 114 | 114 | ||
| 115 | void AddNewController(NPadControllerType controller); | 115 | void AddNewController(NPadControllerType controller); |
| @@ -315,7 +315,8 @@ private: | |||
| 315 | sticks; | 315 | sticks; |
| 316 | std::vector<u32> supported_npad_id_types{}; | 316 | std::vector<u32> supported_npad_id_types{}; |
| 317 | NpadHoldType hold_type{NpadHoldType::Vertical}; | 317 | NpadHoldType hold_type{NpadHoldType::Vertical}; |
| 318 | Kernel::EventPair styleset_changed_event; | 318 | // Each controller should have their own styleset changed event |
| 319 | std::array<Kernel::EventPair, 10> styleset_changed_events; | ||
| 319 | Vibration last_processed_vibration{}; | 320 | Vibration last_processed_vibration{}; |
| 320 | std::array<ControllerHolder, 10> connected_controllers{}; | 321 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 321 | bool can_controllers_vibrate{true}; | 322 | bool can_controllers_vibrate{true}; |
| @@ -327,5 +328,6 @@ private: | |||
| 327 | std::array<ControllerPad, 10> npad_pad_states{}; | 328 | std::array<ControllerPad, 10> npad_pad_states{}; |
| 328 | bool IsControllerSupported(NPadControllerType controller); | 329 | bool IsControllerSupported(NPadControllerType controller); |
| 329 | bool is_in_lr_assignment_mode{false}; | 330 | bool is_in_lr_assignment_mode{false}; |
| 331 | Core::System& system; | ||
| 330 | }; | 332 | }; |
| 331 | } // namespace Service::HID | 333 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 946948f5e..9b829341e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp | |||
| @@ -9,7 +9,8 @@ | |||
| 9 | 9 | ||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | 11 | ||
| 12 | Controller_Stubbed::Controller_Stubbed() = default; | 12 | Controller_Stubbed::Controller_Stubbed(Core::System& system) |
| 13 | : ControllerBase(system), system(system) {} | ||
| 13 | Controller_Stubbed::~Controller_Stubbed() = default; | 14 | Controller_Stubbed::~Controller_Stubbed() = default; |
| 14 | 15 | ||
| 15 | void Controller_Stubbed::OnInit() {} | 16 | 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 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | class Controller_Stubbed final : public ControllerBase { | 11 | class Controller_Stubbed final : public ControllerBase { |
| 12 | public: | 12 | public: |
| 13 | Controller_Stubbed(); | 13 | explicit Controller_Stubbed(Core::System& system); |
| 14 | ~Controller_Stubbed() override; | 14 | ~Controller_Stubbed() override; |
| 15 | 15 | ||
| 16 | // Called when the controller is initialized | 16 | // Called when the controller is initialized |
| @@ -30,5 +30,6 @@ public: | |||
| 30 | private: | 30 | private: |
| 31 | bool smart_update{}; | 31 | bool smart_update{}; |
| 32 | std::size_t common_offset{}; | 32 | std::size_t common_offset{}; |
| 33 | Core::System& system; | ||
| 33 | }; | 34 | }; |
| 34 | } // namespace Service::HID | 35 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1a8445a43..25912fd69 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -13,7 +13,8 @@ | |||
| 13 | namespace Service::HID { | 13 | namespace Service::HID { |
| 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; | 14 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; |
| 15 | 15 | ||
| 16 | Controller_Touchscreen::Controller_Touchscreen() = default; | 16 | Controller_Touchscreen::Controller_Touchscreen(Core::System& system) |
| 17 | : ControllerBase(system), system(system) {} | ||
| 17 | Controller_Touchscreen::~Controller_Touchscreen() = default; | 18 | Controller_Touchscreen::~Controller_Touchscreen() = default; |
| 18 | 19 | ||
| 19 | void Controller_Touchscreen::OnInit() {} | 20 | 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 @@ | |||
| 14 | namespace Service::HID { | 14 | namespace Service::HID { |
| 15 | class Controller_Touchscreen final : public ControllerBase { | 15 | class Controller_Touchscreen final : public ControllerBase { |
| 16 | public: | 16 | public: |
| 17 | Controller_Touchscreen(); | 17 | explicit Controller_Touchscreen(Core::System& system); |
| 18 | ~Controller_Touchscreen() override; | 18 | ~Controller_Touchscreen() override; |
| 19 | 19 | ||
| 20 | // Called when the controller is initialized | 20 | // Called when the controller is initialized |
| @@ -69,5 +69,6 @@ private: | |||
| 69 | TouchScreenSharedMemory shared_memory{}; | 69 | TouchScreenSharedMemory shared_memory{}; |
| 70 | std::unique_ptr<Input::TouchDevice> touch_device; | 70 | std::unique_ptr<Input::TouchDevice> touch_device; |
| 71 | s64_le last_touch{}; | 71 | s64_le last_touch{}; |
| 72 | Core::System& system; | ||
| 72 | }; | 73 | }; |
| 73 | } // namespace Service::HID | 74 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 1a9da9576..1bce044b4 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | namespace Service::HID { | 10 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; | 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; |
| 12 | 12 | ||
| 13 | Controller_XPad::Controller_XPad() = default; | 13 | Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {} |
| 14 | Controller_XPad::~Controller_XPad() = default; | 14 | Controller_XPad::~Controller_XPad() = default; |
| 15 | 15 | ||
| 16 | void Controller_XPad::OnInit() {} | 16 | 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 @@ | |||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | class Controller_XPad final : public ControllerBase { | 13 | class Controller_XPad final : public ControllerBase { |
| 14 | public: | 14 | public: |
| 15 | Controller_XPad(); | 15 | explicit Controller_XPad(Core::System& system); |
| 16 | ~Controller_XPad() override; | 16 | ~Controller_XPad() override; |
| 17 | 17 | ||
| 18 | // Called when the controller is initialized | 18 | // Called when the controller is initialized |
| @@ -56,5 +56,6 @@ private: | |||
| 56 | }; | 56 | }; |
| 57 | static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); | 57 | static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); |
| 58 | SharedMemory shared_memory{}; | 58 | SharedMemory shared_memory{}; |
| 59 | Core::System& system; | ||
| 59 | }; | 60 | }; |
| 60 | } // namespace Service::HID | 61 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index f8b1ca816..8d76ba746 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -42,13 +42,14 @@ constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_C | |||
| 42 | constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | 42 | constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); |
| 43 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 43 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 44 | 44 | ||
| 45 | IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { | 45 | IAppletResource::IAppletResource(Core::System& system) |
| 46 | : ServiceFramework("IAppletResource"), system(system) { | ||
| 46 | static const FunctionInfo functions[] = { | 47 | static const FunctionInfo functions[] = { |
| 47 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, | 48 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, |
| 48 | }; | 49 | }; |
| 49 | RegisterHandlers(functions); | 50 | RegisterHandlers(functions); |
| 50 | 51 | ||
| 51 | auto& kernel = Core::System::GetInstance().Kernel(); | 52 | auto& kernel = system.Kernel(); |
| 52 | shared_mem = Kernel::SharedMemory::Create( | 53 | shared_mem = Kernel::SharedMemory::Create( |
| 53 | kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, | 54 | kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, |
| 54 | Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); | 55 | Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); |
| @@ -74,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { | |||
| 74 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); | 75 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); |
| 75 | 76 | ||
| 76 | // Register update callbacks | 77 | // Register update callbacks |
| 77 | auto& core_timing = Core::System::GetInstance().CoreTiming(); | 78 | auto& core_timing = system.CoreTiming(); |
| 78 | pad_update_event = | 79 | pad_update_event = |
| 79 | core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | 80 | core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { |
| 80 | UpdateControllers(userdata, cycles_late); | 81 | UpdateControllers(userdata, cycles_late); |
| @@ -96,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) { | |||
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | IAppletResource ::~IAppletResource() { | 99 | IAppletResource ::~IAppletResource() { |
| 99 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); | 100 | system.CoreTiming().UnscheduleEvent(pad_update_event, 0); |
| 100 | } | 101 | } |
| 101 | 102 | ||
| 102 | void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | 103 | void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { |
| @@ -108,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 108 | } | 109 | } |
| 109 | 110 | ||
| 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { | 111 | void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { |
| 111 | auto& core_timing = Core::System::GetInstance().CoreTiming(); | 112 | auto& core_timing = system.CoreTiming(); |
| 112 | 113 | ||
| 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 114 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| 114 | for (const auto& controller : controllers) { | 115 | for (const auto& controller : controllers) { |
| @@ -141,13 +142,13 @@ private: | |||
| 141 | 142 | ||
| 142 | std::shared_ptr<IAppletResource> Hid::GetAppletResource() { | 143 | std::shared_ptr<IAppletResource> Hid::GetAppletResource() { |
| 143 | if (applet_resource == nullptr) { | 144 | if (applet_resource == nullptr) { |
| 144 | applet_resource = std::make_shared<IAppletResource>(); | 145 | applet_resource = std::make_shared<IAppletResource>(system); |
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | return applet_resource; | 148 | return applet_resource; |
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | Hid::Hid() : ServiceFramework("hid") { | 151 | Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { |
| 151 | // clang-format off | 152 | // clang-format off |
| 152 | static const FunctionInfo functions[] = { | 153 | static const FunctionInfo functions[] = { |
| 153 | {0, &Hid::CreateAppletResource, "CreateAppletResource"}, | 154 | {0, &Hid::CreateAppletResource, "CreateAppletResource"}, |
| @@ -286,7 +287,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) { | |||
| 286 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); | 287 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 287 | 288 | ||
| 288 | if (applet_resource == nullptr) { | 289 | if (applet_resource == nullptr) { |
| 289 | applet_resource = std::make_shared<IAppletResource>(); | 290 | applet_resource = std::make_shared<IAppletResource>(system); |
| 290 | } | 291 | } |
| 291 | 292 | ||
| 292 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 293 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| @@ -479,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { | |||
| 479 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 480 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 480 | rb.Push(RESULT_SUCCESS); | 481 | rb.Push(RESULT_SUCCESS); |
| 481 | rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) | 482 | rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 482 | .GetStyleSetChangedEvent()); | 483 | .GetStyleSetChangedEvent(npad_id)); |
| 483 | } | 484 | } |
| 484 | 485 | ||
| 485 | void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { | 486 | void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { |
| @@ -1053,14 +1054,14 @@ void ReloadInputDevices() { | |||
| 1053 | Settings::values.is_device_reload_pending.store(true); | 1054 | Settings::values.is_device_reload_pending.store(true); |
| 1054 | } | 1055 | } |
| 1055 | 1056 | ||
| 1056 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 1057 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 1057 | std::make_shared<Hid>()->InstallAsService(service_manager); | 1058 | std::make_shared<Hid>(system)->InstallAsService(service_manager); |
| 1058 | std::make_shared<HidBus>()->InstallAsService(service_manager); | 1059 | std::make_shared<HidBus>()->InstallAsService(service_manager); |
| 1059 | std::make_shared<HidDbg>()->InstallAsService(service_manager); | 1060 | std::make_shared<HidDbg>()->InstallAsService(service_manager); |
| 1060 | std::make_shared<HidSys>()->InstallAsService(service_manager); | 1061 | std::make_shared<HidSys>()->InstallAsService(service_manager); |
| 1061 | std::make_shared<HidTmp>()->InstallAsService(service_manager); | 1062 | std::make_shared<HidTmp>()->InstallAsService(service_manager); |
| 1062 | 1063 | ||
| 1063 | std::make_shared<IRS>()->InstallAsService(service_manager); | 1064 | std::make_shared<IRS>(system)->InstallAsService(service_manager); |
| 1064 | std::make_shared<IRS_SYS>()->InstallAsService(service_manager); | 1065 | std::make_shared<IRS_SYS>()->InstallAsService(service_manager); |
| 1065 | 1066 | ||
| 1066 | std::make_shared<XCD_SYS>()->InstallAsService(service_manager); | 1067 | std::make_shared<XCD_SYS>()->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 2fd6d9fc7..35b663679 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -42,7 +42,7 @@ enum class HidController : std::size_t { | |||
| 42 | 42 | ||
| 43 | class IAppletResource final : public ServiceFramework<IAppletResource> { | 43 | class IAppletResource final : public ServiceFramework<IAppletResource> { |
| 44 | public: | 44 | public: |
| 45 | IAppletResource(); | 45 | explicit IAppletResource(Core::System& system); |
| 46 | ~IAppletResource() override; | 46 | ~IAppletResource() override; |
| 47 | 47 | ||
| 48 | void ActivateController(HidController controller); | 48 | void ActivateController(HidController controller); |
| @@ -61,7 +61,7 @@ public: | |||
| 61 | private: | 61 | private: |
| 62 | template <typename T> | 62 | template <typename T> |
| 63 | void MakeController(HidController controller) { | 63 | void MakeController(HidController controller) { |
| 64 | controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(); | 64 | controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); | 67 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); |
| @@ -70,6 +70,7 @@ private: | |||
| 70 | Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; | 70 | Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; |
| 71 | 71 | ||
| 72 | Core::Timing::EventType* pad_update_event; | 72 | Core::Timing::EventType* pad_update_event; |
| 73 | Core::System& system; | ||
| 73 | 74 | ||
| 74 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | 75 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> |
| 75 | controllers{}; | 76 | controllers{}; |
| @@ -77,7 +78,7 @@ private: | |||
| 77 | 78 | ||
| 78 | class Hid final : public ServiceFramework<Hid> { | 79 | class Hid final : public ServiceFramework<Hid> { |
| 79 | public: | 80 | public: |
| 80 | Hid(); | 81 | explicit Hid(Core::System& system); |
| 81 | ~Hid() override; | 82 | ~Hid() override; |
| 82 | 83 | ||
| 83 | std::shared_ptr<IAppletResource> GetAppletResource(); | 84 | std::shared_ptr<IAppletResource> GetAppletResource(); |
| @@ -126,12 +127,13 @@ private: | |||
| 126 | void SwapNpadAssignment(Kernel::HLERequestContext& ctx); | 127 | void SwapNpadAssignment(Kernel::HLERequestContext& ctx); |
| 127 | 128 | ||
| 128 | std::shared_ptr<IAppletResource> applet_resource; | 129 | std::shared_ptr<IAppletResource> applet_resource; |
| 130 | Core::System& system; | ||
| 129 | }; | 131 | }; |
| 130 | 132 | ||
| 131 | /// Reload input devices. Used when input configuration changed | 133 | /// Reload input devices. Used when input configuration changed |
| 132 | void ReloadInputDevices(); | 134 | void ReloadInputDevices(); |
| 133 | 135 | ||
| 134 | /// Registers all HID services with the specified service manager. | 136 | /// Registers all HID services with the specified service manager. |
| 135 | void InstallInterfaces(SM::ServiceManager& service_manager); | 137 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 136 | 138 | ||
| 137 | } // namespace Service::HID | 139 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 2c4625c99..5e79e2c1a 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Service::HID { | 12 | namespace Service::HID { |
| 13 | 13 | ||
| 14 | IRS::IRS() : ServiceFramework{"irs"} { | 14 | IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) { |
| 15 | // clang-format off | 15 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 17 | {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, | 17 | {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, |
| @@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} { | |||
| 37 | 37 | ||
| 38 | RegisterHandlers(functions); | 38 | RegisterHandlers(functions); |
| 39 | 39 | ||
| 40 | auto& kernel = Core::System::GetInstance().Kernel(); | 40 | auto& kernel = system.Kernel(); |
| 41 | shared_mem = Kernel::SharedMemory::Create( | 41 | shared_mem = Kernel::SharedMemory::Create( |
| 42 | kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, | 42 | kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, |
| 43 | Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); | 43 | Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); |
| @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | |||
| 98 | 98 | ||
| 99 | IPC::ResponseBuilder rb{ctx, 5}; | 99 | IPC::ResponseBuilder rb{ctx, 5}; |
| 100 | rb.Push(RESULT_SUCCESS); | 100 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks()); | 101 | rb.PushRaw<u64>(system.CoreTiming().GetTicks()); |
| 102 | rb.PushRaw<u32>(0); | 102 | rb.PushRaw<u32>(0); |
| 103 | } | 103 | } |
| 104 | 104 | ||
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 12de6bfb3..eb4e898dd 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h | |||
| @@ -15,7 +15,7 @@ namespace Service::HID { | |||
| 15 | 15 | ||
| 16 | class IRS final : public ServiceFramework<IRS> { | 16 | class IRS final : public ServiceFramework<IRS> { |
| 17 | public: | 17 | public: |
| 18 | explicit IRS(); | 18 | explicit IRS(Core::System& system); |
| 19 | ~IRS() override; | 19 | ~IRS() override; |
| 20 | 20 | ||
| 21 | private: | 21 | private: |
| @@ -39,6 +39,7 @@ private: | |||
| 39 | void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); | 39 | void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); |
| 40 | Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; | 40 | Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; |
| 41 | const u32 device_handle{0xABCD}; | 41 | const u32 device_handle{0xABCD}; |
| 42 | Core::System& system; | ||
| 42 | }; | 43 | }; |
| 43 | 44 | ||
| 44 | class IRS_SYS final : public ServiceFramework<IRS_SYS> { | 45 | 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: | |||
| 78 | 78 | ||
| 79 | class RelocatableObject final : public ServiceFramework<RelocatableObject> { | 79 | class RelocatableObject final : public ServiceFramework<RelocatableObject> { |
| 80 | public: | 80 | public: |
| 81 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { | 81 | explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) { |
| 82 | // clang-format off | 82 | // clang-format off |
| 83 | static const FunctionInfo functions[] = { | 83 | static const FunctionInfo functions[] = { |
| 84 | {0, &RelocatableObject::LoadNro, "LoadNro"}, | 84 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| @@ -364,7 +364,7 @@ public: | |||
| 364 | vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, | 364 | vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, |
| 365 | Kernel::VMAPermission::ReadWrite); | 365 | Kernel::VMAPermission::ReadWrite); |
| 366 | 366 | ||
| 367 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 367 | system.InvalidateCpuInstructionCaches(); |
| 368 | 368 | ||
| 369 | nro.insert_or_assign(*map_address, | 369 | nro.insert_or_assign(*map_address, |
| 370 | NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); | 370 | NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); |
| @@ -430,7 +430,7 @@ public: | |||
| 430 | .IsSuccess()); | 430 | .IsSuccess()); |
| 431 | } | 431 | } |
| 432 | 432 | ||
| 433 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 433 | system.InvalidateCpuInstructionCaches(); |
| 434 | 434 | ||
| 435 | nro.erase(iter); | 435 | nro.erase(iter); |
| 436 | IPC::ResponseBuilder rb{ctx, 2}; | 436 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -516,13 +516,14 @@ private: | |||
| 516 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | 516 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && |
| 517 | Common::Is4KBAligned(header.rw_size); | 517 | Common::Is4KBAligned(header.rw_size); |
| 518 | } | 518 | } |
| 519 | Core::System& system; | ||
| 519 | }; | 520 | }; |
| 520 | 521 | ||
| 521 | void InstallInterfaces(SM::ServiceManager& sm) { | 522 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { |
| 522 | std::make_shared<DebugMonitor>()->InstallAsService(sm); | 523 | std::make_shared<DebugMonitor>()->InstallAsService(sm); |
| 523 | std::make_shared<ProcessManager>()->InstallAsService(sm); | 524 | std::make_shared<ProcessManager>()->InstallAsService(sm); |
| 524 | std::make_shared<Shell>()->InstallAsService(sm); | 525 | std::make_shared<Shell>()->InstallAsService(sm); |
| 525 | std::make_shared<RelocatableObject>()->InstallAsService(sm); | 526 | std::make_shared<RelocatableObject>(system)->InstallAsService(sm); |
| 526 | } | 527 | } |
| 527 | 528 | ||
| 528 | } // namespace Service::LDR | 529 | } // namespace Service::LDR |
diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h index 412410c4f..7ac8c0b65 100644 --- a/src/core/hle/service/ldr/ldr.h +++ b/src/core/hle/service/ldr/ldr.h | |||
| @@ -11,6 +11,6 @@ class ServiceManager; | |||
| 11 | namespace Service::LDR { | 11 | namespace Service::LDR { |
| 12 | 12 | ||
| 13 | /// Registers all LDR services with the specified service manager. | 13 | /// Registers all LDR services with the specified service manager. |
| 14 | void InstallInterfaces(SM::ServiceManager& sm); | 14 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); |
| 15 | 15 | ||
| 16 | } // namespace Service::LDR | 16 | } // namespace Service::LDR |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index a5cb06f8a..a42c22d44 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -23,9 +23,9 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, | |||
| 23 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | 23 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); |
| 24 | } // namespace ErrCodes | 24 | } // namespace ErrCodes |
| 25 | 25 | ||
| 26 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 26 | Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) |
| 27 | : ServiceFramework(name), module(std::move(module)) { | 27 | : ServiceFramework(name), module(std::move(module)), system(system) { |
| 28 | auto& kernel = Core::System::GetInstance().Kernel(); | 28 | auto& kernel = system.Kernel(); |
| 29 | nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, | 29 | nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 30 | "IUser:NFCTagDetected"); | 30 | "IUser:NFCTagDetected"); |
| 31 | } | 31 | } |
| @@ -34,8 +34,8 @@ Module::Interface::~Interface() = default; | |||
| 34 | 34 | ||
| 35 | class IUser final : public ServiceFramework<IUser> { | 35 | class IUser final : public ServiceFramework<IUser> { |
| 36 | public: | 36 | public: |
| 37 | IUser(Module::Interface& nfp_interface) | 37 | IUser(Module::Interface& nfp_interface, Core::System& system) |
| 38 | : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { | 38 | : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) { |
| 39 | static const FunctionInfo functions[] = { | 39 | static const FunctionInfo functions[] = { |
| 40 | {0, &IUser::Initialize, "Initialize"}, | 40 | {0, &IUser::Initialize, "Initialize"}, |
| 41 | {1, &IUser::Finalize, "Finalize"}, | 41 | {1, &IUser::Finalize, "Finalize"}, |
| @@ -65,7 +65,7 @@ public: | |||
| 65 | }; | 65 | }; |
| 66 | RegisterHandlers(functions); | 66 | RegisterHandlers(functions); |
| 67 | 67 | ||
| 68 | auto& kernel = Core::System::GetInstance().Kernel(); | 68 | auto& kernel = system.Kernel(); |
| 69 | deactivate_event = Kernel::WritableEvent::CreateEventPair( | 69 | deactivate_event = Kernel::WritableEvent::CreateEventPair( |
| 70 | kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); | 70 | kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); |
| 71 | availability_change_event = Kernel::WritableEvent::CreateEventPair( | 71 | availability_change_event = Kernel::WritableEvent::CreateEventPair( |
| @@ -324,6 +324,7 @@ private: | |||
| 324 | Kernel::EventPair deactivate_event; | 324 | Kernel::EventPair deactivate_event; |
| 325 | Kernel::EventPair availability_change_event; | 325 | Kernel::EventPair availability_change_event; |
| 326 | const Module::Interface& nfp_interface; | 326 | const Module::Interface& nfp_interface; |
| 327 | Core::System& system; | ||
| 327 | }; | 328 | }; |
| 328 | 329 | ||
| 329 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | 330 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { |
| @@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | |||
| 331 | 332 | ||
| 332 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 333 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 333 | rb.Push(RESULT_SUCCESS); | 334 | rb.Push(RESULT_SUCCESS); |
| 334 | rb.PushIpcInterface<IUser>(*this); | 335 | rb.PushIpcInterface<IUser>(*this, system); |
| 335 | } | 336 | } |
| 336 | 337 | ||
| 337 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { | 338 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { |
| @@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const | |||
| 353 | return amiibo; | 354 | return amiibo; |
| 354 | } | 355 | } |
| 355 | 356 | ||
| 356 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 357 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 357 | auto module = std::make_shared<Module>(); | 358 | auto module = std::make_shared<Module>(); |
| 358 | std::make_shared<NFP_User>(module)->InstallAsService(service_manager); | 359 | std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 361 | } // namespace Service::NFP | 362 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index a1817e991..9718ef745 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -16,7 +16,7 @@ class Module final { | |||
| 16 | public: | 16 | public: |
| 17 | class Interface : public ServiceFramework<Interface> { | 17 | class Interface : public ServiceFramework<Interface> { |
| 18 | public: | 18 | public: |
| 19 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 19 | explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); |
| 20 | ~Interface() override; | 20 | ~Interface() override; |
| 21 | 21 | ||
| 22 | struct ModelInfo { | 22 | struct ModelInfo { |
| @@ -43,9 +43,10 @@ public: | |||
| 43 | 43 | ||
| 44 | protected: | 44 | protected: |
| 45 | std::shared_ptr<Module> module; | 45 | std::shared_ptr<Module> module; |
| 46 | Core::System& system; | ||
| 46 | }; | 47 | }; |
| 47 | }; | 48 | }; |
| 48 | 49 | ||
| 49 | void InstallInterfaces(SM::ServiceManager& service_manager); | 50 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 50 | 51 | ||
| 51 | } // namespace Service::NFP | 52 | } // namespace Service::NFP |
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 784a87c1b..298184f17 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | namespace Service::NFP { | 7 | namespace Service::NFP { |
| 8 | 8 | ||
| 9 | NFP_User::NFP_User(std::shared_ptr<Module> module) | 9 | NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system) |
| 10 | : Module::Interface(std::move(module), "nfp:user") { | 10 | : Module::Interface(std::move(module), system, "nfp:user") { |
| 11 | static const FunctionInfo functions[] = { | 11 | static const FunctionInfo functions[] = { |
| 12 | {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, | 12 | {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, |
| 13 | }; | 13 | }; |
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 65d9aaf48..1686ebf20 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h | |||
| @@ -10,7 +10,7 @@ namespace Service::NFP { | |||
| 10 | 10 | ||
| 11 | class NFP_User final : public Module::Interface { | 11 | class NFP_User final : public Module::Interface { |
| 12 | public: | 12 | public: |
| 13 | explicit NFP_User(std::shared_ptr<Module> module); | 13 | explicit NFP_User(std::shared_ptr<Module> module, Core::System& system); |
| 14 | ~NFP_User() override; | 14 | ~NFP_User() override; |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 76b12b482..24d1813a7 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | 31 | ||
| 32 | class IRequest final : public ServiceFramework<IRequest> { | 32 | class IRequest final : public ServiceFramework<IRequest> { |
| 33 | public: | 33 | public: |
| 34 | explicit IRequest() : ServiceFramework("IRequest") { | 34 | explicit IRequest(Core::System& system) : ServiceFramework("IRequest") { |
| 35 | static const FunctionInfo functions[] = { | 35 | static const FunctionInfo functions[] = { |
| 36 | {0, &IRequest::GetRequestState, "GetRequestState"}, | 36 | {0, &IRequest::GetRequestState, "GetRequestState"}, |
| 37 | {1, &IRequest::GetResult, "GetResult"}, | 37 | {1, &IRequest::GetResult, "GetResult"}, |
| @@ -61,7 +61,7 @@ public: | |||
| 61 | }; | 61 | }; |
| 62 | RegisterHandlers(functions); | 62 | RegisterHandlers(functions); |
| 63 | 63 | ||
| 64 | auto& kernel = Core::System::GetInstance().Kernel(); | 64 | auto& kernel = system.Kernel(); |
| 65 | event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, | 65 | event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| 66 | "IRequest:Event1"); | 66 | "IRequest:Event1"); |
| 67 | event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, | 67 | event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, |
| @@ -130,7 +130,7 @@ public: | |||
| 130 | 130 | ||
| 131 | class IGeneralService final : public ServiceFramework<IGeneralService> { | 131 | class IGeneralService final : public ServiceFramework<IGeneralService> { |
| 132 | public: | 132 | public: |
| 133 | IGeneralService(); | 133 | IGeneralService(Core::System& system); |
| 134 | 134 | ||
| 135 | private: | 135 | private: |
| 136 | void GetClientId(Kernel::HLERequestContext& ctx) { | 136 | void GetClientId(Kernel::HLERequestContext& ctx) { |
| @@ -155,7 +155,7 @@ private: | |||
| 155 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 155 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 156 | 156 | ||
| 157 | rb.Push(RESULT_SUCCESS); | 157 | rb.Push(RESULT_SUCCESS); |
| 158 | rb.PushIpcInterface<IRequest>(); | 158 | rb.PushIpcInterface<IRequest>(system); |
| 159 | } | 159 | } |
| 160 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { | 160 | void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 161 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 161 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| @@ -198,9 +198,11 @@ private: | |||
| 198 | rb.Push(RESULT_SUCCESS); | 198 | rb.Push(RESULT_SUCCESS); |
| 199 | rb.Push<u8>(0); | 199 | rb.Push<u8>(0); |
| 200 | } | 200 | } |
| 201 | Core::System& system; | ||
| 201 | }; | 202 | }; |
| 202 | 203 | ||
| 203 | IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | 204 | IGeneralService::IGeneralService(Core::System& system) |
| 205 | : ServiceFramework("IGeneralService"), system(system) { | ||
| 204 | static const FunctionInfo functions[] = { | 206 | static const FunctionInfo functions[] = { |
| 205 | {1, &IGeneralService::GetClientId, "GetClientId"}, | 207 | {1, &IGeneralService::GetClientId, "GetClientId"}, |
| 206 | {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, | 208 | {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, |
| @@ -245,7 +247,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | |||
| 245 | 247 | ||
| 246 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { | 248 | class NetworkInterface final : public ServiceFramework<NetworkInterface> { |
| 247 | public: | 249 | public: |
| 248 | explicit NetworkInterface(const char* name) : ServiceFramework{name} { | 250 | explicit NetworkInterface(const char* name, Core::System& system) |
| 251 | : ServiceFramework{name}, system(system) { | ||
| 249 | static const FunctionInfo functions[] = { | 252 | static const FunctionInfo functions[] = { |
| 250 | {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, | 253 | {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, |
| 251 | {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"}, | 254 | {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"}, |
| @@ -258,7 +261,7 @@ public: | |||
| 258 | 261 | ||
| 259 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 262 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 260 | rb.Push(RESULT_SUCCESS); | 263 | rb.Push(RESULT_SUCCESS); |
| 261 | rb.PushIpcInterface<IGeneralService>(); | 264 | rb.PushIpcInterface<IGeneralService>(system); |
| 262 | } | 265 | } |
| 263 | 266 | ||
| 264 | void CreateGeneralService(Kernel::HLERequestContext& ctx) { | 267 | void CreateGeneralService(Kernel::HLERequestContext& ctx) { |
| @@ -266,14 +269,17 @@ public: | |||
| 266 | 269 | ||
| 267 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 270 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 268 | rb.Push(RESULT_SUCCESS); | 271 | rb.Push(RESULT_SUCCESS); |
| 269 | rb.PushIpcInterface<IGeneralService>(); | 272 | rb.PushIpcInterface<IGeneralService>(system); |
| 270 | } | 273 | } |
| 274 | |||
| 275 | private: | ||
| 276 | Core::System& system; | ||
| 271 | }; | 277 | }; |
| 272 | 278 | ||
| 273 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 279 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 274 | std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager); | 280 | std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager); |
| 275 | std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager); | 281 | std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager); |
| 276 | std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager); | 282 | std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager); |
| 277 | } | 283 | } |
| 278 | 284 | ||
| 279 | } // namespace Service::NIFM | 285 | } // namespace Service::NIFM |
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 4616b3b48..6857e18f9 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h | |||
| @@ -8,9 +8,13 @@ namespace Service::SM { | |||
| 8 | class ServiceManager; | 8 | class ServiceManager; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Service::NIFM { | 15 | namespace Service::NIFM { |
| 12 | 16 | ||
| 13 | /// Registers all NIFM services with the specified service manager. | 17 | /// Registers all NIFM services with the specified service manager. |
| 14 | void InstallInterfaces(SM::ServiceManager& service_manager); | 18 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 15 | 19 | ||
| 16 | } // namespace Service::NIFM | 20 | } // namespace Service::NIFM |
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index f319a3ca1..75d414952 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -126,7 +126,7 @@ public: | |||
| 126 | class IEnsureNetworkClockAvailabilityService final | 126 | class IEnsureNetworkClockAvailabilityService final |
| 127 | : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { | 127 | : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { |
| 128 | public: | 128 | public: |
| 129 | IEnsureNetworkClockAvailabilityService() | 129 | explicit IEnsureNetworkClockAvailabilityService(Core::System& system) |
| 130 | : ServiceFramework("IEnsureNetworkClockAvailabilityService") { | 130 | : ServiceFramework("IEnsureNetworkClockAvailabilityService") { |
| 131 | static const FunctionInfo functions[] = { | 131 | static const FunctionInfo functions[] = { |
| 132 | {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, | 132 | {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, |
| @@ -139,7 +139,7 @@ public: | |||
| 139 | }; | 139 | }; |
| 140 | RegisterHandlers(functions); | 140 | RegisterHandlers(functions); |
| 141 | 141 | ||
| 142 | auto& kernel = Core::System::GetInstance().Kernel(); | 142 | auto& kernel = system.Kernel(); |
| 143 | finished_event = Kernel::WritableEvent::CreateEventPair( | 143 | finished_event = Kernel::WritableEvent::CreateEventPair( |
| 144 | kernel, Kernel::ResetType::Automatic, | 144 | kernel, Kernel::ResetType::Automatic, |
| 145 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); | 145 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); |
| @@ -200,7 +200,7 @@ private: | |||
| 200 | 200 | ||
| 201 | class NTC final : public ServiceFramework<NTC> { | 201 | class NTC final : public ServiceFramework<NTC> { |
| 202 | public: | 202 | public: |
| 203 | explicit NTC() : ServiceFramework{"ntc"} { | 203 | explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) { |
| 204 | // clang-format off | 204 | // clang-format off |
| 205 | static const FunctionInfo functions[] = { | 205 | static const FunctionInfo functions[] = { |
| 206 | {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, | 206 | {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, |
| @@ -218,7 +218,7 @@ private: | |||
| 218 | 218 | ||
| 219 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 219 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 220 | rb.Push(RESULT_SUCCESS); | 220 | rb.Push(RESULT_SUCCESS); |
| 221 | rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); | 221 | rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | // TODO(ogniK): Do we need these? | 224 | // TODO(ogniK): Do we need these? |
| @@ -235,13 +235,14 @@ private: | |||
| 235 | IPC::ResponseBuilder rb{ctx, 2}; | 235 | IPC::ResponseBuilder rb{ctx, 2}; |
| 236 | rb.Push(RESULT_SUCCESS); | 236 | rb.Push(RESULT_SUCCESS); |
| 237 | } | 237 | } |
| 238 | Core::System& system; | ||
| 238 | }; | 239 | }; |
| 239 | 240 | ||
| 240 | void InstallInterfaces(SM::ServiceManager& sm) { | 241 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { |
| 241 | std::make_shared<NIM>()->InstallAsService(sm); | 242 | std::make_shared<NIM>()->InstallAsService(sm); |
| 242 | std::make_shared<NIM_ECA>()->InstallAsService(sm); | 243 | std::make_shared<NIM_ECA>()->InstallAsService(sm); |
| 243 | std::make_shared<NIM_SHP>()->InstallAsService(sm); | 244 | std::make_shared<NIM_SHP>()->InstallAsService(sm); |
| 244 | std::make_shared<NTC>()->InstallAsService(sm); | 245 | std::make_shared<NTC>(system)->InstallAsService(sm); |
| 245 | } | 246 | } |
| 246 | 247 | ||
| 247 | } // namespace Service::NIM | 248 | } // namespace Service::NIM |
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index 2a2a92df0..dbe25dc01 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h | |||
| @@ -8,8 +8,12 @@ namespace Service::SM { | |||
| 8 | class ServiceManager; | 8 | class ServiceManager; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Service::NIM { | 15 | namespace Service::NIM { |
| 12 | 16 | ||
| 13 | void InstallInterfaces(SM::ServiceManager& sm); | 17 | void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); |
| 14 | 18 | ||
| 15 | } // namespace Service::NIM | 19 | } // namespace Service::NIM |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index ce88a2941..15c156ce1 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -617,7 +617,8 @@ public: | |||
| 617 | } | 617 | } |
| 618 | }; | 618 | }; |
| 619 | 619 | ||
| 620 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 620 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 621 | |||
| 621 | std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); | 622 | std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); |
| 622 | std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); | 623 | std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); |
| 623 | std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); | 624 | std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); |
| @@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { | |||
| 628 | std::make_shared<NS_SU>()->InstallAsService(service_manager); | 629 | std::make_shared<NS_SU>()->InstallAsService(service_manager); |
| 629 | std::make_shared<NS_VM>()->InstallAsService(service_manager); | 630 | std::make_shared<NS_VM>()->InstallAsService(service_manager); |
| 630 | 631 | ||
| 631 | std::make_shared<PL_U>()->InstallAsService(service_manager); | 632 | std::make_shared<PL_U>(system)->InstallAsService(service_manager); |
| 632 | } | 633 | } |
| 633 | 634 | ||
| 634 | } // namespace Service::NS | 635 | } // namespace Service::NS |
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 0e8256cb4..13a64ad88 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h | |||
| @@ -6,7 +6,13 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | 8 | ||
| 9 | namespace Service::NS { | 9 | namespace Service { |
| 10 | |||
| 11 | namespace FileSystem { | ||
| 12 | class FileSystemController; | ||
| 13 | } // namespace FileSystem | ||
| 14 | |||
| 15 | namespace NS { | ||
| 10 | 16 | ||
| 11 | class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { | 17 | class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { |
| 12 | public: | 18 | public: |
| @@ -91,6 +97,7 @@ private: | |||
| 91 | }; | 97 | }; |
| 92 | 98 | ||
| 93 | /// Registers all NS services with the specified service manager. | 99 | /// Registers all NS services with the specified service manager. |
| 94 | void InstallInterfaces(SM::ServiceManager& service_manager); | 100 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 95 | 101 | ||
| 96 | } // namespace Service::NS | 102 | } // namespace NS |
| 103 | } // namespace Service | ||
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 2a522136d..7dcdb4a07 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -150,7 +150,9 @@ struct PL_U::Impl { | |||
| 150 | std::vector<FontRegion> shared_font_regions; | 150 | std::vector<FontRegion> shared_font_regions; |
| 151 | }; | 151 | }; |
| 152 | 152 | ||
| 153 | PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | 153 | PL_U::PL_U(Core::System& system) |
| 154 | : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) { | ||
| 155 | |||
| 154 | static const FunctionInfo functions[] = { | 156 | static const FunctionInfo functions[] = { |
| 155 | {0, &PL_U::RequestLoad, "RequestLoad"}, | 157 | {0, &PL_U::RequestLoad, "RequestLoad"}, |
| 156 | {1, &PL_U::GetLoadState, "GetLoadState"}, | 158 | {1, &PL_U::GetLoadState, "GetLoadState"}, |
| @@ -160,8 +162,11 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | |||
| 160 | {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, | 162 | {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, |
| 161 | }; | 163 | }; |
| 162 | RegisterHandlers(functions); | 164 | RegisterHandlers(functions); |
| 165 | |||
| 166 | auto& fsc = system.GetFileSystemController(); | ||
| 167 | |||
| 163 | // Attempt to load shared font data from disk | 168 | // Attempt to load shared font data from disk |
| 164 | const auto* nand = FileSystem::GetSystemNANDContents(); | 169 | const auto* nand = fsc.GetSystemNANDContents(); |
| 165 | std::size_t offset = 0; | 170 | std::size_t offset = 0; |
| 166 | // Rebuild shared fonts from data ncas | 171 | // Rebuild shared fonts from data ncas |
| 167 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), | 172 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), |
| @@ -324,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | |||
| 324 | Kernel::MemoryState::Shared); | 329 | Kernel::MemoryState::Shared); |
| 325 | 330 | ||
| 326 | // Create shared font memory object | 331 | // Create shared font memory object |
| 327 | auto& kernel = Core::System::GetInstance().Kernel(); | 332 | auto& kernel = system.Kernel(); |
| 328 | impl->shared_font_mem = Kernel::SharedMemory::Create( | 333 | impl->shared_font_mem = Kernel::SharedMemory::Create( |
| 329 | kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, | 334 | kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, |
| 330 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, | 335 | Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, |
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h index 253f26a2a..1063f4204 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/pl_u.h | |||
| @@ -7,11 +7,17 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 9 | 9 | ||
| 10 | namespace Service::NS { | 10 | namespace Service { |
| 11 | |||
| 12 | namespace FileSystem { | ||
| 13 | class FileSystemController; | ||
| 14 | } // namespace FileSystem | ||
| 15 | |||
| 16 | namespace NS { | ||
| 11 | 17 | ||
| 12 | class PL_U final : public ServiceFramework<PL_U> { | 18 | class PL_U final : public ServiceFramework<PL_U> { |
| 13 | public: | 19 | public: |
| 14 | PL_U(); | 20 | explicit PL_U(Core::System& system); |
| 15 | ~PL_U() override; | 21 | ~PL_U() override; |
| 16 | 22 | ||
| 17 | private: | 23 | private: |
| @@ -24,6 +30,9 @@ private: | |||
| 24 | 30 | ||
| 25 | struct Impl; | 31 | struct Impl; |
| 26 | std::unique_ptr<Impl> impl; | 32 | std::unique_ptr<Impl> impl; |
| 33 | Core::System& system; | ||
| 27 | }; | 34 | }; |
| 28 | 35 | ||
| 29 | } // namespace Service::NS | 36 | } // namespace NS |
| 37 | |||
| 38 | } // namespace Service | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 5b8248433..1b52511a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/hle/service/nvdrv/nvdata.h" | 11 | #include "core/hle/service/nvdrv/nvdata.h" |
| 12 | #include "core/hle/service/service.h" | ||
| 12 | 13 | ||
| 13 | namespace Core { | 14 | namespace Core { |
| 14 | class System; | 15 | class System; |
| @@ -38,8 +39,9 @@ public: | |||
| 38 | * @param output A buffer where the output data will be written to. | 39 | * @param output A buffer where the output data will be written to. |
| 39 | * @returns The result code of the ioctl. | 40 | * @returns The result code of the ioctl. |
| 40 | */ | 41 | */ |
| 41 | virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 42 | virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 42 | IoctlCtrl& ctrl) = 0; | 43 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 44 | IoctlVersion version) = 0; | ||
| 43 | 45 | ||
| 44 | protected: | 46 | protected: |
| 45 | Core::System& system; | 47 | Core::System& system; |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 926a1285d..f764388bc 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -17,8 +17,9 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de | |||
| 17 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 17 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| 18 | nvdisp_disp0 ::~nvdisp_disp0() = default; | 18 | nvdisp_disp0 ::~nvdisp_disp0() = default; |
| 19 | 19 | ||
| 20 | u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 20 | u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 21 | IoctlCtrl& ctrl) { | 21 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 22 | IoctlVersion version) { | ||
| 22 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | 23 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); |
| 23 | return 0; | 24 | return 0; |
| 24 | } | 25 | } |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index e79e490ff..6fcdeee84 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h | |||
| @@ -20,8 +20,9 @@ public: | |||
| 20 | explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 20 | explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 21 | ~nvdisp_disp0() override; | 21 | ~nvdisp_disp0() override; |
| 22 | 22 | ||
| 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 24 | IoctlCtrl& ctrl) override; | 24 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 25 | IoctlVersion version) override; | ||
| 25 | 26 | ||
| 26 | /// Performs a screen flip, drawing the buffer pointed to by the handle. | 27 | /// Performs a screen flip, drawing the buffer pointed to by the handle. |
| 27 | void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, | 28 | void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 24ab3f2e9..6bc053f27 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -26,8 +26,9 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_ | |||
| 26 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 26 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| 27 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 27 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| 28 | 28 | ||
| 29 | u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 29 | u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 30 | IoctlCtrl& ctrl) { | 30 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 31 | IoctlVersion version) { | ||
| 31 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 32 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 32 | command.raw, input.size(), output.size()); | 33 | command.raw, input.size(), output.size()); |
| 33 | 34 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 30ca5f4c3..169fb8f0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -20,8 +20,9 @@ public: | |||
| 20 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 20 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 21 | ~nvhost_as_gpu() override; | 21 | ~nvhost_as_gpu() override; |
| 22 | 22 | ||
| 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 23 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 24 | IoctlCtrl& ctrl) override; | 24 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 25 | IoctlVersion version) override; | ||
| 25 | 26 | ||
| 26 | private: | 27 | private: |
| 27 | enum class IoctlCommand : u32_le { | 28 | enum class IoctlCommand : u32_le { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 9a66a5f88..ff6b1abae 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -19,8 +19,9 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) | |||
| 19 | : nvdevice(system), events_interface{events_interface} {} | 19 | : nvdevice(system), events_interface{events_interface} {} |
| 20 | nvhost_ctrl::~nvhost_ctrl() = default; | 20 | nvhost_ctrl::~nvhost_ctrl() = default; |
| 21 | 21 | ||
| 22 | u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 22 | u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 23 | IoctlCtrl& ctrl) { | 23 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 24 | IoctlVersion version) { | ||
| 24 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 25 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 25 | command.raw, input.size(), output.size()); | 26 | command.raw, input.size(), output.size()); |
| 26 | 27 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 14e6e7e57..9898623de 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h | |||
| @@ -17,8 +17,9 @@ public: | |||
| 17 | explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); | 17 | explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); |
| 18 | ~nvhost_ctrl() override; | 18 | ~nvhost_ctrl() override; |
| 19 | 19 | ||
| 20 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 20 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 21 | IoctlCtrl& ctrl) override; | 21 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 22 | IoctlVersion version) override; | ||
| 22 | 23 | ||
| 23 | private: | 24 | private: |
| 24 | enum class IoctlCommand : u32_le { | 25 | enum class IoctlCommand : u32_le { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 988effd90..389ace76f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp | |||
| @@ -15,14 +15,15 @@ namespace Service::Nvidia::Devices { | |||
| 15 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} | 15 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} |
| 16 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; | 16 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; |
| 17 | 17 | ||
| 18 | u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 18 | u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, |
| 19 | IoctlCtrl& ctrl) { | 19 | const std::vector<u8>& input2, std::vector<u8>& output, |
| 20 | std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) { | ||
| 20 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 21 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 21 | command.raw, input.size(), output.size()); | 22 | command.raw, input.size(), output.size()); |
| 22 | 23 | ||
| 23 | switch (static_cast<IoctlCommand>(command.raw)) { | 24 | switch (static_cast<IoctlCommand>(command.raw)) { |
| 24 | case IoctlCommand::IocGetCharacteristicsCommand: | 25 | case IoctlCommand::IocGetCharacteristicsCommand: |
| 25 | return GetCharacteristics(input, output); | 26 | return GetCharacteristics(input, output, output2, version); |
| 26 | case IoctlCommand::IocGetTPCMasksCommand: | 27 | case IoctlCommand::IocGetTPCMasksCommand: |
| 27 | return GetTPCMasks(input, output); | 28 | return GetTPCMasks(input, output); |
| 28 | case IoctlCommand::IocGetActiveSlotMaskCommand: | 29 | case IoctlCommand::IocGetActiveSlotMaskCommand: |
| @@ -44,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec | |||
| 44 | return 0; | 45 | return 0; |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { | 48 | u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, |
| 49 | std::vector<u8>& output2, IoctlVersion version) { | ||
| 48 | LOG_DEBUG(Service_NVDRV, "called"); | 50 | LOG_DEBUG(Service_NVDRV, "called"); |
| 49 | IoctlCharacteristics params{}; | 51 | IoctlCharacteristics params{}; |
| 50 | std::memcpy(¶ms, input.data(), input.size()); | 52 | std::memcpy(¶ms, input.data(), input.size()); |
| @@ -85,7 +87,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto | |||
| 85 | params.gc.gr_compbit_store_base_hw = 0x0; | 87 | params.gc.gr_compbit_store_base_hw = 0x0; |
| 86 | params.gpu_characteristics_buf_size = 0xA0; | 88 | params.gpu_characteristics_buf_size = 0xA0; |
| 87 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) | 89 | params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) |
| 88 | std::memcpy(output.data(), ¶ms, output.size()); | 90 | |
| 91 | if (version == IoctlVersion::Version3) { | ||
| 92 | std::memcpy(output.data(), input.data(), output.size()); | ||
| 93 | std::memcpy(output2.data(), ¶ms.gc, output2.size()); | ||
| 94 | } else { | ||
| 95 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 96 | } | ||
| 89 | return 0; | 97 | return 0; |
| 90 | } | 98 | } |
| 91 | 99 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 2b035ae3f..642b0a2cb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h | |||
| @@ -16,8 +16,9 @@ public: | |||
| 16 | explicit nvhost_ctrl_gpu(Core::System& system); | 16 | explicit nvhost_ctrl_gpu(Core::System& system); |
| 17 | ~nvhost_ctrl_gpu() override; | 17 | ~nvhost_ctrl_gpu() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 20 | IoctlCtrl& ctrl) override; | 20 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 21 | IoctlVersion version) override; | ||
| 21 | 22 | ||
| 22 | private: | 23 | private: |
| 23 | enum class IoctlCommand : u32_le { | 24 | enum class IoctlCommand : u32_le { |
| @@ -162,7 +163,8 @@ private: | |||
| 162 | }; | 163 | }; |
| 163 | static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); | 164 | static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); |
| 164 | 165 | ||
| 165 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); | 166 | u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, |
| 167 | std::vector<u8>& output2, IoctlVersion version); | ||
| 166 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); | 168 | u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); |
| 167 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); | 169 | u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); |
| 168 | u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); | 170 | u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 241dac881..2b8d1bef6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -17,8 +17,9 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | |||
| 17 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 17 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| 18 | nvhost_gpu::~nvhost_gpu() = default; | 18 | nvhost_gpu::~nvhost_gpu() = default; |
| 19 | 19 | ||
| 20 | u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 20 | u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 21 | IoctlCtrl& ctrl) { | 21 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 22 | IoctlVersion version) { | ||
| 22 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 23 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 23 | command.raw, input.size(), output.size()); | 24 | command.raw, input.size(), output.size()); |
| 24 | 25 | ||
| @@ -50,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u | |||
| 50 | return SubmitGPFIFO(input, output); | 51 | return SubmitGPFIFO(input, output); |
| 51 | } | 52 | } |
| 52 | if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { | 53 | if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { |
| 53 | return KickoffPB(input, output); | 54 | return KickoffPB(input, output, input2, version); |
| 54 | } | 55 | } |
| 55 | } | 56 | } |
| 56 | 57 | ||
| @@ -146,8 +147,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp | |||
| 146 | } | 147 | } |
| 147 | IoctlSubmitGpfifo params{}; | 148 | IoctlSubmitGpfifo params{}; |
| 148 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | 149 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); |
| 149 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", | 150 | LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, |
| 150 | params.address, params.num_entries, params.flags.raw); | 151 | params.num_entries, params.flags.raw); |
| 151 | 152 | ||
| 152 | ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + | 153 | ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + |
| 153 | params.num_entries * sizeof(Tegra::CommandListHeader), | 154 | params.num_entries * sizeof(Tegra::CommandListHeader), |
| @@ -173,19 +174,24 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp | |||
| 173 | return 0; | 174 | return 0; |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) { | 177 | u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, |
| 178 | const std::vector<u8>& input2, IoctlVersion version) { | ||
| 177 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { | 179 | if (input.size() < sizeof(IoctlSubmitGpfifo)) { |
| 178 | UNIMPLEMENTED(); | 180 | UNIMPLEMENTED(); |
| 179 | } | 181 | } |
| 180 | IoctlSubmitGpfifo params{}; | 182 | IoctlSubmitGpfifo params{}; |
| 181 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | 183 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); |
| 182 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", | 184 | LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, |
| 183 | params.address, params.num_entries, params.flags.raw); | 185 | params.num_entries, params.flags.raw); |
| 184 | 186 | ||
| 185 | Tegra::CommandList entries(params.num_entries); | 187 | Tegra::CommandList entries(params.num_entries); |
| 186 | Memory::ReadBlock(params.address, entries.data(), | 188 | if (version == IoctlVersion::Version2) { |
| 187 | params.num_entries * sizeof(Tegra::CommandListHeader)); | 189 | std::memcpy(entries.data(), input2.data(), |
| 188 | 190 | params.num_entries * sizeof(Tegra::CommandListHeader)); | |
| 191 | } else { | ||
| 192 | Memory::ReadBlock(params.address, entries.data(), | ||
| 193 | params.num_entries * sizeof(Tegra::CommandListHeader)); | ||
| 194 | } | ||
| 189 | UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); | 195 | UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); |
| 190 | UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); | 196 | UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); |
| 191 | 197 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index d2e8fbae9..d056dd046 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h | |||
| @@ -24,8 +24,9 @@ public: | |||
| 24 | explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 24 | explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 25 | ~nvhost_gpu() override; | 25 | ~nvhost_gpu() override; |
| 26 | 26 | ||
| 27 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 27 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 28 | IoctlCtrl& ctrl) override; | 28 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 29 | IoctlVersion version) override; | ||
| 29 | 30 | ||
| 30 | private: | 31 | private: |
| 31 | enum class IoctlCommand : u32_le { | 32 | enum class IoctlCommand : u32_le { |
| @@ -183,7 +184,8 @@ private: | |||
| 183 | u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); | 184 | u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); |
| 184 | u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); | 185 | u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); |
| 185 | u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); | 186 | u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); |
| 186 | u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output); | 187 | u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, |
| 188 | const std::vector<u8>& input2, IoctlVersion version); | ||
| 187 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); | 189 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); |
| 188 | u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); | 190 | u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); |
| 189 | 191 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f572ad30f..bdae8b887 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices { | |||
| 13 | nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} | 13 | nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} |
| 14 | nvhost_nvdec::~nvhost_nvdec() = default; | 14 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 15 | 15 | ||
| 16 | u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 16 | u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 17 | IoctlCtrl& ctrl) { | 17 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 18 | IoctlVersion version) { | ||
| 18 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 19 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 19 | command.raw, input.size(), output.size()); | 20 | command.raw, input.size(), output.size()); |
| 20 | 21 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 2710f0511..cbdac8069 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -16,8 +16,9 @@ public: | |||
| 16 | explicit nvhost_nvdec(Core::System& system); | 16 | explicit nvhost_nvdec(Core::System& system); |
| 17 | ~nvhost_nvdec() override; | 17 | ~nvhost_nvdec() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 20 | IoctlCtrl& ctrl) override; | 20 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 21 | IoctlVersion version) override; | ||
| 21 | 22 | ||
| 22 | private: | 23 | private: |
| 23 | enum class IoctlCommand : u32_le { | 24 | enum class IoctlCommand : u32_le { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 38282956f..96e7b7dab 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp | |||
| @@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices { | |||
| 13 | nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} | 13 | nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} |
| 14 | nvhost_nvjpg::~nvhost_nvjpg() = default; | 14 | nvhost_nvjpg::~nvhost_nvjpg() = default; |
| 15 | 15 | ||
| 16 | u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 16 | u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 17 | IoctlCtrl& ctrl) { | 17 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 18 | IoctlVersion version) { | ||
| 18 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 19 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 19 | command.raw, input.size(), output.size()); | 20 | command.raw, input.size(), output.size()); |
| 20 | 21 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 379766693..98dcac52f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h | |||
| @@ -16,8 +16,9 @@ public: | |||
| 16 | explicit nvhost_nvjpg(Core::System& system); | 16 | explicit nvhost_nvjpg(Core::System& system); |
| 17 | ~nvhost_nvjpg() override; | 17 | ~nvhost_nvjpg() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 20 | IoctlCtrl& ctrl) override; | 20 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 21 | IoctlVersion version) override; | ||
| 21 | 22 | ||
| 22 | private: | 23 | private: |
| 23 | enum class IoctlCommand : u32_le { | 24 | enum class IoctlCommand : u32_le { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 70e8091db..c695b8863 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices { | |||
| 13 | nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} | 13 | nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} |
| 14 | nvhost_vic::~nvhost_vic() = default; | 14 | nvhost_vic::~nvhost_vic() = default; |
| 15 | 15 | ||
| 16 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 16 | u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 17 | IoctlCtrl& ctrl) { | 17 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 18 | IoctlVersion version) { | ||
| 18 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", | 19 | LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", |
| 19 | command.raw, input.size(), output.size()); | 20 | command.raw, input.size(), output.size()); |
| 20 | 21 | ||
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 7d111977e..bec32bea1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -16,8 +16,9 @@ public: | |||
| 16 | explicit nvhost_vic(Core::System& system); | 16 | explicit nvhost_vic(Core::System& system); |
| 17 | ~nvhost_vic() override; | 17 | ~nvhost_vic() override; |
| 18 | 18 | ||
| 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 20 | IoctlCtrl& ctrl) override; | 20 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 21 | IoctlVersion version) override; | ||
| 21 | 22 | ||
| 22 | private: | 23 | private: |
| 23 | enum class IoctlCommand : u32_le { | 24 | enum class IoctlCommand : u32_le { |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 223b496b7..8c742316c 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -28,8 +28,9 @@ VAddr nvmap::GetObjectAddress(u32 handle) const { | |||
| 28 | return object->addr; | 28 | return object->addr; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 31 | u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 32 | IoctlCtrl& ctrl) { | 32 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 33 | IoctlVersion version) { | ||
| 33 | switch (static_cast<IoctlCommand>(command.raw)) { | 34 | switch (static_cast<IoctlCommand>(command.raw)) { |
| 34 | case IoctlCommand::Create: | 35 | case IoctlCommand::Create: |
| 35 | return IocCreate(input, output); | 36 | return IocCreate(input, output); |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index bf4a101c2..73c2e8809 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -22,8 +22,9 @@ public: | |||
| 22 | /// Returns the allocated address of an nvmap object given its handle. | 22 | /// Returns the allocated address of an nvmap object given its handle. |
| 23 | VAddr GetObjectAddress(u32 handle) const; | 23 | VAddr GetObjectAddress(u32 handle) const; |
| 24 | 24 | ||
| 25 | u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 25 | u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 26 | IoctlCtrl& ctrl) override; | 26 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 27 | IoctlVersion version) override; | ||
| 27 | 28 | ||
| 28 | /// Represents an nvmap object. | 29 | /// Represents an nvmap object. |
| 29 | struct Object { | 30 | struct Object { |
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index d5be64ed2..5e0c23602 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp | |||
| @@ -33,42 +33,77 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { | |||
| 33 | rb.Push<u32>(0); | 33 | rb.Push<u32>(0); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { | 36 | void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { |
| 37 | LOG_DEBUG(Service_NVDRV, "called"); | ||
| 38 | |||
| 39 | IPC::RequestParser rp{ctx}; | 37 | IPC::RequestParser rp{ctx}; |
| 40 | u32 fd = rp.Pop<u32>(); | 38 | u32 fd = rp.Pop<u32>(); |
| 41 | u32 command = rp.Pop<u32>(); | 39 | u32 command = rp.Pop<u32>(); |
| 42 | 40 | ||
| 43 | std::vector<u8> output(ctx.GetWriteBufferSize()); | 41 | /// Ioctl 3 has 2 outputs, first in the input params, second is the result |
| 42 | std::vector<u8> output(ctx.GetWriteBufferSize(0)); | ||
| 43 | std::vector<u8> output2; | ||
| 44 | if (version == IoctlVersion::Version3) { | ||
| 45 | output2.resize((ctx.GetWriteBufferSize(1))); | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer. | ||
| 49 | /// KickOfPB uses this | ||
| 50 | auto input = ctx.ReadBuffer(0); | ||
| 51 | |||
| 52 | std::vector<u8> input2; | ||
| 53 | if (version == IoctlVersion::Version2) { | ||
| 54 | input2 = ctx.ReadBuffer(1); | ||
| 55 | } | ||
| 44 | 56 | ||
| 45 | IoctlCtrl ctrl{}; | 57 | IoctlCtrl ctrl{}; |
| 46 | 58 | ||
| 47 | u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl); | 59 | u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version); |
| 48 | 60 | ||
| 49 | if (ctrl.must_delay) { | 61 | if (ctrl.must_delay) { |
| 50 | ctrl.fresh_call = false; | 62 | ctrl.fresh_call = false; |
| 51 | ctx.SleepClientThread( | 63 | ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, |
| 52 | "NVServices::DelayedResponse", ctrl.timeout, | 64 | [=](Kernel::SharedPtr<Kernel::Thread> thread, |
| 53 | [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 65 | Kernel::HLERequestContext& ctx, |
| 54 | Kernel::ThreadWakeupReason reason) { | 66 | Kernel::ThreadWakeupReason reason) { |
| 55 | IoctlCtrl ctrl2{ctrl}; | 67 | IoctlCtrl ctrl2{ctrl}; |
| 56 | std::vector<u8> output2 = output; | 68 | std::vector<u8> tmp_output = output; |
| 57 | u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2); | 69 | std::vector<u8> tmp_output2 = output2; |
| 58 | ctx.WriteBuffer(output2); | 70 | u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, |
| 59 | IPC::ResponseBuilder rb{ctx, 3}; | 71 | tmp_output2, ctrl2, version); |
| 60 | rb.Push(RESULT_SUCCESS); | 72 | ctx.WriteBuffer(tmp_output, 0); |
| 61 | rb.Push(result); | 73 | if (version == IoctlVersion::Version3) { |
| 62 | }, | 74 | ctx.WriteBuffer(tmp_output2, 1); |
| 63 | nvdrv->GetEventWriteable(ctrl.event_id)); | 75 | } |
| 76 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 77 | rb.Push(RESULT_SUCCESS); | ||
| 78 | rb.Push(result); | ||
| 79 | }, | ||
| 80 | nvdrv->GetEventWriteable(ctrl.event_id)); | ||
| 64 | } else { | 81 | } else { |
| 65 | ctx.WriteBuffer(output); | 82 | ctx.WriteBuffer(output); |
| 83 | if (version == IoctlVersion::Version3) { | ||
| 84 | ctx.WriteBuffer(output2, 1); | ||
| 85 | } | ||
| 66 | } | 86 | } |
| 67 | IPC::ResponseBuilder rb{ctx, 3}; | 87 | IPC::ResponseBuilder rb{ctx, 3}; |
| 68 | rb.Push(RESULT_SUCCESS); | 88 | rb.Push(RESULT_SUCCESS); |
| 69 | rb.Push(result); | 89 | rb.Push(result); |
| 70 | } | 90 | } |
| 71 | 91 | ||
| 92 | void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { | ||
| 93 | LOG_DEBUG(Service_NVDRV, "called"); | ||
| 94 | IoctlBase(ctx, IoctlVersion::Version1); | ||
| 95 | } | ||
| 96 | |||
| 97 | void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { | ||
| 98 | LOG_DEBUG(Service_NVDRV, "called"); | ||
| 99 | IoctlBase(ctx, IoctlVersion::Version2); | ||
| 100 | } | ||
| 101 | |||
| 102 | void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { | ||
| 103 | LOG_DEBUG(Service_NVDRV, "called"); | ||
| 104 | IoctlBase(ctx, IoctlVersion::Version3); | ||
| 105 | } | ||
| 106 | |||
| 72 | void NVDRV::Close(Kernel::HLERequestContext& ctx) { | 107 | void NVDRV::Close(Kernel::HLERequestContext& ctx) { |
| 73 | LOG_DEBUG(Service_NVDRV, "called"); | 108 | LOG_DEBUG(Service_NVDRV, "called"); |
| 74 | 109 | ||
| @@ -154,8 +189,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) | |||
| 154 | {8, &NVDRV::SetClientPID, "SetClientPID"}, | 189 | {8, &NVDRV::SetClientPID, "SetClientPID"}, |
| 155 | {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, | 190 | {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, |
| 156 | {10, nullptr, "InitializeDevtools"}, | 191 | {10, nullptr, "InitializeDevtools"}, |
| 157 | {11, &NVDRV::Ioctl, "Ioctl2"}, | 192 | {11, &NVDRV::Ioctl2, "Ioctl2"}, |
| 158 | {12, nullptr, "Ioctl3"}, | 193 | {12, &NVDRV::Ioctl3, "Ioctl3"}, |
| 159 | {13, &NVDRV::FinishInitialize, "FinishInitialize"}, | 194 | {13, &NVDRV::FinishInitialize, "FinishInitialize"}, |
| 160 | }; | 195 | }; |
| 161 | RegisterHandlers(functions); | 196 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 10a0ecd52..9269ce00c 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h | |||
| @@ -24,6 +24,8 @@ public: | |||
| 24 | private: | 24 | private: |
| 25 | void Open(Kernel::HLERequestContext& ctx); | 25 | void Open(Kernel::HLERequestContext& ctx); |
| 26 | void Ioctl(Kernel::HLERequestContext& ctx); | 26 | void Ioctl(Kernel::HLERequestContext& ctx); |
| 27 | void Ioctl2(Kernel::HLERequestContext& ctx); | ||
| 28 | void Ioctl3(Kernel::HLERequestContext& ctx); | ||
| 27 | void Close(Kernel::HLERequestContext& ctx); | 29 | void Close(Kernel::HLERequestContext& ctx); |
| 28 | void Initialize(Kernel::HLERequestContext& ctx); | 30 | void Initialize(Kernel::HLERequestContext& ctx); |
| 29 | void QueryEvent(Kernel::HLERequestContext& ctx); | 31 | void QueryEvent(Kernel::HLERequestContext& ctx); |
| @@ -31,6 +33,7 @@ private: | |||
| 31 | void FinishInitialize(Kernel::HLERequestContext& ctx); | 33 | void FinishInitialize(Kernel::HLERequestContext& ctx); |
| 32 | void GetStatus(Kernel::HLERequestContext& ctx); | 34 | void GetStatus(Kernel::HLERequestContext& ctx); |
| 33 | void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); | 35 | void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); |
| 36 | void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version); | ||
| 34 | 37 | ||
| 35 | std::shared_ptr<Module> nvdrv; | 38 | std::shared_ptr<Module> nvdrv; |
| 36 | 39 | ||
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index ac03cbc23..529b03471 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h | |||
| @@ -34,6 +34,12 @@ enum class EventState { | |||
| 34 | Busy = 3, | 34 | Busy = 3, |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | enum class IoctlVersion : u32 { | ||
| 38 | Version1, | ||
| 39 | Version2, | ||
| 40 | Version3, | ||
| 41 | }; | ||
| 42 | |||
| 37 | struct IoctlCtrl { | 43 | struct IoctlCtrl { |
| 38 | // First call done to the servioce for services that call itself again after a call. | 44 | // First call done to the servioce for services that call itself again after a call. |
| 39 | bool fresh_call{true}; | 45 | bool fresh_call{true}; |
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 2011a226a..307a7e928 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp | |||
| @@ -71,13 +71,14 @@ u32 Module::Open(const std::string& device_name) { | |||
| 71 | return fd; | 71 | return fd; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, | 74 | u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 75 | IoctlCtrl& ctrl) { | 75 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 76 | IoctlVersion version) { | ||
| 76 | auto itr = open_files.find(fd); | 77 | auto itr = open_files.find(fd); |
| 77 | ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); | 78 | ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); |
| 78 | 79 | ||
| 79 | auto& device = itr->second; | 80 | auto& device = itr->second; |
| 80 | return device->ioctl({command}, input, output, ctrl); | 81 | return device->ioctl({command}, input, input2, output, output2, ctrl, version); |
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | ResultCode Module::Close(u32 fd) { | 84 | 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: | |||
| 106 | /// Opens a device node and returns a file descriptor to it. | 106 | /// Opens a device node and returns a file descriptor to it. |
| 107 | u32 Open(const std::string& device_name); | 107 | u32 Open(const std::string& device_name); |
| 108 | /// Sends an ioctl command to the specified file descriptor. | 108 | /// Sends an ioctl command to the specified file descriptor. |
| 109 | u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, | 109 | u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, |
| 110 | IoctlCtrl& ctrl); | 110 | std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, |
| 111 | IoctlVersion version); | ||
| 111 | /// Closes a device file descriptor and returns operation success. | 112 | /// Closes a device file descriptor and returns operation success. |
| 112 | ResultCode Close(u32 fd); | 113 | ResultCode Close(u32 fd); |
| 113 | 114 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index f9db79370..2e4d707b9 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -29,26 +29,28 @@ namespace Service::NVFlinger { | |||
| 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); | 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); |
| 30 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); | 30 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); |
| 31 | 31 | ||
| 32 | NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { | 32 | NVFlinger::NVFlinger(Core::System& system) : system(system) { |
| 33 | displays.emplace_back(0, "Default"); | 33 | displays.emplace_back(0, "Default", system); |
| 34 | displays.emplace_back(1, "External"); | 34 | displays.emplace_back(1, "External", system); |
| 35 | displays.emplace_back(2, "Edid"); | 35 | displays.emplace_back(2, "Edid", system); |
| 36 | displays.emplace_back(3, "Internal"); | 36 | displays.emplace_back(3, "Internal", system); |
| 37 | displays.emplace_back(4, "Null"); | 37 | displays.emplace_back(4, "Null", system); |
| 38 | 38 | ||
| 39 | // Schedule the screen composition events | 39 | // Schedule the screen composition events |
| 40 | composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, | 40 | composition_event = system.CoreTiming().RegisterEvent( |
| 41 | s64 cycles_late) { | 41 | "ScreenComposition", [this](u64 userdata, s64 cycles_late) { |
| 42 | Compose(); | 42 | Compose(); |
| 43 | const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); | 43 | const auto ticks = |
| 44 | this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event); | 44 | Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); |
| 45 | }); | 45 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), |
| 46 | 46 | composition_event); | |
| 47 | core_timing.ScheduleEvent(frame_ticks, composition_event); | 47 | }); |
| 48 | |||
| 49 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | ||
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | NVFlinger::~NVFlinger() { | 52 | NVFlinger::~NVFlinger() { |
| 51 | core_timing.UnscheduleEvent(composition_event, 0); | 53 | system.CoreTiming().UnscheduleEvent(composition_event, 0); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 56 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { |
| @@ -185,11 +187,9 @@ void NVFlinger::Compose() { | |||
| 185 | MicroProfileFlip(); | 187 | MicroProfileFlip(); |
| 186 | 188 | ||
| 187 | if (!buffer) { | 189 | if (!buffer) { |
| 188 | auto& system_instance = Core::System::GetInstance(); | ||
| 189 | |||
| 190 | // There was no queued buffer to draw, render previous frame | 190 | // There was no queued buffer to draw, render previous frame |
| 191 | system_instance.GetPerfStats().EndGameFrame(); | 191 | system.GetPerfStats().EndGameFrame(); |
| 192 | system_instance.GPU().SwapBuffers({}); | 192 | system.GPU().SwapBuffers({}); |
| 193 | continue; | 193 | continue; |
| 194 | } | 194 | } |
| 195 | 195 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 988be8726..5d7e3bfb8 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -38,7 +38,7 @@ class BufferQueue; | |||
| 38 | 38 | ||
| 39 | class NVFlinger final { | 39 | class NVFlinger final { |
| 40 | public: | 40 | public: |
| 41 | explicit NVFlinger(Core::Timing::CoreTiming& core_timing); | 41 | explicit NVFlinger(Core::System& system); |
| 42 | ~NVFlinger(); | 42 | ~NVFlinger(); |
| 43 | 43 | ||
| 44 | /// Sets the NVDrv module instance to use to send buffers to the GPU. | 44 | /// Sets the NVDrv module instance to use to send buffers to the GPU. |
| @@ -105,8 +105,7 @@ private: | |||
| 105 | /// Event that handles screen composition. | 105 | /// Event that handles screen composition. |
| 106 | Core::Timing::EventType* composition_event; | 106 | Core::Timing::EventType* composition_event; |
| 107 | 107 | ||
| 108 | /// Core timing instance for registering/unregistering the composition event. | 108 | Core::System& system; |
| 109 | Core::Timing::CoreTiming& core_timing; | ||
| 110 | }; | 109 | }; |
| 111 | 110 | ||
| 112 | } // namespace Service::NVFlinger | 111 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 7e134f5c1..18d895263 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -2,34 +2,31 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <json.hpp> | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "common/hex_util.h" | 5 | #include "common/hex_util.h" |
| 8 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 9 | #include "common/scm_rev.h" | ||
| 10 | #include "core/hle/ipc_helpers.h" | 7 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/process.h" | 8 | #include "core/hle/kernel/process.h" |
| 12 | #include "core/hle/service/acc/profile_manager.h" | 9 | #include "core/hle/service/acc/profile_manager.h" |
| 13 | #include "core/hle/service/prepo/prepo.h" | 10 | #include "core/hle/service/prepo/prepo.h" |
| 14 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 15 | #include "core/reporter.h" | 12 | #include "core/reporter.h" |
| 16 | #include "core/settings.h" | ||
| 17 | 13 | ||
| 18 | namespace Service::PlayReport { | 14 | namespace Service::PlayReport { |
| 19 | 15 | ||
| 20 | class PlayReport final : public ServiceFramework<PlayReport> { | 16 | class PlayReport final : public ServiceFramework<PlayReport> { |
| 21 | public: | 17 | public: |
| 22 | explicit PlayReport(const char* name) : ServiceFramework{name} { | 18 | explicit PlayReport(const char* name, Core::System& system) |
| 19 | : ServiceFramework{name}, system(system) { | ||
| 23 | // clang-format off | 20 | // clang-format off |
| 24 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 25 | {10100, nullptr, "SaveReportOld"}, | 22 | {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"}, |
| 26 | {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, | 23 | {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, |
| 27 | {10102, nullptr, "SaveReport"}, | 24 | {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"}, |
| 28 | {10103, nullptr, "SaveReportWithUser"}, | 25 | {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"}, |
| 29 | {10200, nullptr, "RequestImmediateTransmission"}, | 26 | {10200, nullptr, "RequestImmediateTransmission"}, |
| 30 | {10300, nullptr, "GetTransmissionStatus"}, | 27 | {10300, nullptr, "GetTransmissionStatus"}, |
| 31 | {20100, nullptr, "SaveSystemReport"}, | 28 | {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, |
| 32 | {20101, nullptr, "SaveSystemReportWithUser"}, | 29 | {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, |
| 33 | {20200, nullptr, "SetOperationMode"}, | 30 | {20200, nullptr, "SetOperationMode"}, |
| 34 | {30100, nullptr, "ClearStorage"}, | 31 | {30100, nullptr, "ClearStorage"}, |
| 35 | {30200, nullptr, "ClearStatistics"}, | 32 | {30200, nullptr, "ClearStatistics"}, |
| @@ -47,7 +44,28 @@ public: | |||
| 47 | } | 44 | } |
| 48 | 45 | ||
| 49 | private: | 46 | private: |
| 50 | void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { | 47 | template <Core::Reporter::PlayReportType Type> |
| 48 | void SaveReport(Kernel::HLERequestContext& ctx) { | ||
| 49 | IPC::RequestParser rp{ctx}; | ||
| 50 | const auto process_id = rp.PopRaw<u64>(); | ||
| 51 | |||
| 52 | const auto data1 = ctx.ReadBuffer(0); | ||
| 53 | const auto data2 = ctx.ReadBuffer(1); | ||
| 54 | |||
| 55 | LOG_DEBUG(Service_PREPO, | ||
| 56 | "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", | ||
| 57 | static_cast<u8>(Type), process_id, data1.size(), data2.size()); | ||
| 58 | |||
| 59 | const auto& reporter{system.GetReporter()}; | ||
| 60 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, | ||
| 61 | process_id); | ||
| 62 | |||
| 63 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 64 | rb.Push(RESULT_SUCCESS); | ||
| 65 | } | ||
| 66 | |||
| 67 | template <Core::Reporter::PlayReportType Type> | ||
| 68 | void SaveReportWithUser(Kernel::HLERequestContext& ctx) { | ||
| 51 | IPC::RequestParser rp{ctx}; | 69 | IPC::RequestParser rp{ctx}; |
| 52 | const auto user_id = rp.PopRaw<u128>(); | 70 | const auto user_id = rp.PopRaw<u128>(); |
| 53 | const auto process_id = rp.PopRaw<u64>(); | 71 | const auto process_id = rp.PopRaw<u64>(); |
| @@ -57,24 +75,65 @@ private: | |||
| 57 | 75 | ||
| 58 | LOG_DEBUG( | 76 | LOG_DEBUG( |
| 59 | Service_PREPO, | 77 | Service_PREPO, |
| 60 | "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}", | 78 | "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " |
| 61 | user_id[1], user_id[0], process_id, data1.size(), data2.size()); | 79 | "data2_size={:016X}", |
| 80 | static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size()); | ||
| 81 | |||
| 82 | const auto& reporter{system.GetReporter()}; | ||
| 83 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, | ||
| 84 | process_id, user_id); | ||
| 85 | |||
| 86 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 87 | rb.Push(RESULT_SUCCESS); | ||
| 88 | } | ||
| 89 | |||
| 90 | void SaveSystemReport(Kernel::HLERequestContext& ctx) { | ||
| 91 | IPC::RequestParser rp{ctx}; | ||
| 92 | const auto title_id = rp.PopRaw<u64>(); | ||
| 93 | |||
| 94 | const auto data1 = ctx.ReadBuffer(0); | ||
| 95 | const auto data2 = ctx.ReadBuffer(1); | ||
| 96 | |||
| 97 | LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", | ||
| 98 | title_id, data1.size(), data2.size()); | ||
| 99 | |||
| 100 | const auto& reporter{system.GetReporter()}; | ||
| 101 | reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}); | ||
| 102 | |||
| 103 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 104 | rb.Push(RESULT_SUCCESS); | ||
| 105 | } | ||
| 62 | 106 | ||
| 63 | const auto& reporter{Core::System::GetInstance().GetReporter()}; | 107 | void SaveSystemReportWithUser(Kernel::HLERequestContext& ctx) { |
| 64 | reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2}, | 108 | IPC::RequestParser rp{ctx}; |
| 65 | user_id); | 109 | const auto user_id = rp.PopRaw<u128>(); |
| 110 | const auto title_id = rp.PopRaw<u64>(); | ||
| 111 | |||
| 112 | const auto data1 = ctx.ReadBuffer(0); | ||
| 113 | const auto data2 = ctx.ReadBuffer(1); | ||
| 114 | |||
| 115 | LOG_DEBUG(Service_PREPO, | ||
| 116 | "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " | ||
| 117 | "data2_size={:016X}", | ||
| 118 | user_id[1], user_id[0], title_id, data1.size(), data2.size()); | ||
| 119 | |||
| 120 | const auto& reporter{system.GetReporter()}; | ||
| 121 | reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}, | ||
| 122 | std::nullopt, user_id); | ||
| 66 | 123 | ||
| 67 | IPC::ResponseBuilder rb{ctx, 2}; | 124 | IPC::ResponseBuilder rb{ctx, 2}; |
| 68 | rb.Push(RESULT_SUCCESS); | 125 | rb.Push(RESULT_SUCCESS); |
| 69 | } | 126 | } |
| 127 | |||
| 128 | Core::System& system; | ||
| 70 | }; | 129 | }; |
| 71 | 130 | ||
| 72 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 131 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 73 | std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); | 132 | std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager); |
| 74 | std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); | 133 | std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager); |
| 75 | std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); | 134 | std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager); |
| 76 | std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); | 135 | std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager); |
| 77 | std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); | 136 | std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager); |
| 78 | } | 137 | } |
| 79 | 138 | ||
| 80 | } // namespace Service::PlayReport | 139 | } // namespace Service::PlayReport |
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index 0e7b01331..a5682ee26 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h | |||
| @@ -8,8 +8,12 @@ namespace Service::SM { | |||
| 8 | class ServiceManager; | 8 | class ServiceManager; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Service::PlayReport { | 15 | namespace Service::PlayReport { |
| 12 | 16 | ||
| 13 | void InstallInterfaces(SM::ServiceManager& service_manager); | 17 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 14 | 18 | ||
| 15 | } // namespace Service::PlayReport | 19 | } // namespace Service::PlayReport |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 3a0f8c3f6..831a427de 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -198,49 +198,50 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co | |||
| 198 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | 198 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { |
| 199 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it | 199 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it |
| 200 | // here and pass it into the respective InstallInterfaces functions. | 200 | // here and pass it into the respective InstallInterfaces functions. |
| 201 | auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); | 201 | auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); |
| 202 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); | ||
| 202 | 203 | ||
| 203 | SM::ServiceManager::InstallInterfaces(sm); | 204 | SM::ServiceManager::InstallInterfaces(sm); |
| 204 | 205 | ||
| 205 | Account::InstallInterfaces(system); | 206 | Account::InstallInterfaces(system); |
| 206 | AM::InstallInterfaces(*sm, nv_flinger, system); | 207 | AM::InstallInterfaces(*sm, nv_flinger, system); |
| 207 | AOC::InstallInterfaces(*sm); | 208 | AOC::InstallInterfaces(*sm, system); |
| 208 | APM::InstallInterfaces(system); | 209 | APM::InstallInterfaces(system); |
| 209 | Audio::InstallInterfaces(*sm, system); | 210 | Audio::InstallInterfaces(*sm, system); |
| 210 | BCAT::InstallInterfaces(*sm); | 211 | BCAT::InstallInterfaces(*sm); |
| 211 | BPC::InstallInterfaces(*sm); | 212 | BPC::InstallInterfaces(*sm); |
| 212 | BtDrv::InstallInterfaces(*sm); | 213 | BtDrv::InstallInterfaces(*sm, system); |
| 213 | BTM::InstallInterfaces(*sm); | 214 | BTM::InstallInterfaces(*sm, system); |
| 214 | Capture::InstallInterfaces(*sm); | 215 | Capture::InstallInterfaces(*sm); |
| 215 | ERPT::InstallInterfaces(*sm); | 216 | ERPT::InstallInterfaces(*sm); |
| 216 | ES::InstallInterfaces(*sm); | 217 | ES::InstallInterfaces(*sm); |
| 217 | EUPLD::InstallInterfaces(*sm); | 218 | EUPLD::InstallInterfaces(*sm); |
| 218 | Fatal::InstallInterfaces(*sm); | 219 | Fatal::InstallInterfaces(*sm, system); |
| 219 | FGM::InstallInterfaces(*sm); | 220 | FGM::InstallInterfaces(*sm); |
| 220 | FileSystem::InstallInterfaces(system); | 221 | FileSystem::InstallInterfaces(system); |
| 221 | Friend::InstallInterfaces(*sm); | 222 | Friend::InstallInterfaces(*sm, system); |
| 222 | Glue::InstallInterfaces(system); | 223 | Glue::InstallInterfaces(system); |
| 223 | GRC::InstallInterfaces(*sm); | 224 | GRC::InstallInterfaces(*sm); |
| 224 | HID::InstallInterfaces(*sm); | 225 | HID::InstallInterfaces(*sm, system); |
| 225 | LBL::InstallInterfaces(*sm); | 226 | LBL::InstallInterfaces(*sm); |
| 226 | LDN::InstallInterfaces(*sm); | 227 | LDN::InstallInterfaces(*sm); |
| 227 | LDR::InstallInterfaces(*sm); | 228 | LDR::InstallInterfaces(*sm, system); |
| 228 | LM::InstallInterfaces(*sm); | 229 | LM::InstallInterfaces(*sm); |
| 229 | Migration::InstallInterfaces(*sm); | 230 | Migration::InstallInterfaces(*sm); |
| 230 | Mii::InstallInterfaces(*sm); | 231 | Mii::InstallInterfaces(*sm); |
| 231 | MM::InstallInterfaces(*sm); | 232 | MM::InstallInterfaces(*sm); |
| 232 | NCM::InstallInterfaces(*sm); | 233 | NCM::InstallInterfaces(*sm); |
| 233 | NFC::InstallInterfaces(*sm); | 234 | NFC::InstallInterfaces(*sm); |
| 234 | NFP::InstallInterfaces(*sm); | 235 | NFP::InstallInterfaces(*sm, system); |
| 235 | NIFM::InstallInterfaces(*sm); | 236 | NIFM::InstallInterfaces(*sm, system); |
| 236 | NIM::InstallInterfaces(*sm); | 237 | NIM::InstallInterfaces(*sm, system); |
| 237 | NPNS::InstallInterfaces(*sm); | 238 | NPNS::InstallInterfaces(*sm); |
| 238 | NS::InstallInterfaces(*sm); | 239 | NS::InstallInterfaces(*sm, system); |
| 239 | Nvidia::InstallInterfaces(*sm, *nv_flinger, system); | 240 | Nvidia::InstallInterfaces(*sm, *nv_flinger, system); |
| 240 | PCIe::InstallInterfaces(*sm); | 241 | PCIe::InstallInterfaces(*sm); |
| 241 | PCTL::InstallInterfaces(*sm); | 242 | PCTL::InstallInterfaces(*sm); |
| 242 | PCV::InstallInterfaces(*sm); | 243 | PCV::InstallInterfaces(*sm); |
| 243 | PlayReport::InstallInterfaces(*sm); | 244 | PlayReport::InstallInterfaces(*sm, system); |
| 244 | PM::InstallInterfaces(system); | 245 | PM::InstallInterfaces(system); |
| 245 | PSC::InstallInterfaces(*sm); | 246 | PSC::InstallInterfaces(*sm); |
| 246 | PSM::InstallInterfaces(*sm); | 247 | PSM::InstallInterfaces(*sm); |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index c6c4bdae5..aef964861 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -18,10 +18,6 @@ namespace Core { | |||
| 18 | class System; | 18 | class System; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | namespace FileSys { | ||
| 22 | class VfsFilesystem; | ||
| 23 | } | ||
| 24 | |||
| 25 | namespace Kernel { | 21 | namespace Kernel { |
| 26 | class ClientPort; | 22 | class ClientPort; |
| 27 | class ServerPort; | 23 | class ServerPort; |
| @@ -31,6 +27,10 @@ class HLERequestContext; | |||
| 31 | 27 | ||
| 32 | namespace Service { | 28 | namespace Service { |
| 33 | 29 | ||
| 30 | namespace FileSystem { | ||
| 31 | class FileSystemController; | ||
| 32 | } // namespace FileSystem | ||
| 33 | |||
| 34 | namespace SM { | 34 | namespace SM { |
| 35 | class ServiceManager; | 35 | class ServiceManager; |
| 36 | } | 36 | } |
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 1030185e0..9565e7de5 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp | |||
| @@ -7,8 +7,8 @@ | |||
| 7 | namespace Service::Time { | 7 | namespace Service::Time { |
| 8 | 8 | ||
| 9 | Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, | 9 | Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, |
| 10 | const char* name) | 10 | Core::System& system, const char* name) |
| 11 | : Module::Interface(std::move(time), std::move(shared_memory), name) { | 11 | : Module::Interface(std::move(time), std::move(shared_memory), system, name) { |
| 12 | // clang-format off | 12 | // clang-format off |
| 13 | static const FunctionInfo functions[] = { | 13 | static const FunctionInfo functions[] = { |
| 14 | {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, | 14 | {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, |
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h index bdf0883e2..5c63a07f4 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/interface.h | |||
| @@ -13,7 +13,7 @@ class SharedMemory; | |||
| 13 | class Time final : public Module::Interface { | 13 | class Time final : public Module::Interface { |
| 14 | public: | 14 | public: |
| 15 | explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, | 15 | explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, |
| 16 | const char* name); | 16 | Core::System& system, const char* name); |
| 17 | ~Time() override; | 17 | ~Time() override; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index ae6446204..1b9ab8401 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -126,8 +126,8 @@ private: | |||
| 126 | 126 | ||
| 127 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { | 127 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { |
| 128 | public: | 128 | public: |
| 129 | ISteadyClock(std::shared_ptr<SharedMemory> shared_memory) | 129 | ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system) |
| 130 | : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) { | 130 | : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) { |
| 131 | static const FunctionInfo functions[] = { | 131 | static const FunctionInfo functions[] = { |
| 132 | {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, | 132 | {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, |
| 133 | }; | 133 | }; |
| @@ -150,12 +150,13 @@ private: | |||
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | SteadyClockTimePoint GetCurrentTimePoint() const { | 152 | SteadyClockTimePoint GetCurrentTimePoint() const { |
| 153 | const auto& core_timing = Core::System::GetInstance().CoreTiming(); | 153 | const auto& core_timing = system.CoreTiming(); |
| 154 | const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); | 154 | const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); |
| 155 | return {static_cast<u64_le>(ms.count() / 1000), {}}; | 155 | return {static_cast<u64_le>(ms.count() / 1000), {}}; |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | std::shared_ptr<SharedMemory> shared_memory; | 158 | std::shared_ptr<SharedMemory> shared_memory; |
| 159 | Core::System& system; | ||
| 159 | }; | 160 | }; |
| 160 | 161 | ||
| 161 | class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { | 162 | class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { |
| @@ -290,7 +291,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { | |||
| 290 | 291 | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 292 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 292 | rb.Push(RESULT_SUCCESS); | 293 | rb.Push(RESULT_SUCCESS); |
| 293 | rb.PushIpcInterface<ISteadyClock>(shared_memory); | 294 | rb.PushIpcInterface<ISteadyClock>(shared_memory, system); |
| 294 | } | 295 | } |
| 295 | 296 | ||
| 296 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { | 297 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { |
| @@ -325,7 +326,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 325 | return; | 326 | return; |
| 326 | } | 327 | } |
| 327 | 328 | ||
| 328 | const auto& core_timing = Core::System::GetInstance().CoreTiming(); | 329 | const auto& core_timing = system.CoreTiming(); |
| 329 | const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); | 330 | const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); |
| 330 | const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; | 331 | const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; |
| 331 | 332 | ||
| @@ -407,8 +408,10 @@ void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled( | |||
| 407 | } | 408 | } |
| 408 | 409 | ||
| 409 | Module::Interface::Interface(std::shared_ptr<Module> time, | 410 | Module::Interface::Interface(std::shared_ptr<Module> time, |
| 410 | std::shared_ptr<SharedMemory> shared_memory, const char* name) | 411 | std::shared_ptr<SharedMemory> shared_memory, Core::System& system, |
| 411 | : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {} | 412 | const char* name) |
| 413 | : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)), | ||
| 414 | system(system) {} | ||
| 412 | 415 | ||
| 413 | Module::Interface::~Interface() = default; | 416 | Module::Interface::~Interface() = default; |
| 414 | 417 | ||
| @@ -416,9 +419,11 @@ void InstallInterfaces(Core::System& system) { | |||
| 416 | auto time = std::make_shared<Module>(); | 419 | auto time = std::make_shared<Module>(); |
| 417 | auto shared_mem = std::make_shared<SharedMemory>(system); | 420 | auto shared_mem = std::make_shared<SharedMemory>(system); |
| 418 | 421 | ||
| 419 | std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager()); | 422 | std::make_shared<Time>(time, shared_mem, system, "time:a") |
| 420 | std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager()); | 423 | ->InstallAsService(system.ServiceManager()); |
| 421 | std::make_shared<Time>(std::move(time), shared_mem, "time:u") | 424 | std::make_shared<Time>(time, shared_mem, system, "time:s") |
| 425 | ->InstallAsService(system.ServiceManager()); | ||
| 426 | std::make_shared<Time>(std::move(time), shared_mem, system, "time:u") | ||
| 422 | ->InstallAsService(system.ServiceManager()); | 427 | ->InstallAsService(system.ServiceManager()); |
| 423 | } | 428 | } |
| 424 | 429 | ||
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index e0708f856..c32d32860 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h | |||
| @@ -80,7 +80,8 @@ public: | |||
| 80 | class Interface : public ServiceFramework<Interface> { | 80 | class Interface : public ServiceFramework<Interface> { |
| 81 | public: | 81 | public: |
| 82 | explicit Interface(std::shared_ptr<Module> time, | 82 | explicit Interface(std::shared_ptr<Module> time, |
| 83 | std::shared_ptr<SharedMemory> shared_memory, const char* name); | 83 | std::shared_ptr<SharedMemory> shared_memory, Core::System& system, |
| 84 | const char* name); | ||
| 84 | ~Interface() override; | 85 | ~Interface() override; |
| 85 | 86 | ||
| 86 | void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); | 87 | void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); |
| @@ -97,6 +98,7 @@ public: | |||
| 97 | protected: | 98 | protected: |
| 98 | std::shared_ptr<Module> time; | 99 | std::shared_ptr<Module> time; |
| 99 | std::shared_ptr<SharedMemory> shared_memory; | 100 | std::shared_ptr<SharedMemory> shared_memory; |
| 101 | Core::System& system; | ||
| 100 | }; | 102 | }; |
| 101 | }; | 103 | }; |
| 102 | 104 | ||
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index a8d088305..006a6d9ff 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -15,8 +15,8 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::VI { | 16 | namespace Service::VI { |
| 17 | 17 | ||
| 18 | Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { | 18 | Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} { |
| 19 | auto& kernel = Core::System::GetInstance().Kernel(); | 19 | auto& kernel = system.Kernel(); |
| 20 | vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, | 20 | vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, |
| 21 | fmt::format("Display VSync Event {}", id)); | 21 | fmt::format("Display VSync Event {}", id)); |
| 22 | } | 22 | } |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 2acd46ff8..f56b5badc 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | /// @param id The unique ID for this display. | 26 | /// @param id The unique ID for this display. |
| 27 | /// @param name The name for this display. | 27 | /// @param name The name for this display. |
| 28 | /// | 28 | /// |
| 29 | Display(u64 id, std::string name); | 29 | Display(u64 id, std::string name, Core::System& system); |
| 30 | ~Display(); | 30 | ~Display(); |
| 31 | 31 | ||
| 32 | Display(const Display&) = delete; | 32 | Display(const Display&) = delete; |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index f9e88be2b..d19c3623c 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/content_archive.h" | 11 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/control_metadata.h" | 12 | #include "core/file_sys/control_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| @@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 176 | // Register the RomFS if a ".romfs" file was found | 177 | // Register the RomFS if a ".romfs" file was found |
| 177 | if (romfs_iter != files.end() && *romfs_iter != nullptr) { | 178 | if (romfs_iter != files.end() && *romfs_iter != nullptr) { |
| 178 | romfs = *romfs_iter; | 179 | romfs = *romfs_iter; |
| 179 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | 180 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( |
| 181 | std::make_unique<FileSys::RomFSFactory>(*this)); | ||
| 180 | } | 182 | } |
| 181 | 183 | ||
| 182 | is_loaded = true; | 184 | is_loaded = true; |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 0f65fb637..5a0469978 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | ||
| 9 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/romfs_factory.h" | 11 | #include "core/file_sys/romfs_factory.h" |
| 11 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| @@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { | |||
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { | 60 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { |
| 60 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | 61 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( |
| 62 | std::make_unique<FileSys::RomFSFactory>(*this)); | ||
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | is_loaded = true; | 65 | is_loaded = true; |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 3a5361fdd..175898b91 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/core.h" | ||
| 13 | #include "core/file_sys/control_metadata.h" | 14 | #include "core/file_sys/control_metadata.h" |
| 14 | #include "core/file_sys/romfs_factory.h" | 15 | #include "core/file_sys/romfs_factory.h" |
| 15 | #include "core/file_sys/vfs_offset.h" | 16 | #include "core/file_sys/vfs_offset.h" |
| @@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | |||
| 214 | } | 215 | } |
| 215 | 216 | ||
| 216 | if (romfs != nullptr) { | 217 | if (romfs != nullptr) { |
| 217 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | 218 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( |
| 219 | std::make_unique<FileSys::RomFSFactory>(*this)); | ||
| 218 | } | 220 | } |
| 219 | 221 | ||
| 220 | is_loaded = true; | 222 | is_loaded = true; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 70c90109f..e75c700ad 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -152,8 +152,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 152 | auto& system = Core::System::GetInstance(); | 152 | auto& system = Core::System::GetInstance(); |
| 153 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); | 153 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); |
| 154 | if (!cheats.empty()) { | 154 | if (!cheats.empty()) { |
| 155 | system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base, | 155 | system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); |
| 156 | load_base + program_image.size()); | ||
| 157 | } | 156 | } |
| 158 | } | 157 | } |
| 159 | 158 | ||
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index b1171ce65..13950fc08 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/core.h" | ||
| 8 | #include "core/file_sys/card_image.h" | 9 | #include "core/file_sys/card_image.h" |
| 9 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 11 | #include "core/file_sys/control_metadata.h" |
| @@ -26,20 +27,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) | |||
| 26 | 27 | ||
| 27 | if (nsp->GetStatus() != ResultStatus::Success) | 28 | if (nsp->GetStatus() != ResultStatus::Success) |
| 28 | return; | 29 | return; |
| 29 | if (nsp->IsExtractedType()) | ||
| 30 | return; | ||
| 31 | |||
| 32 | const auto control_nca = | ||
| 33 | nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); | ||
| 34 | if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | ||
| 35 | return; | ||
| 36 | |||
| 37 | std::tie(nacp_file, icon_file) = | ||
| 38 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); | ||
| 39 | 30 | ||
| 40 | if (nsp->IsExtractedType()) { | 31 | if (nsp->IsExtractedType()) { |
| 41 | secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); | 32 | secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); |
| 42 | } else { | 33 | } else { |
| 34 | const auto control_nca = | ||
| 35 | nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); | ||
| 36 | if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | ||
| 37 | return; | ||
| 38 | |||
| 39 | std::tie(nacp_file, icon_file) = | ||
| 40 | FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); | ||
| 41 | |||
| 43 | if (title_id == 0) | 42 | if (title_id == 0) |
| 44 | return; | 43 | return; |
| 45 | 44 | ||
| @@ -56,11 +55,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 56 | if (nsp.GetStatus() == ResultStatus::Success) { | 55 | if (nsp.GetStatus() == ResultStatus::Success) { |
| 57 | // Extracted Type case | 56 | // Extracted Type case |
| 58 | if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && | 57 | if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && |
| 59 | FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) { | 58 | FileSys::IsDirectoryExeFS(nsp.GetExeFS())) { |
| 60 | return FileType::NSP; | 59 | return FileType::NSP; |
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | // Non-Ectracted Type case | 62 | // Non-Extracted Type case |
| 64 | if (!nsp.IsExtractedType() && | 63 | if (!nsp.IsExtractedType() && |
| 65 | nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && | 64 | nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && |
| 66 | AppLoader_NCA::IdentifyType(nsp.GetNCAFile( | 65 | AppLoader_NCA::IdentifyType(nsp.GetNCAFile( |
| @@ -77,7 +76,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | |||
| 77 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 76 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 78 | } | 77 | } |
| 79 | 78 | ||
| 80 | if (title_id == 0) { | 79 | if (!nsp->IsExtractedType() && title_id == 0) { |
| 81 | return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; | 80 | return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; |
| 82 | } | 81 | } |
| 83 | 82 | ||
| @@ -91,7 +90,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | |||
| 91 | return {nsp_program_status, {}}; | 90 | return {nsp_program_status, {}}; |
| 92 | } | 91 | } |
| 93 | 92 | ||
| 94 | if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { | 93 | if (!nsp->IsExtractedType() && |
| 94 | nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { | ||
| 95 | if (!Core::Crypto::KeyManager::KeyFileExists(false)) { | 95 | if (!Core::Crypto::KeyManager::KeyFileExists(false)) { |
| 96 | return {ResultStatus::ErrorMissingProductionKeyFile, {}}; | 96 | return {ResultStatus::ErrorMissingProductionKeyFile, {}}; |
| 97 | } | 97 | } |
| @@ -106,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | |||
| 106 | 106 | ||
| 107 | FileSys::VirtualFile update_raw; | 107 | FileSys::VirtualFile update_raw; |
| 108 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | 108 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { |
| 109 | Service::FileSystem::SetPackedUpdate(std::move(update_raw)); | 109 | Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( |
| 110 | std::move(update_raw)); | ||
| 110 | } | 111 | } |
| 111 | 112 | ||
| 112 | is_loaded = true; | 113 | is_loaded = true; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 5e8553db9..7186ad1ff 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/core.h" | ||
| 8 | #include "core/file_sys/card_image.h" | 9 | #include "core/file_sys/card_image.h" |
| 9 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 11 | #include "core/file_sys/control_metadata.h" |
| @@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { | |||
| 72 | 73 | ||
| 73 | FileSys::VirtualFile update_raw; | 74 | FileSys::VirtualFile update_raw; |
| 74 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | 75 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { |
| 75 | Service::FileSystem::SetPackedUpdate(std::move(update_raw)); | 76 | Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( |
| 77 | std::move(update_raw)); | ||
| 76 | } | 78 | } |
| 77 | 79 | ||
| 78 | is_loaded = true; | 80 | is_loaded = true; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8555691c0..9e030789d 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -43,8 +43,13 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me | |||
| 43 | 43 | ||
| 44 | // During boot, current_page_table might not be set yet, in which case we need not flush | 44 | // During boot, current_page_table might not be set yet, in which case we need not flush |
| 45 | if (Core::System::GetInstance().IsPoweredOn()) { | 45 | if (Core::System::GetInstance().IsPoweredOn()) { |
| 46 | Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, | 46 | auto& gpu = Core::System::GetInstance().GPU(); |
| 47 | size * PAGE_SIZE); | 47 | for (u64 i = 0; i < size; i++) { |
| 48 | const auto page = base + i; | ||
| 49 | if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) { | ||
| 50 | gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); | ||
| 51 | } | ||
| 52 | } | ||
| 48 | } | 53 | } |
| 49 | 54 | ||
| 50 | VAddr end = base + size; | 55 | VAddr end = base + size; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp new file mode 100644 index 000000000..b56cb0627 --- /dev/null +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <locale> | ||
| 6 | #include "common/hex_util.h" | ||
| 7 | #include "common/microprofile.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/core_timing.h" | ||
| 11 | #include "core/core_timing_util.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 14 | #include "core/hle/service/hid/hid.h" | ||
| 15 | #include "core/hle/service/sm/sm.h" | ||
| 16 | #include "core/memory/cheat_engine.h" | ||
| 17 | |||
| 18 | namespace Memory { | ||
| 19 | |||
| 20 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12); | ||
| 21 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | ||
| 22 | |||
| 23 | StandardVmCallbacks::StandardVmCallbacks(const Core::System& system, | ||
| 24 | const CheatProcessMetadata& metadata) | ||
| 25 | : system(system), metadata(metadata) {} | ||
| 26 | |||
| 27 | StandardVmCallbacks::~StandardVmCallbacks() = default; | ||
| 28 | |||
| 29 | void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { | ||
| 30 | ReadBlock(SanitizeAddress(address), data, size); | ||
| 31 | } | ||
| 32 | |||
| 33 | void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { | ||
| 34 | WriteBlock(SanitizeAddress(address), data, size); | ||
| 35 | } | ||
| 36 | |||
| 37 | u64 StandardVmCallbacks::HidKeysDown() { | ||
| 38 | const auto applet_resource = | ||
| 39 | system.ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource(); | ||
| 40 | if (applet_resource == nullptr) { | ||
| 41 | LOG_WARNING(CheatEngine, | ||
| 42 | "Attempted to read input state, but applet resource is not initialized!"); | ||
| 43 | return false; | ||
| 44 | } | ||
| 45 | |||
| 46 | const auto press_state = | ||
| 47 | applet_resource | ||
| 48 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad) | ||
| 49 | .GetAndResetPressState(); | ||
| 50 | return press_state & KEYPAD_BITMASK; | ||
| 51 | } | ||
| 52 | |||
| 53 | void StandardVmCallbacks::DebugLog(u8 id, u64 value) { | ||
| 54 | LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value); | ||
| 55 | } | ||
| 56 | |||
| 57 | void StandardVmCallbacks::CommandLog(std::string_view data) { | ||
| 58 | LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}", | ||
| 59 | data.back() == '\n' ? data.substr(0, data.size() - 1) : data); | ||
| 60 | } | ||
| 61 | |||
| 62 | VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { | ||
| 63 | if ((in < metadata.main_nso_extents.base || | ||
| 64 | in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && | ||
| 65 | (in < metadata.heap_extents.base || | ||
| 66 | in >= metadata.heap_extents.base + metadata.heap_extents.size)) { | ||
| 67 | LOG_ERROR(CheatEngine, | ||
| 68 | "Cheat attempting to access memory at invalid address={:016X}, if this " | ||
| 69 | "persists, " | ||
| 70 | "the cheat may be incorrect. However, this may be normal early in execution if " | ||
| 71 | "the game has not properly set up yet.", | ||
| 72 | in); | ||
| 73 | return 0; ///< Invalid addresses will hard crash | ||
| 74 | } | ||
| 75 | |||
| 76 | return in; | ||
| 77 | } | ||
| 78 | |||
| 79 | CheatParser::~CheatParser() = default; | ||
| 80 | |||
| 81 | TextCheatParser::~TextCheatParser() = default; | ||
| 82 | |||
| 83 | namespace { | ||
| 84 | template <char match> | ||
| 85 | std::string_view ExtractName(std::string_view data, std::size_t start_index) { | ||
| 86 | auto end_index = start_index; | ||
| 87 | while (data[end_index] != match) { | ||
| 88 | ++end_index; | ||
| 89 | if (end_index > data.size() || | ||
| 90 | (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) { | ||
| 91 | return {}; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | return data.substr(start_index, end_index - start_index); | ||
| 96 | } | ||
| 97 | } // Anonymous namespace | ||
| 98 | |||
| 99 | std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | ||
| 100 | std::string_view data) const { | ||
| 101 | std::vector<CheatEntry> out(1); | ||
| 102 | std::optional<u64> current_entry = std::nullopt; | ||
| 103 | |||
| 104 | for (std::size_t i = 0; i < data.size(); ++i) { | ||
| 105 | if (::isspace(data[i])) { | ||
| 106 | continue; | ||
| 107 | } | ||
| 108 | |||
| 109 | if (data[i] == '{') { | ||
| 110 | current_entry = 0; | ||
| 111 | |||
| 112 | if (out[*current_entry].definition.num_opcodes > 0) { | ||
| 113 | return {}; | ||
| 114 | } | ||
| 115 | |||
| 116 | const auto name = ExtractName<'}'>(data, i + 1); | ||
| 117 | if (name.empty()) { | ||
| 118 | return {}; | ||
| 119 | } | ||
| 120 | |||
| 121 | std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(), | ||
| 122 | std::min<std::size_t>(out[*current_entry].definition.readable_name.size(), | ||
| 123 | name.size())); | ||
| 124 | out[*current_entry] | ||
| 125 | .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] = | ||
| 126 | '\0'; | ||
| 127 | |||
| 128 | i += name.length() + 1; | ||
| 129 | } else if (data[i] == '[') { | ||
| 130 | current_entry = out.size(); | ||
| 131 | out.emplace_back(); | ||
| 132 | |||
| 133 | const auto name = ExtractName<']'>(data, i + 1); | ||
| 134 | if (name.empty()) { | ||
| 135 | return {}; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(), | ||
| 139 | std::min<std::size_t>(out[*current_entry].definition.readable_name.size(), | ||
| 140 | name.size())); | ||
| 141 | out[*current_entry] | ||
| 142 | .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] = | ||
| 143 | '\0'; | ||
| 144 | |||
| 145 | i += name.length() + 1; | ||
| 146 | } else if (::isxdigit(data[i])) { | ||
| 147 | if (!current_entry || out[*current_entry].definition.num_opcodes >= | ||
| 148 | out[*current_entry].definition.opcodes.size()) { | ||
| 149 | return {}; | ||
| 150 | } | ||
| 151 | |||
| 152 | const auto hex = std::string(data.substr(i, 8)); | ||
| 153 | if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) { | ||
| 154 | return {}; | ||
| 155 | } | ||
| 156 | |||
| 157 | out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] = | ||
| 158 | std::stoul(hex, nullptr, 0x10); | ||
| 159 | |||
| 160 | i += 8; | ||
| 161 | } else { | ||
| 162 | return {}; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | out[0].enabled = out[0].definition.num_opcodes > 0; | ||
| 167 | out[0].cheat_id = 0; | ||
| 168 | |||
| 169 | for (u32 i = 1; i < out.size(); ++i) { | ||
| 170 | out[i].enabled = out[i].definition.num_opcodes > 0; | ||
| 171 | out[i].cheat_id = i; | ||
| 172 | } | ||
| 173 | |||
| 174 | return out; | ||
| 175 | } | ||
| 176 | |||
| 177 | CheatEngine::CheatEngine(Core::System& system, std::vector<CheatEntry> cheats, | ||
| 178 | const std::array<u8, 0x20>& build_id) | ||
| 179 | : system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique<StandardVmCallbacks>( | ||
| 180 | system, metadata)}, | ||
| 181 | cheats(std::move(cheats)) { | ||
| 182 | metadata.main_nso_build_id = build_id; | ||
| 183 | } | ||
| 184 | |||
| 185 | CheatEngine::~CheatEngine() { | ||
| 186 | core_timing.UnscheduleEvent(event, 0); | ||
| 187 | } | ||
| 188 | |||
| 189 | void CheatEngine::Initialize() { | ||
| 190 | event = core_timing.RegisterEvent( | ||
| 191 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | ||
| 192 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | ||
| 193 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | ||
| 194 | |||
| 195 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | ||
| 196 | metadata.title_id = system.CurrentProcess()->GetTitleID(); | ||
| 197 | |||
| 198 | const auto& vm_manager = system.CurrentProcess()->VMManager(); | ||
| 199 | metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()}; | ||
| 200 | metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(), | ||
| 201 | vm_manager.GetAddressSpaceSize()}; | ||
| 202 | metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()}; | ||
| 203 | |||
| 204 | is_pending_reload.exchange(true); | ||
| 205 | } | ||
| 206 | |||
| 207 | void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { | ||
| 208 | metadata.main_nso_extents = {main_region_begin, main_region_size}; | ||
| 209 | } | ||
| 210 | |||
| 211 | void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | ||
| 212 | this->cheats = std::move(cheats); | ||
| 213 | is_pending_reload.exchange(true); | ||
| 214 | } | ||
| 215 | |||
| 216 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | ||
| 217 | |||
| 218 | void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | ||
| 219 | if (is_pending_reload.exchange(false)) { | ||
| 220 | vm.LoadProgram(cheats); | ||
| 221 | } | ||
| 222 | |||
| 223 | if (vm.GetProgramSize() == 0) { | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | |||
| 227 | MICROPROFILE_SCOPE(Cheat_Engine); | ||
| 228 | |||
| 229 | vm.Execute(metadata); | ||
| 230 | |||
| 231 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | ||
| 232 | } | ||
| 233 | |||
| 234 | } // namespace Memory | ||
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h new file mode 100644 index 000000000..0f012e9b5 --- /dev/null +++ b/src/core/memory/cheat_engine.h | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/memory/dmnt_cheat_types.h" | ||
| 11 | #include "core/memory/dmnt_cheat_vm.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Core::Timing { | ||
| 18 | class CoreTiming; | ||
| 19 | struct EventType; | ||
| 20 | } // namespace Core::Timing | ||
| 21 | |||
| 22 | namespace Memory { | ||
| 23 | |||
| 24 | class StandardVmCallbacks : public DmntCheatVm::Callbacks { | ||
| 25 | public: | ||
| 26 | StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata); | ||
| 27 | ~StandardVmCallbacks() override; | ||
| 28 | |||
| 29 | void MemoryRead(VAddr address, void* data, u64 size) override; | ||
| 30 | void MemoryWrite(VAddr address, const void* data, u64 size) override; | ||
| 31 | u64 HidKeysDown() override; | ||
| 32 | void DebugLog(u8 id, u64 value) override; | ||
| 33 | void CommandLog(std::string_view data) override; | ||
| 34 | |||
| 35 | private: | ||
| 36 | VAddr SanitizeAddress(VAddr address) const; | ||
| 37 | |||
| 38 | const CheatProcessMetadata& metadata; | ||
| 39 | const Core::System& system; | ||
| 40 | }; | ||
| 41 | |||
| 42 | // Intermediary class that parses a text file or other disk format for storing cheats into a | ||
| 43 | // CheatList object, that can be used for execution. | ||
| 44 | class CheatParser { | ||
| 45 | public: | ||
| 46 | virtual ~CheatParser(); | ||
| 47 | |||
| 48 | virtual std::vector<CheatEntry> Parse(const Core::System& system, | ||
| 49 | std::string_view data) const = 0; | ||
| 50 | }; | ||
| 51 | |||
| 52 | // CheatParser implementation that parses text files | ||
| 53 | class TextCheatParser final : public CheatParser { | ||
| 54 | public: | ||
| 55 | ~TextCheatParser() override; | ||
| 56 | |||
| 57 | std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override; | ||
| 58 | }; | ||
| 59 | |||
| 60 | // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming | ||
| 61 | class CheatEngine final { | ||
| 62 | public: | ||
| 63 | CheatEngine(Core::System& system_, std::vector<CheatEntry> cheats_, | ||
| 64 | const std::array<u8, 0x20>& build_id); | ||
| 65 | ~CheatEngine(); | ||
| 66 | |||
| 67 | void Initialize(); | ||
| 68 | void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size); | ||
| 69 | |||
| 70 | void Reload(std::vector<CheatEntry> cheats); | ||
| 71 | |||
| 72 | private: | ||
| 73 | void FrameCallback(u64 userdata, s64 cycles_late); | ||
| 74 | |||
| 75 | DmntCheatVm vm; | ||
| 76 | CheatProcessMetadata metadata; | ||
| 77 | |||
| 78 | std::vector<CheatEntry> cheats; | ||
| 79 | std::atomic_bool is_pending_reload{false}; | ||
| 80 | |||
| 81 | Core::Timing::EventType* event{}; | ||
| 82 | Core::Timing::CoreTiming& core_timing; | ||
| 83 | Core::System& system; | ||
| 84 | }; | ||
| 85 | |||
| 86 | } // namespace Memory | ||
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h new file mode 100644 index 000000000..bf68fa0fe --- /dev/null +++ b/src/core/memory/dmnt_cheat_types.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2018-2019 Atmosphère-NX | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Adapted by DarkLordZach for use/interaction with yuzu | ||
| 19 | * | ||
| 20 | * Modifications Copyright 2019 yuzu emulator team | ||
| 21 | * Licensed under GPLv2 or any later version | ||
| 22 | * Refer to the license.txt file included. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #pragma once | ||
| 26 | |||
| 27 | #include "common/common_types.h" | ||
| 28 | |||
| 29 | namespace Memory { | ||
| 30 | |||
| 31 | struct MemoryRegionExtents { | ||
| 32 | u64 base{}; | ||
| 33 | u64 size{}; | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct CheatProcessMetadata { | ||
| 37 | u64 process_id{}; | ||
| 38 | u64 title_id{}; | ||
| 39 | MemoryRegionExtents main_nso_extents{}; | ||
| 40 | MemoryRegionExtents heap_extents{}; | ||
| 41 | MemoryRegionExtents alias_extents{}; | ||
| 42 | MemoryRegionExtents address_space_extents{}; | ||
| 43 | std::array<u8, 0x20> main_nso_build_id{}; | ||
| 44 | }; | ||
| 45 | |||
| 46 | struct CheatDefinition { | ||
| 47 | std::array<char, 0x40> readable_name{}; | ||
| 48 | u32 num_opcodes{}; | ||
| 49 | std::array<u32, 0x100> opcodes{}; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct CheatEntry { | ||
| 53 | bool enabled{}; | ||
| 54 | u32 cheat_id{}; | ||
| 55 | CheatDefinition definition{}; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } // namespace Memory | ||
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp new file mode 100644 index 000000000..cc16d15a4 --- /dev/null +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -0,0 +1,1212 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2018-2019 Atmosphère-NX | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Adapted by DarkLordZach for use/interaction with yuzu | ||
| 19 | * | ||
| 20 | * Modifications Copyright 2019 yuzu emulator team | ||
| 21 | * Licensed under GPLv2 or any later version | ||
| 22 | * Refer to the license.txt file included. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include "common/assert.h" | ||
| 26 | #include "common/scope_exit.h" | ||
| 27 | #include "core/memory/dmnt_cheat_types.h" | ||
| 28 | #include "core/memory/dmnt_cheat_vm.h" | ||
| 29 | |||
| 30 | namespace Memory { | ||
| 31 | |||
| 32 | DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} | ||
| 33 | |||
| 34 | DmntCheatVm::~DmntCheatVm() = default; | ||
| 35 | |||
| 36 | void DmntCheatVm::DebugLog(u32 log_id, u64 value) { | ||
| 37 | callbacks->DebugLog(static_cast<u8>(log_id), value); | ||
| 38 | } | ||
| 39 | |||
| 40 | void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) { | ||
| 41 | if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) { | ||
| 42 | callbacks->CommandLog("Opcode: Store Static"); | ||
| 43 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width)); | ||
| 44 | callbacks->CommandLog( | ||
| 45 | fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type))); | ||
| 46 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register)); | ||
| 47 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address)); | ||
| 48 | callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64)); | ||
| 49 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) { | ||
| 50 | callbacks->CommandLog("Opcode: Begin Conditional"); | ||
| 51 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width)); | ||
| 52 | callbacks->CommandLog( | ||
| 53 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type))); | ||
| 54 | callbacks->CommandLog( | ||
| 55 | fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type))); | ||
| 56 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address)); | ||
| 57 | callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64)); | ||
| 58 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) { | ||
| 59 | callbacks->CommandLog("Opcode: End Conditional"); | ||
| 60 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) { | ||
| 61 | if (ctrl_loop->start_loop) { | ||
| 62 | callbacks->CommandLog("Opcode: Start Loop"); | ||
| 63 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); | ||
| 64 | callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters)); | ||
| 65 | } else { | ||
| 66 | callbacks->CommandLog("Opcode: End Loop"); | ||
| 67 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); | ||
| 68 | } | ||
| 69 | } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) { | ||
| 70 | callbacks->CommandLog("Opcode: Load Register Static"); | ||
| 71 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index)); | ||
| 72 | callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value)); | ||
| 73 | } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) { | ||
| 74 | callbacks->CommandLog("Opcode: Load Register Memory"); | ||
| 75 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width)); | ||
| 76 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index)); | ||
| 77 | callbacks->CommandLog( | ||
| 78 | fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type))); | ||
| 79 | callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg)); | ||
| 80 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address)); | ||
| 81 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) { | ||
| 82 | callbacks->CommandLog("Opcode: Store Static to Address"); | ||
| 83 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width)); | ||
| 84 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index)); | ||
| 85 | if (str_static->add_offset_reg) { | ||
| 86 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index)); | ||
| 87 | } | ||
| 88 | callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg)); | ||
| 89 | callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value)); | ||
| 90 | } else if (auto perform_math_static = | ||
| 91 | std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) { | ||
| 92 | callbacks->CommandLog("Opcode: Perform Static Arithmetic"); | ||
| 93 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width)); | ||
| 94 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index)); | ||
| 95 | callbacks->CommandLog( | ||
| 96 | fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type))); | ||
| 97 | callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value)); | ||
| 98 | } else if (auto begin_keypress_cond = | ||
| 99 | std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) { | ||
| 100 | callbacks->CommandLog("Opcode: Begin Keypress Conditional"); | ||
| 101 | callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask)); | ||
| 102 | } else if (auto perform_math_reg = | ||
| 103 | std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) { | ||
| 104 | callbacks->CommandLog("Opcode: Perform Register Arithmetic"); | ||
| 105 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width)); | ||
| 106 | callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index)); | ||
| 107 | callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index)); | ||
| 108 | if (perform_math_reg->has_immediate) { | ||
| 109 | callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64)); | ||
| 110 | } else { | ||
| 111 | callbacks->CommandLog( | ||
| 112 | fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index)); | ||
| 113 | } | ||
| 114 | } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) { | ||
| 115 | callbacks->CommandLog("Opcode: Store Register to Address"); | ||
| 116 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width)); | ||
| 117 | callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index)); | ||
| 118 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index)); | ||
| 119 | callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg)); | ||
| 120 | switch (str_register->ofs_type) { | ||
| 121 | case StoreRegisterOffsetType::None: | ||
| 122 | break; | ||
| 123 | case StoreRegisterOffsetType::Reg: | ||
| 124 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index)); | ||
| 125 | break; | ||
| 126 | case StoreRegisterOffsetType::Imm: | ||
| 127 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); | ||
| 128 | break; | ||
| 129 | case StoreRegisterOffsetType::MemReg: | ||
| 130 | callbacks->CommandLog( | ||
| 131 | fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); | ||
| 132 | break; | ||
| 133 | case StoreRegisterOffsetType::MemImm: | ||
| 134 | case StoreRegisterOffsetType::MemImmReg: | ||
| 135 | callbacks->CommandLog( | ||
| 136 | fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); | ||
| 137 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) { | ||
| 141 | callbacks->CommandLog("Opcode: Begin Register Conditional"); | ||
| 142 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width)); | ||
| 143 | callbacks->CommandLog( | ||
| 144 | fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type))); | ||
| 145 | callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index)); | ||
| 146 | switch (begin_reg_cond->comp_type) { | ||
| 147 | case CompareRegisterValueType::StaticValue: | ||
| 148 | callbacks->CommandLog("Comp Type: Static Value"); | ||
| 149 | callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64)); | ||
| 150 | break; | ||
| 151 | case CompareRegisterValueType::OtherRegister: | ||
| 152 | callbacks->CommandLog("Comp Type: Other Register"); | ||
| 153 | callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index)); | ||
| 154 | break; | ||
| 155 | case CompareRegisterValueType::MemoryRelAddr: | ||
| 156 | callbacks->CommandLog("Comp Type: Memory Relative Address"); | ||
| 157 | callbacks->CommandLog( | ||
| 158 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); | ||
| 159 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); | ||
| 160 | break; | ||
| 161 | case CompareRegisterValueType::MemoryOfsReg: | ||
| 162 | callbacks->CommandLog("Comp Type: Memory Offset Register"); | ||
| 163 | callbacks->CommandLog( | ||
| 164 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); | ||
| 165 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); | ||
| 166 | break; | ||
| 167 | case CompareRegisterValueType::RegisterRelAddr: | ||
| 168 | callbacks->CommandLog("Comp Type: Register Relative Address"); | ||
| 169 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); | ||
| 170 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); | ||
| 171 | break; | ||
| 172 | case CompareRegisterValueType::RegisterOfsReg: | ||
| 173 | callbacks->CommandLog("Comp Type: Register Offset Register"); | ||
| 174 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); | ||
| 175 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) { | ||
| 179 | callbacks->CommandLog("Opcode: Save or Restore Register"); | ||
| 180 | callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index)); | ||
| 181 | callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index)); | ||
| 182 | callbacks->CommandLog( | ||
| 183 | fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type))); | ||
| 184 | } else if (auto save_restore_regmask = | ||
| 185 | std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) { | ||
| 186 | callbacks->CommandLog("Opcode: Save or Restore Register Mask"); | ||
| 187 | callbacks->CommandLog( | ||
| 188 | fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type))); | ||
| 189 | for (std::size_t i = 0; i < NumRegisters; i++) { | ||
| 190 | callbacks->CommandLog( | ||
| 191 | fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i])); | ||
| 192 | } | ||
| 193 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) { | ||
| 194 | callbacks->CommandLog("Opcode: Debug Log"); | ||
| 195 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width)); | ||
| 196 | callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id)); | ||
| 197 | callbacks->CommandLog( | ||
| 198 | fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type))); | ||
| 199 | switch (debug_log->val_type) { | ||
| 200 | case DebugLogValueType::RegisterValue: | ||
| 201 | callbacks->CommandLog("Val Type: Register Value"); | ||
| 202 | callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index)); | ||
| 203 | break; | ||
| 204 | case DebugLogValueType::MemoryRelAddr: | ||
| 205 | callbacks->CommandLog("Val Type: Memory Relative Address"); | ||
| 206 | callbacks->CommandLog( | ||
| 207 | fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); | ||
| 208 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); | ||
| 209 | break; | ||
| 210 | case DebugLogValueType::MemoryOfsReg: | ||
| 211 | callbacks->CommandLog("Val Type: Memory Offset Register"); | ||
| 212 | callbacks->CommandLog( | ||
| 213 | fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); | ||
| 214 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); | ||
| 215 | break; | ||
| 216 | case DebugLogValueType::RegisterRelAddr: | ||
| 217 | callbacks->CommandLog("Val Type: Register Relative Address"); | ||
| 218 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); | ||
| 219 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); | ||
| 220 | break; | ||
| 221 | case DebugLogValueType::RegisterOfsReg: | ||
| 222 | callbacks->CommandLog("Val Type: Register Offset Register"); | ||
| 223 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); | ||
| 224 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) { | ||
| 228 | callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode))); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | DmntCheatVm::Callbacks::~Callbacks() = default; | ||
| 233 | |||
| 234 | bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | ||
| 235 | // If we've ever seen a decode failure, return false. | ||
| 236 | bool valid = decode_success; | ||
| 237 | CheatVmOpcode opcode = {}; | ||
| 238 | SCOPE_EXIT({ | ||
| 239 | decode_success &= valid; | ||
| 240 | if (valid) { | ||
| 241 | out = opcode; | ||
| 242 | } | ||
| 243 | }); | ||
| 244 | |||
| 245 | // Helper function for getting instruction dwords. | ||
| 246 | const auto GetNextDword = [&] { | ||
| 247 | if (instruction_ptr >= num_opcodes) { | ||
| 248 | valid = false; | ||
| 249 | return static_cast<u32>(0); | ||
| 250 | } | ||
| 251 | return program[instruction_ptr++]; | ||
| 252 | }; | ||
| 253 | |||
| 254 | // Helper function for parsing a VmInt. | ||
| 255 | const auto GetNextVmInt = [&](const u32 bit_width) { | ||
| 256 | VmInt val{}; | ||
| 257 | |||
| 258 | const u32 first_dword = GetNextDword(); | ||
| 259 | switch (bit_width) { | ||
| 260 | case 1: | ||
| 261 | val.bit8 = static_cast<u8>(first_dword); | ||
| 262 | break; | ||
| 263 | case 2: | ||
| 264 | val.bit16 = static_cast<u16>(first_dword); | ||
| 265 | break; | ||
| 266 | case 4: | ||
| 267 | val.bit32 = first_dword; | ||
| 268 | break; | ||
| 269 | case 8: | ||
| 270 | val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword()); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | |||
| 274 | return val; | ||
| 275 | }; | ||
| 276 | |||
| 277 | // Read opcode. | ||
| 278 | const u32 first_dword = GetNextDword(); | ||
| 279 | if (!valid) { | ||
| 280 | return valid; | ||
| 281 | } | ||
| 282 | |||
| 283 | auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF)); | ||
| 284 | if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) { | ||
| 285 | opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | | ||
| 286 | ((first_dword >> 24) & 0xF)); | ||
| 287 | } | ||
| 288 | if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) { | ||
| 289 | opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | | ||
| 290 | ((first_dword >> 20) & 0xF)); | ||
| 291 | } | ||
| 292 | |||
| 293 | // detect condition start. | ||
| 294 | switch (opcode_type) { | ||
| 295 | case CheatVmOpcodeType::BeginConditionalBlock: | ||
| 296 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: | ||
| 297 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: | ||
| 298 | opcode.begin_conditional_block = true; | ||
| 299 | break; | ||
| 300 | default: | ||
| 301 | opcode.begin_conditional_block = false; | ||
| 302 | break; | ||
| 303 | } | ||
| 304 | |||
| 305 | switch (opcode_type) { | ||
| 306 | case CheatVmOpcodeType::StoreStatic: { | ||
| 307 | StoreStaticOpcode store_static{}; | ||
| 308 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | ||
| 309 | // Read additional words. | ||
| 310 | const u32 second_dword = GetNextDword(); | ||
| 311 | store_static.bit_width = (first_dword >> 24) & 0xF; | ||
| 312 | store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | ||
| 313 | store_static.offset_register = ((first_dword >> 16) & 0xF); | ||
| 314 | store_static.rel_address = | ||
| 315 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | ||
| 316 | store_static.value = GetNextVmInt(store_static.bit_width); | ||
| 317 | opcode.opcode = store_static; | ||
| 318 | } break; | ||
| 319 | case CheatVmOpcodeType::BeginConditionalBlock: { | ||
| 320 | BeginConditionalOpcode begin_cond{}; | ||
| 321 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | ||
| 322 | // Read additional words. | ||
| 323 | const u32 second_dword = GetNextDword(); | ||
| 324 | begin_cond.bit_width = (first_dword >> 24) & 0xF; | ||
| 325 | begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | ||
| 326 | begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | ||
| 327 | begin_cond.rel_address = | ||
| 328 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | ||
| 329 | begin_cond.value = GetNextVmInt(begin_cond.bit_width); | ||
| 330 | opcode.opcode = begin_cond; | ||
| 331 | } break; | ||
| 332 | case CheatVmOpcodeType::EndConditionalBlock: { | ||
| 333 | // 20000000 | ||
| 334 | // There's actually nothing left to process here! | ||
| 335 | opcode.opcode = EndConditionalOpcode{}; | ||
| 336 | } break; | ||
| 337 | case CheatVmOpcodeType::ControlLoop: { | ||
| 338 | ControlLoopOpcode ctrl_loop{}; | ||
| 339 | // 300R0000 VVVVVVVV | ||
| 340 | // 310R0000 | ||
| 341 | // Parse register, whether loop start or loop end. | ||
| 342 | ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; | ||
| 343 | ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); | ||
| 344 | |||
| 345 | // Read number of iters if loop start. | ||
| 346 | if (ctrl_loop.start_loop) { | ||
| 347 | ctrl_loop.num_iters = GetNextDword(); | ||
| 348 | } | ||
| 349 | opcode.opcode = ctrl_loop; | ||
| 350 | } break; | ||
| 351 | case CheatVmOpcodeType::LoadRegisterStatic: { | ||
| 352 | LoadRegisterStaticOpcode ldr_static{}; | ||
| 353 | // 400R0000 VVVVVVVV VVVVVVVV | ||
| 354 | // Read additional words. | ||
| 355 | ldr_static.reg_index = ((first_dword >> 16) & 0xF); | ||
| 356 | ldr_static.value = | ||
| 357 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | ||
| 358 | opcode.opcode = ldr_static; | ||
| 359 | } break; | ||
| 360 | case CheatVmOpcodeType::LoadRegisterMemory: { | ||
| 361 | LoadRegisterMemoryOpcode ldr_memory{}; | ||
| 362 | // 5TMRI0AA AAAAAAAA | ||
| 363 | // Read additional words. | ||
| 364 | const u32 second_dword = GetNextDword(); | ||
| 365 | ldr_memory.bit_width = (first_dword >> 24) & 0xF; | ||
| 366 | ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | ||
| 367 | ldr_memory.reg_index = ((first_dword >> 16) & 0xF); | ||
| 368 | ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; | ||
| 369 | ldr_memory.rel_address = | ||
| 370 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | ||
| 371 | opcode.opcode = ldr_memory; | ||
| 372 | } break; | ||
| 373 | case CheatVmOpcodeType::StoreStaticToAddress: { | ||
| 374 | StoreStaticToAddressOpcode str_static{}; | ||
| 375 | // 6T0RIor0 VVVVVVVV VVVVVVVV | ||
| 376 | // Read additional words. | ||
| 377 | str_static.bit_width = (first_dword >> 24) & 0xF; | ||
| 378 | str_static.reg_index = ((first_dword >> 16) & 0xF); | ||
| 379 | str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; | ||
| 380 | str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; | ||
| 381 | str_static.offset_reg_index = ((first_dword >> 4) & 0xF); | ||
| 382 | str_static.value = | ||
| 383 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | ||
| 384 | opcode.opcode = str_static; | ||
| 385 | } break; | ||
| 386 | case CheatVmOpcodeType::PerformArithmeticStatic: { | ||
| 387 | PerformArithmeticStaticOpcode perform_math_static{}; | ||
| 388 | // 7T0RC000 VVVVVVVV | ||
| 389 | // Read additional words. | ||
| 390 | perform_math_static.bit_width = (first_dword >> 24) & 0xF; | ||
| 391 | perform_math_static.reg_index = ((first_dword >> 16) & 0xF); | ||
| 392 | perform_math_static.math_type = | ||
| 393 | static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); | ||
| 394 | perform_math_static.value = GetNextDword(); | ||
| 395 | opcode.opcode = perform_math_static; | ||
| 396 | } break; | ||
| 397 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { | ||
| 398 | BeginKeypressConditionalOpcode begin_keypress_cond{}; | ||
| 399 | // 8kkkkkkk | ||
| 400 | // Just parse the mask. | ||
| 401 | begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; | ||
| 402 | } break; | ||
| 403 | case CheatVmOpcodeType::PerformArithmeticRegister: { | ||
| 404 | PerformArithmeticRegisterOpcode perform_math_reg{}; | ||
| 405 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) | ||
| 406 | perform_math_reg.bit_width = (first_dword >> 24) & 0xF; | ||
| 407 | perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); | ||
| 408 | perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); | ||
| 409 | perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); | ||
| 410 | perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; | ||
| 411 | if (perform_math_reg.has_immediate) { | ||
| 412 | perform_math_reg.src_reg_2_index = 0; | ||
| 413 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); | ||
| 414 | } else { | ||
| 415 | perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF); | ||
| 416 | } | ||
| 417 | opcode.opcode = perform_math_reg; | ||
| 418 | } break; | ||
| 419 | case CheatVmOpcodeType::StoreRegisterToAddress: { | ||
| 420 | StoreRegisterToAddressOpcode str_register{}; | ||
| 421 | // ATSRIOxa (aaaaaaaa) | ||
| 422 | // A = opcode 10 | ||
| 423 | // T = bit width | ||
| 424 | // S = src register index | ||
| 425 | // R = address register index | ||
| 426 | // I = 1 if increment address register, 0 if not increment address register | ||
| 427 | // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region, | ||
| 428 | // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region + | ||
| 429 | // Relative Address | ||
| 430 | // x = offset register (for offset type 1), memory type (for offset type 3) | ||
| 431 | // a = relative address (for offset type 2+3) | ||
| 432 | str_register.bit_width = (first_dword >> 24) & 0xF; | ||
| 433 | str_register.str_reg_index = ((first_dword >> 20) & 0xF); | ||
| 434 | str_register.addr_reg_index = ((first_dword >> 16) & 0xF); | ||
| 435 | str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; | ||
| 436 | str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); | ||
| 437 | str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); | ||
| 438 | switch (str_register.ofs_type) { | ||
| 439 | case StoreRegisterOffsetType::None: | ||
| 440 | case StoreRegisterOffsetType::Reg: | ||
| 441 | // Nothing more to do | ||
| 442 | break; | ||
| 443 | case StoreRegisterOffsetType::Imm: | ||
| 444 | str_register.rel_address = | ||
| 445 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 446 | break; | ||
| 447 | case StoreRegisterOffsetType::MemReg: | ||
| 448 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 449 | break; | ||
| 450 | case StoreRegisterOffsetType::MemImm: | ||
| 451 | case StoreRegisterOffsetType::MemImmReg: | ||
| 452 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 453 | str_register.rel_address = | ||
| 454 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 455 | break; | ||
| 456 | default: | ||
| 457 | str_register.ofs_type = StoreRegisterOffsetType::None; | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | opcode.opcode = str_register; | ||
| 461 | } break; | ||
| 462 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { | ||
| 463 | BeginRegisterConditionalOpcode begin_reg_cond{}; | ||
| 464 | // C0TcSX## | ||
| 465 | // C0TcS0Ma aaaaaaaa | ||
| 466 | // C0TcS1Mr | ||
| 467 | // C0TcS2Ra aaaaaaaa | ||
| 468 | // C0TcS3Rr | ||
| 469 | // C0TcS400 VVVVVVVV (VVVVVVVV) | ||
| 470 | // C0TcS5X0 | ||
| 471 | // C0 = opcode 0xC0 | ||
| 472 | // T = bit width | ||
| 473 | // c = condition type. | ||
| 474 | // S = source register. | ||
| 475 | // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset | ||
| 476 | // register, | ||
| 477 | // 2 = register with relative offset, 3 = register with offset register, 4 = static | ||
| 478 | // value, 5 = other register. | ||
| 479 | // M = memory type. | ||
| 480 | // R = address register. | ||
| 481 | // a = relative address. | ||
| 482 | // r = offset register. | ||
| 483 | // X = other register. | ||
| 484 | // V = value. | ||
| 485 | begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; | ||
| 486 | begin_reg_cond.cond_type = | ||
| 487 | static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | ||
| 488 | begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); | ||
| 489 | begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); | ||
| 490 | |||
| 491 | switch (begin_reg_cond.comp_type) { | ||
| 492 | case CompareRegisterValueType::StaticValue: | ||
| 493 | begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width); | ||
| 494 | break; | ||
| 495 | case CompareRegisterValueType::OtherRegister: | ||
| 496 | begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF); | ||
| 497 | break; | ||
| 498 | case CompareRegisterValueType::MemoryRelAddr: | ||
| 499 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 500 | begin_reg_cond.rel_address = | ||
| 501 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 502 | break; | ||
| 503 | case CompareRegisterValueType::MemoryOfsReg: | ||
| 504 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 505 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | ||
| 506 | break; | ||
| 507 | case CompareRegisterValueType::RegisterRelAddr: | ||
| 508 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | ||
| 509 | begin_reg_cond.rel_address = | ||
| 510 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 511 | break; | ||
| 512 | case CompareRegisterValueType::RegisterOfsReg: | ||
| 513 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | ||
| 514 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | ||
| 515 | break; | ||
| 516 | } | ||
| 517 | opcode.opcode = begin_reg_cond; | ||
| 518 | } break; | ||
| 519 | case CheatVmOpcodeType::SaveRestoreRegister: { | ||
| 520 | SaveRestoreRegisterOpcode save_restore_reg{}; | ||
| 521 | // C10D0Sx0 | ||
| 522 | // C1 = opcode 0xC1 | ||
| 523 | // D = destination index. | ||
| 524 | // S = source index. | ||
| 525 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring | ||
| 526 | // a register. | ||
| 527 | // NOTE: If we add more save slots later, current encoding is backwards compatible. | ||
| 528 | save_restore_reg.dst_index = (first_dword >> 16) & 0xF; | ||
| 529 | save_restore_reg.src_index = (first_dword >> 8) & 0xF; | ||
| 530 | save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); | ||
| 531 | opcode.opcode = save_restore_reg; | ||
| 532 | } break; | ||
| 533 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { | ||
| 534 | SaveRestoreRegisterMaskOpcode save_restore_regmask{}; | ||
| 535 | // C2x0XXXX | ||
| 536 | // C2 = opcode 0xC2 | ||
| 537 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. | ||
| 538 | // X = 16-bit bitmask, bit i --> save or restore register i. | ||
| 539 | save_restore_regmask.op_type = | ||
| 540 | static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); | ||
| 541 | for (std::size_t i = 0; i < NumRegisters; i++) { | ||
| 542 | save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; | ||
| 543 | } | ||
| 544 | opcode.opcode = save_restore_regmask; | ||
| 545 | } break; | ||
| 546 | case CheatVmOpcodeType::DebugLog: { | ||
| 547 | DebugLogOpcode debug_log{}; | ||
| 548 | // FFFTIX## | ||
| 549 | // FFFTI0Ma aaaaaaaa | ||
| 550 | // FFFTI1Mr | ||
| 551 | // FFFTI2Ra aaaaaaaa | ||
| 552 | // FFFTI3Rr | ||
| 553 | // FFFTI4X0 | ||
| 554 | // FFF = opcode 0xFFF | ||
| 555 | // T = bit width. | ||
| 556 | // I = log id. | ||
| 557 | // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset | ||
| 558 | // register, | ||
| 559 | // 2 = register with relative offset, 3 = register with offset register, 4 = register | ||
| 560 | // value. | ||
| 561 | // M = memory type. | ||
| 562 | // R = address register. | ||
| 563 | // a = relative address. | ||
| 564 | // r = offset register. | ||
| 565 | // X = value register. | ||
| 566 | debug_log.bit_width = (first_dword >> 16) & 0xF; | ||
| 567 | debug_log.log_id = ((first_dword >> 12) & 0xF); | ||
| 568 | debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); | ||
| 569 | |||
| 570 | switch (debug_log.val_type) { | ||
| 571 | case DebugLogValueType::RegisterValue: | ||
| 572 | debug_log.val_reg_index = ((first_dword >> 4) & 0xF); | ||
| 573 | break; | ||
| 574 | case DebugLogValueType::MemoryRelAddr: | ||
| 575 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 576 | debug_log.rel_address = | ||
| 577 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 578 | break; | ||
| 579 | case DebugLogValueType::MemoryOfsReg: | ||
| 580 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | ||
| 581 | debug_log.ofs_reg_index = (first_dword & 0xF); | ||
| 582 | break; | ||
| 583 | case DebugLogValueType::RegisterRelAddr: | ||
| 584 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | ||
| 585 | debug_log.rel_address = | ||
| 586 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 587 | break; | ||
| 588 | case DebugLogValueType::RegisterOfsReg: | ||
| 589 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | ||
| 590 | debug_log.ofs_reg_index = (first_dword & 0xF); | ||
| 591 | break; | ||
| 592 | } | ||
| 593 | opcode.opcode = debug_log; | ||
| 594 | } break; | ||
| 595 | case CheatVmOpcodeType::ExtendedWidth: | ||
| 596 | case CheatVmOpcodeType::DoubleExtendedWidth: | ||
| 597 | default: | ||
| 598 | // Unrecognized instruction cannot be decoded. | ||
| 599 | valid = false; | ||
| 600 | opcode.opcode = UnrecognizedInstruction{opcode_type}; | ||
| 601 | break; | ||
| 602 | } | ||
| 603 | |||
| 604 | // End decoding. | ||
| 605 | return valid; | ||
| 606 | } | ||
| 607 | |||
| 608 | void DmntCheatVm::SkipConditionalBlock() { | ||
| 609 | if (condition_depth > 0) { | ||
| 610 | // We want to continue until we're out of the current block. | ||
| 611 | const std::size_t desired_depth = condition_depth - 1; | ||
| 612 | |||
| 613 | CheatVmOpcode skip_opcode{}; | ||
| 614 | while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) { | ||
| 615 | // Decode instructions until we see end of the current conditional block. | ||
| 616 | // NOTE: This is broken in gateway's implementation. | ||
| 617 | // Gateway currently checks for "0x2" instead of "0x20000000" | ||
| 618 | // In addition, they do a linear scan instead of correctly decoding opcodes. | ||
| 619 | // This causes issues if "0x2" appears as an immediate in the conditional block... | ||
| 620 | |||
| 621 | // We also support nesting of conditional blocks, and Gateway does not. | ||
| 622 | if (skip_opcode.begin_conditional_block) { | ||
| 623 | condition_depth++; | ||
| 624 | } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { | ||
| 625 | condition_depth--; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | } else { | ||
| 629 | // Skipping, but condition_depth = 0. | ||
| 630 | // This is an error condition. | ||
| 631 | // However, I don't actually believe it is possible for this to happen. | ||
| 632 | // I guess we'll throw a fatal error here, so as to encourage me to fix the VM | ||
| 633 | // in the event that someone triggers it? I don't know how you'd do that. | ||
| 634 | UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM"); | ||
| 635 | } | ||
| 636 | } | ||
| 637 | |||
| 638 | u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) { | ||
| 639 | switch (bit_width) { | ||
| 640 | case 1: | ||
| 641 | return value.bit8; | ||
| 642 | case 2: | ||
| 643 | return value.bit16; | ||
| 644 | case 4: | ||
| 645 | return value.bit32; | ||
| 646 | case 8: | ||
| 647 | return value.bit64; | ||
| 648 | default: | ||
| 649 | // Invalid bit width -> return 0. | ||
| 650 | return 0; | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, | ||
| 655 | MemoryAccessType mem_type, u64 rel_address) { | ||
| 656 | switch (mem_type) { | ||
| 657 | case MemoryAccessType::MainNso: | ||
| 658 | default: | ||
| 659 | return metadata.main_nso_extents.base + rel_address; | ||
| 660 | case MemoryAccessType::Heap: | ||
| 661 | return metadata.heap_extents.base + rel_address; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | void DmntCheatVm::ResetState() { | ||
| 666 | registers.fill(0); | ||
| 667 | saved_values.fill(0); | ||
| 668 | loop_tops.fill(0); | ||
| 669 | instruction_ptr = 0; | ||
| 670 | condition_depth = 0; | ||
| 671 | decode_success = true; | ||
| 672 | } | ||
| 673 | |||
| 674 | bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) { | ||
| 675 | // Reset opcode count. | ||
| 676 | num_opcodes = 0; | ||
| 677 | |||
| 678 | for (std::size_t i = 0; i < entries.size(); i++) { | ||
| 679 | if (entries[i].enabled) { | ||
| 680 | // Bounds check. | ||
| 681 | if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) { | ||
| 682 | num_opcodes = 0; | ||
| 683 | return false; | ||
| 684 | } | ||
| 685 | |||
| 686 | for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) { | ||
| 687 | program[num_opcodes++] = entries[i].definition.opcodes[n]; | ||
| 688 | } | ||
| 689 | } | ||
| 690 | } | ||
| 691 | |||
| 692 | return true; | ||
| 693 | } | ||
| 694 | |||
| 695 | void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | ||
| 696 | CheatVmOpcode cur_opcode{}; | ||
| 697 | |||
| 698 | // Get Keys down. | ||
| 699 | u64 kDown = callbacks->HidKeysDown(); | ||
| 700 | |||
| 701 | callbacks->CommandLog("Started VM execution."); | ||
| 702 | callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base)); | ||
| 703 | callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base)); | ||
| 704 | callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF))); | ||
| 705 | |||
| 706 | // Clear VM state. | ||
| 707 | ResetState(); | ||
| 708 | |||
| 709 | // Loop until program finishes. | ||
| 710 | while (DecodeNextOpcode(cur_opcode)) { | ||
| 711 | callbacks->CommandLog( | ||
| 712 | fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr))); | ||
| 713 | |||
| 714 | for (std::size_t i = 0; i < NumRegisters; i++) { | ||
| 715 | callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i])); | ||
| 716 | } | ||
| 717 | |||
| 718 | for (std::size_t i = 0; i < NumRegisters; i++) { | ||
| 719 | callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i])); | ||
| 720 | } | ||
| 721 | LogOpcode(cur_opcode); | ||
| 722 | |||
| 723 | // Increment conditional depth, if relevant. | ||
| 724 | if (cur_opcode.begin_conditional_block) { | ||
| 725 | condition_depth++; | ||
| 726 | } | ||
| 727 | |||
| 728 | if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) { | ||
| 729 | // Calculate address, write value to memory. | ||
| 730 | u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type, | ||
| 731 | store_static->rel_address + | ||
| 732 | registers[store_static->offset_register]); | ||
| 733 | u64 dst_value = GetVmInt(store_static->value, store_static->bit_width); | ||
| 734 | switch (store_static->bit_width) { | ||
| 735 | case 1: | ||
| 736 | case 2: | ||
| 737 | case 4: | ||
| 738 | case 8: | ||
| 739 | callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); | ||
| 740 | break; | ||
| 741 | } | ||
| 742 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { | ||
| 743 | // Read value from memory. | ||
| 744 | u64 src_address = | ||
| 745 | GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address); | ||
| 746 | u64 src_value = 0; | ||
| 747 | switch (store_static->bit_width) { | ||
| 748 | case 1: | ||
| 749 | case 2: | ||
| 750 | case 4: | ||
| 751 | case 8: | ||
| 752 | callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | // Check against condition. | ||
| 756 | u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width); | ||
| 757 | bool cond_met = false; | ||
| 758 | switch (begin_cond->cond_type) { | ||
| 759 | case ConditionalComparisonType::GT: | ||
| 760 | cond_met = src_value > cond_value; | ||
| 761 | break; | ||
| 762 | case ConditionalComparisonType::GE: | ||
| 763 | cond_met = src_value >= cond_value; | ||
| 764 | break; | ||
| 765 | case ConditionalComparisonType::LT: | ||
| 766 | cond_met = src_value < cond_value; | ||
| 767 | break; | ||
| 768 | case ConditionalComparisonType::LE: | ||
| 769 | cond_met = src_value <= cond_value; | ||
| 770 | break; | ||
| 771 | case ConditionalComparisonType::EQ: | ||
| 772 | cond_met = src_value == cond_value; | ||
| 773 | break; | ||
| 774 | case ConditionalComparisonType::NE: | ||
| 775 | cond_met = src_value != cond_value; | ||
| 776 | break; | ||
| 777 | } | ||
| 778 | // Skip conditional block if condition not met. | ||
| 779 | if (!cond_met) { | ||
| 780 | SkipConditionalBlock(); | ||
| 781 | } | ||
| 782 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { | ||
| 783 | // Decrement the condition depth. | ||
| 784 | // We will assume, graciously, that mismatched conditional block ends are a nop. | ||
| 785 | if (condition_depth > 0) { | ||
| 786 | condition_depth--; | ||
| 787 | } | ||
| 788 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { | ||
| 789 | if (ctrl_loop->start_loop) { | ||
| 790 | // Start a loop. | ||
| 791 | registers[ctrl_loop->reg_index] = ctrl_loop->num_iters; | ||
| 792 | loop_tops[ctrl_loop->reg_index] = instruction_ptr; | ||
| 793 | } else { | ||
| 794 | // End a loop. | ||
| 795 | registers[ctrl_loop->reg_index]--; | ||
| 796 | if (registers[ctrl_loop->reg_index] != 0) { | ||
| 797 | instruction_ptr = loop_tops[ctrl_loop->reg_index]; | ||
| 798 | } | ||
| 799 | } | ||
| 800 | } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) { | ||
| 801 | // Set a register to a static value. | ||
| 802 | registers[ldr_static->reg_index] = ldr_static->value; | ||
| 803 | } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) { | ||
| 804 | // Choose source address. | ||
| 805 | u64 src_address; | ||
| 806 | if (ldr_memory->load_from_reg) { | ||
| 807 | src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address; | ||
| 808 | } else { | ||
| 809 | src_address = | ||
| 810 | GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address); | ||
| 811 | } | ||
| 812 | // Read into register. Gateway only reads on valid bitwidth. | ||
| 813 | switch (ldr_memory->bit_width) { | ||
| 814 | case 1: | ||
| 815 | case 2: | ||
| 816 | case 4: | ||
| 817 | case 8: | ||
| 818 | callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], | ||
| 819 | ldr_memory->bit_width); | ||
| 820 | break; | ||
| 821 | } | ||
| 822 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { | ||
| 823 | // Calculate address. | ||
| 824 | u64 dst_address = registers[str_static->reg_index]; | ||
| 825 | u64 dst_value = str_static->value; | ||
| 826 | if (str_static->add_offset_reg) { | ||
| 827 | dst_address += registers[str_static->offset_reg_index]; | ||
| 828 | } | ||
| 829 | // Write value to memory. Gateway only writes on valid bitwidth. | ||
| 830 | switch (str_static->bit_width) { | ||
| 831 | case 1: | ||
| 832 | case 2: | ||
| 833 | case 4: | ||
| 834 | case 8: | ||
| 835 | callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); | ||
| 836 | break; | ||
| 837 | } | ||
| 838 | // Increment register if relevant. | ||
| 839 | if (str_static->increment_reg) { | ||
| 840 | registers[str_static->reg_index] += str_static->bit_width; | ||
| 841 | } | ||
| 842 | } else if (auto perform_math_static = | ||
| 843 | std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) { | ||
| 844 | // Do requested math. | ||
| 845 | switch (perform_math_static->math_type) { | ||
| 846 | case RegisterArithmeticType::Addition: | ||
| 847 | registers[perform_math_static->reg_index] += | ||
| 848 | static_cast<u64>(perform_math_static->value); | ||
| 849 | break; | ||
| 850 | case RegisterArithmeticType::Subtraction: | ||
| 851 | registers[perform_math_static->reg_index] -= | ||
| 852 | static_cast<u64>(perform_math_static->value); | ||
| 853 | break; | ||
| 854 | case RegisterArithmeticType::Multiplication: | ||
| 855 | registers[perform_math_static->reg_index] *= | ||
| 856 | static_cast<u64>(perform_math_static->value); | ||
| 857 | break; | ||
| 858 | case RegisterArithmeticType::LeftShift: | ||
| 859 | registers[perform_math_static->reg_index] <<= | ||
| 860 | static_cast<u64>(perform_math_static->value); | ||
| 861 | break; | ||
| 862 | case RegisterArithmeticType::RightShift: | ||
| 863 | registers[perform_math_static->reg_index] >>= | ||
| 864 | static_cast<u64>(perform_math_static->value); | ||
| 865 | break; | ||
| 866 | default: | ||
| 867 | // Do not handle extensions here. | ||
| 868 | break; | ||
| 869 | } | ||
| 870 | // Apply bit width. | ||
| 871 | switch (perform_math_static->bit_width) { | ||
| 872 | case 1: | ||
| 873 | registers[perform_math_static->reg_index] = | ||
| 874 | static_cast<u8>(registers[perform_math_static->reg_index]); | ||
| 875 | break; | ||
| 876 | case 2: | ||
| 877 | registers[perform_math_static->reg_index] = | ||
| 878 | static_cast<u16>(registers[perform_math_static->reg_index]); | ||
| 879 | break; | ||
| 880 | case 4: | ||
| 881 | registers[perform_math_static->reg_index] = | ||
| 882 | static_cast<u32>(registers[perform_math_static->reg_index]); | ||
| 883 | break; | ||
| 884 | case 8: | ||
| 885 | registers[perform_math_static->reg_index] = | ||
| 886 | static_cast<u64>(registers[perform_math_static->reg_index]); | ||
| 887 | break; | ||
| 888 | } | ||
| 889 | } else if (auto begin_keypress_cond = | ||
| 890 | std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) { | ||
| 891 | // Check for keypress. | ||
| 892 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { | ||
| 893 | // Keys not pressed. Skip conditional block. | ||
| 894 | SkipConditionalBlock(); | ||
| 895 | } | ||
| 896 | } else if (auto perform_math_reg = | ||
| 897 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { | ||
| 898 | const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index]; | ||
| 899 | const u64 operand_2_value = | ||
| 900 | perform_math_reg->has_immediate | ||
| 901 | ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width) | ||
| 902 | : registers[perform_math_reg->src_reg_2_index]; | ||
| 903 | |||
| 904 | u64 res_val = 0; | ||
| 905 | // Do requested math. | ||
| 906 | switch (perform_math_reg->math_type) { | ||
| 907 | case RegisterArithmeticType::Addition: | ||
| 908 | res_val = operand_1_value + operand_2_value; | ||
| 909 | break; | ||
| 910 | case RegisterArithmeticType::Subtraction: | ||
| 911 | res_val = operand_1_value - operand_2_value; | ||
| 912 | break; | ||
| 913 | case RegisterArithmeticType::Multiplication: | ||
| 914 | res_val = operand_1_value * operand_2_value; | ||
| 915 | break; | ||
| 916 | case RegisterArithmeticType::LeftShift: | ||
| 917 | res_val = operand_1_value << operand_2_value; | ||
| 918 | break; | ||
| 919 | case RegisterArithmeticType::RightShift: | ||
| 920 | res_val = operand_1_value >> operand_2_value; | ||
| 921 | break; | ||
| 922 | case RegisterArithmeticType::LogicalAnd: | ||
| 923 | res_val = operand_1_value & operand_2_value; | ||
| 924 | break; | ||
| 925 | case RegisterArithmeticType::LogicalOr: | ||
| 926 | res_val = operand_1_value | operand_2_value; | ||
| 927 | break; | ||
| 928 | case RegisterArithmeticType::LogicalNot: | ||
| 929 | res_val = ~operand_1_value; | ||
| 930 | break; | ||
| 931 | case RegisterArithmeticType::LogicalXor: | ||
| 932 | res_val = operand_1_value ^ operand_2_value; | ||
| 933 | break; | ||
| 934 | case RegisterArithmeticType::None: | ||
| 935 | res_val = operand_1_value; | ||
| 936 | break; | ||
| 937 | } | ||
| 938 | |||
| 939 | // Apply bit width. | ||
| 940 | switch (perform_math_reg->bit_width) { | ||
| 941 | case 1: | ||
| 942 | res_val = static_cast<u8>(res_val); | ||
| 943 | break; | ||
| 944 | case 2: | ||
| 945 | res_val = static_cast<u16>(res_val); | ||
| 946 | break; | ||
| 947 | case 4: | ||
| 948 | res_val = static_cast<u32>(res_val); | ||
| 949 | break; | ||
| 950 | case 8: | ||
| 951 | res_val = static_cast<u64>(res_val); | ||
| 952 | break; | ||
| 953 | } | ||
| 954 | |||
| 955 | // Save to register. | ||
| 956 | registers[perform_math_reg->dst_reg_index] = res_val; | ||
| 957 | } else if (auto str_register = | ||
| 958 | std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) { | ||
| 959 | // Calculate address. | ||
| 960 | u64 dst_value = registers[str_register->str_reg_index]; | ||
| 961 | u64 dst_address = registers[str_register->addr_reg_index]; | ||
| 962 | switch (str_register->ofs_type) { | ||
| 963 | case StoreRegisterOffsetType::None: | ||
| 964 | // Nothing more to do | ||
| 965 | break; | ||
| 966 | case StoreRegisterOffsetType::Reg: | ||
| 967 | dst_address += registers[str_register->ofs_reg_index]; | ||
| 968 | break; | ||
| 969 | case StoreRegisterOffsetType::Imm: | ||
| 970 | dst_address += str_register->rel_address; | ||
| 971 | break; | ||
| 972 | case StoreRegisterOffsetType::MemReg: | ||
| 973 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, | ||
| 974 | registers[str_register->addr_reg_index]); | ||
| 975 | break; | ||
| 976 | case StoreRegisterOffsetType::MemImm: | ||
| 977 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, | ||
| 978 | str_register->rel_address); | ||
| 979 | break; | ||
| 980 | case StoreRegisterOffsetType::MemImmReg: | ||
| 981 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, | ||
| 982 | registers[str_register->addr_reg_index] + | ||
| 983 | str_register->rel_address); | ||
| 984 | break; | ||
| 985 | } | ||
| 986 | |||
| 987 | // Write value to memory. Write only on valid bitwidth. | ||
| 988 | switch (str_register->bit_width) { | ||
| 989 | case 1: | ||
| 990 | case 2: | ||
| 991 | case 4: | ||
| 992 | case 8: | ||
| 993 | callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); | ||
| 994 | break; | ||
| 995 | } | ||
| 996 | |||
| 997 | // Increment register if relevant. | ||
| 998 | if (str_register->increment_reg) { | ||
| 999 | registers[str_register->addr_reg_index] += str_register->bit_width; | ||
| 1000 | } | ||
| 1001 | } else if (auto begin_reg_cond = | ||
| 1002 | std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) { | ||
| 1003 | // Get value from register. | ||
| 1004 | u64 src_value = 0; | ||
| 1005 | switch (begin_reg_cond->bit_width) { | ||
| 1006 | case 1: | ||
| 1007 | src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul); | ||
| 1008 | break; | ||
| 1009 | case 2: | ||
| 1010 | src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul); | ||
| 1011 | break; | ||
| 1012 | case 4: | ||
| 1013 | src_value = | ||
| 1014 | static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul); | ||
| 1015 | break; | ||
| 1016 | case 8: | ||
| 1017 | src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] & | ||
| 1018 | 0xFFFFFFFFFFFFFFFFul); | ||
| 1019 | break; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | // Read value from memory. | ||
| 1023 | u64 cond_value = 0; | ||
| 1024 | if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) { | ||
| 1025 | cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width); | ||
| 1026 | } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) { | ||
| 1027 | switch (begin_reg_cond->bit_width) { | ||
| 1028 | case 1: | ||
| 1029 | cond_value = | ||
| 1030 | static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul); | ||
| 1031 | break; | ||
| 1032 | case 2: | ||
| 1033 | cond_value = | ||
| 1034 | static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul); | ||
| 1035 | break; | ||
| 1036 | case 4: | ||
| 1037 | cond_value = | ||
| 1038 | static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul); | ||
| 1039 | break; | ||
| 1040 | case 8: | ||
| 1041 | cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] & | ||
| 1042 | 0xFFFFFFFFFFFFFFFFul); | ||
| 1043 | break; | ||
| 1044 | } | ||
| 1045 | } else { | ||
| 1046 | u64 cond_address = 0; | ||
| 1047 | switch (begin_reg_cond->comp_type) { | ||
| 1048 | case CompareRegisterValueType::MemoryRelAddr: | ||
| 1049 | cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, | ||
| 1050 | begin_reg_cond->rel_address); | ||
| 1051 | break; | ||
| 1052 | case CompareRegisterValueType::MemoryOfsReg: | ||
| 1053 | cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, | ||
| 1054 | registers[begin_reg_cond->ofs_reg_index]); | ||
| 1055 | break; | ||
| 1056 | case CompareRegisterValueType::RegisterRelAddr: | ||
| 1057 | cond_address = | ||
| 1058 | registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address; | ||
| 1059 | break; | ||
| 1060 | case CompareRegisterValueType::RegisterOfsReg: | ||
| 1061 | cond_address = registers[begin_reg_cond->addr_reg_index] + | ||
| 1062 | registers[begin_reg_cond->ofs_reg_index]; | ||
| 1063 | break; | ||
| 1064 | default: | ||
| 1065 | break; | ||
| 1066 | } | ||
| 1067 | switch (begin_reg_cond->bit_width) { | ||
| 1068 | case 1: | ||
| 1069 | case 2: | ||
| 1070 | case 4: | ||
| 1071 | case 8: | ||
| 1072 | callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); | ||
| 1073 | break; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | // Check against condition. | ||
| 1078 | bool cond_met = false; | ||
| 1079 | switch (begin_reg_cond->cond_type) { | ||
| 1080 | case ConditionalComparisonType::GT: | ||
| 1081 | cond_met = src_value > cond_value; | ||
| 1082 | break; | ||
| 1083 | case ConditionalComparisonType::GE: | ||
| 1084 | cond_met = src_value >= cond_value; | ||
| 1085 | break; | ||
| 1086 | case ConditionalComparisonType::LT: | ||
| 1087 | cond_met = src_value < cond_value; | ||
| 1088 | break; | ||
| 1089 | case ConditionalComparisonType::LE: | ||
| 1090 | cond_met = src_value <= cond_value; | ||
| 1091 | break; | ||
| 1092 | case ConditionalComparisonType::EQ: | ||
| 1093 | cond_met = src_value == cond_value; | ||
| 1094 | break; | ||
| 1095 | case ConditionalComparisonType::NE: | ||
| 1096 | cond_met = src_value != cond_value; | ||
| 1097 | break; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | // Skip conditional block if condition not met. | ||
| 1101 | if (!cond_met) { | ||
| 1102 | SkipConditionalBlock(); | ||
| 1103 | } | ||
| 1104 | } else if (auto save_restore_reg = | ||
| 1105 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { | ||
| 1106 | // Save or restore a register. | ||
| 1107 | switch (save_restore_reg->op_type) { | ||
| 1108 | case SaveRestoreRegisterOpType::ClearRegs: | ||
| 1109 | registers[save_restore_reg->dst_index] = 0ul; | ||
| 1110 | break; | ||
| 1111 | case SaveRestoreRegisterOpType::ClearSaved: | ||
| 1112 | saved_values[save_restore_reg->dst_index] = 0ul; | ||
| 1113 | break; | ||
| 1114 | case SaveRestoreRegisterOpType::Save: | ||
| 1115 | saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index]; | ||
| 1116 | break; | ||
| 1117 | case SaveRestoreRegisterOpType::Restore: | ||
| 1118 | default: | ||
| 1119 | registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index]; | ||
| 1120 | break; | ||
| 1121 | } | ||
| 1122 | } else if (auto save_restore_regmask = | ||
| 1123 | std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) { | ||
| 1124 | // Save or restore register mask. | ||
| 1125 | u64* src; | ||
| 1126 | u64* dst; | ||
| 1127 | switch (save_restore_regmask->op_type) { | ||
| 1128 | case SaveRestoreRegisterOpType::ClearSaved: | ||
| 1129 | case SaveRestoreRegisterOpType::Save: | ||
| 1130 | src = registers.data(); | ||
| 1131 | dst = saved_values.data(); | ||
| 1132 | break; | ||
| 1133 | case SaveRestoreRegisterOpType::ClearRegs: | ||
| 1134 | case SaveRestoreRegisterOpType::Restore: | ||
| 1135 | default: | ||
| 1136 | src = registers.data(); | ||
| 1137 | dst = saved_values.data(); | ||
| 1138 | break; | ||
| 1139 | } | ||
| 1140 | for (std::size_t i = 0; i < NumRegisters; i++) { | ||
| 1141 | if (save_restore_regmask->should_operate[i]) { | ||
| 1142 | switch (save_restore_regmask->op_type) { | ||
| 1143 | case SaveRestoreRegisterOpType::ClearSaved: | ||
| 1144 | case SaveRestoreRegisterOpType::ClearRegs: | ||
| 1145 | dst[i] = 0ul; | ||
| 1146 | break; | ||
| 1147 | case SaveRestoreRegisterOpType::Save: | ||
| 1148 | case SaveRestoreRegisterOpType::Restore: | ||
| 1149 | default: | ||
| 1150 | dst[i] = src[i]; | ||
| 1151 | break; | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | } | ||
| 1155 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { | ||
| 1156 | // Read value from memory. | ||
| 1157 | u64 log_value = 0; | ||
| 1158 | if (debug_log->val_type == DebugLogValueType::RegisterValue) { | ||
| 1159 | switch (debug_log->bit_width) { | ||
| 1160 | case 1: | ||
| 1161 | log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul); | ||
| 1162 | break; | ||
| 1163 | case 2: | ||
| 1164 | log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul); | ||
| 1165 | break; | ||
| 1166 | case 4: | ||
| 1167 | log_value = | ||
| 1168 | static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul); | ||
| 1169 | break; | ||
| 1170 | case 8: | ||
| 1171 | log_value = static_cast<u64>(registers[debug_log->val_reg_index] & | ||
| 1172 | 0xFFFFFFFFFFFFFFFFul); | ||
| 1173 | break; | ||
| 1174 | } | ||
| 1175 | } else { | ||
| 1176 | u64 val_address = 0; | ||
| 1177 | switch (debug_log->val_type) { | ||
| 1178 | case DebugLogValueType::MemoryRelAddr: | ||
| 1179 | val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, | ||
| 1180 | debug_log->rel_address); | ||
| 1181 | break; | ||
| 1182 | case DebugLogValueType::MemoryOfsReg: | ||
| 1183 | val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, | ||
| 1184 | registers[debug_log->ofs_reg_index]); | ||
| 1185 | break; | ||
| 1186 | case DebugLogValueType::RegisterRelAddr: | ||
| 1187 | val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address; | ||
| 1188 | break; | ||
| 1189 | case DebugLogValueType::RegisterOfsReg: | ||
| 1190 | val_address = | ||
| 1191 | registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index]; | ||
| 1192 | break; | ||
| 1193 | default: | ||
| 1194 | break; | ||
| 1195 | } | ||
| 1196 | switch (debug_log->bit_width) { | ||
| 1197 | case 1: | ||
| 1198 | case 2: | ||
| 1199 | case 4: | ||
| 1200 | case 8: | ||
| 1201 | callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); | ||
| 1202 | break; | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | // Log value. | ||
| 1207 | DebugLog(debug_log->log_id, log_value); | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | } // namespace Memory | ||
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h new file mode 100644 index 000000000..c36212cf1 --- /dev/null +++ b/src/core/memory/dmnt_cheat_vm.h | |||
| @@ -0,0 +1,321 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2018-2019 Atmosphère-NX | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Adapted by DarkLordZach for use/interaction with yuzu | ||
| 19 | * | ||
| 20 | * Modifications Copyright 2019 yuzu emulator team | ||
| 21 | * Licensed under GPLv2 or any later version | ||
| 22 | * Refer to the license.txt file included. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #pragma once | ||
| 26 | |||
| 27 | #include <variant> | ||
| 28 | #include <vector> | ||
| 29 | #include <fmt/printf.h> | ||
| 30 | #include "common/common_types.h" | ||
| 31 | #include "core/memory/dmnt_cheat_types.h" | ||
| 32 | |||
| 33 | namespace Memory { | ||
| 34 | |||
| 35 | enum class CheatVmOpcodeType : u32 { | ||
| 36 | StoreStatic = 0, | ||
| 37 | BeginConditionalBlock = 1, | ||
| 38 | EndConditionalBlock = 2, | ||
| 39 | ControlLoop = 3, | ||
| 40 | LoadRegisterStatic = 4, | ||
| 41 | LoadRegisterMemory = 5, | ||
| 42 | StoreStaticToAddress = 6, | ||
| 43 | PerformArithmeticStatic = 7, | ||
| 44 | BeginKeypressConditionalBlock = 8, | ||
| 45 | |||
| 46 | // These are not implemented by Gateway's VM. | ||
| 47 | PerformArithmeticRegister = 9, | ||
| 48 | StoreRegisterToAddress = 10, | ||
| 49 | Reserved11 = 11, | ||
| 50 | |||
| 51 | // This is a meta entry, and not a real opcode. | ||
| 52 | // This is to facilitate multi-nybble instruction decoding. | ||
| 53 | ExtendedWidth = 12, | ||
| 54 | |||
| 55 | // Extended width opcodes. | ||
| 56 | BeginRegisterConditionalBlock = 0xC0, | ||
| 57 | SaveRestoreRegister = 0xC1, | ||
| 58 | SaveRestoreRegisterMask = 0xC2, | ||
| 59 | |||
| 60 | // This is a meta entry, and not a real opcode. | ||
| 61 | // This is to facilitate multi-nybble instruction decoding. | ||
| 62 | DoubleExtendedWidth = 0xF0, | ||
| 63 | |||
| 64 | // Double-extended width opcodes. | ||
| 65 | DebugLog = 0xFFF, | ||
| 66 | }; | ||
| 67 | |||
| 68 | enum class MemoryAccessType : u32 { | ||
| 69 | MainNso = 0, | ||
| 70 | Heap = 1, | ||
| 71 | }; | ||
| 72 | |||
| 73 | enum class ConditionalComparisonType : u32 { | ||
| 74 | GT = 1, | ||
| 75 | GE = 2, | ||
| 76 | LT = 3, | ||
| 77 | LE = 4, | ||
| 78 | EQ = 5, | ||
| 79 | NE = 6, | ||
| 80 | }; | ||
| 81 | |||
| 82 | enum class RegisterArithmeticType : u32 { | ||
| 83 | Addition = 0, | ||
| 84 | Subtraction = 1, | ||
| 85 | Multiplication = 2, | ||
| 86 | LeftShift = 3, | ||
| 87 | RightShift = 4, | ||
| 88 | |||
| 89 | // These are not supported by Gateway's VM. | ||
| 90 | LogicalAnd = 5, | ||
| 91 | LogicalOr = 6, | ||
| 92 | LogicalNot = 7, | ||
| 93 | LogicalXor = 8, | ||
| 94 | |||
| 95 | None = 9, | ||
| 96 | }; | ||
| 97 | |||
| 98 | enum class StoreRegisterOffsetType : u32 { | ||
| 99 | None = 0, | ||
| 100 | Reg = 1, | ||
| 101 | Imm = 2, | ||
| 102 | MemReg = 3, | ||
| 103 | MemImm = 4, | ||
| 104 | MemImmReg = 5, | ||
| 105 | }; | ||
| 106 | |||
| 107 | enum class CompareRegisterValueType : u32 { | ||
| 108 | MemoryRelAddr = 0, | ||
| 109 | MemoryOfsReg = 1, | ||
| 110 | RegisterRelAddr = 2, | ||
| 111 | RegisterOfsReg = 3, | ||
| 112 | StaticValue = 4, | ||
| 113 | OtherRegister = 5, | ||
| 114 | }; | ||
| 115 | |||
| 116 | enum class SaveRestoreRegisterOpType : u32 { | ||
| 117 | Restore = 0, | ||
| 118 | Save = 1, | ||
| 119 | ClearSaved = 2, | ||
| 120 | ClearRegs = 3, | ||
| 121 | }; | ||
| 122 | |||
| 123 | enum class DebugLogValueType : u32 { | ||
| 124 | MemoryRelAddr = 0, | ||
| 125 | MemoryOfsReg = 1, | ||
| 126 | RegisterRelAddr = 2, | ||
| 127 | RegisterOfsReg = 3, | ||
| 128 | RegisterValue = 4, | ||
| 129 | }; | ||
| 130 | |||
| 131 | union VmInt { | ||
| 132 | u8 bit8; | ||
| 133 | u16 bit16; | ||
| 134 | u32 bit32; | ||
| 135 | u64 bit64; | ||
| 136 | }; | ||
| 137 | |||
| 138 | struct StoreStaticOpcode { | ||
| 139 | u32 bit_width{}; | ||
| 140 | MemoryAccessType mem_type{}; | ||
| 141 | u32 offset_register{}; | ||
| 142 | u64 rel_address{}; | ||
| 143 | VmInt value{}; | ||
| 144 | }; | ||
| 145 | |||
| 146 | struct BeginConditionalOpcode { | ||
| 147 | u32 bit_width{}; | ||
| 148 | MemoryAccessType mem_type{}; | ||
| 149 | ConditionalComparisonType cond_type{}; | ||
| 150 | u64 rel_address{}; | ||
| 151 | VmInt value{}; | ||
| 152 | }; | ||
| 153 | |||
| 154 | struct EndConditionalOpcode {}; | ||
| 155 | |||
| 156 | struct ControlLoopOpcode { | ||
| 157 | bool start_loop{}; | ||
| 158 | u32 reg_index{}; | ||
| 159 | u32 num_iters{}; | ||
| 160 | }; | ||
| 161 | |||
| 162 | struct LoadRegisterStaticOpcode { | ||
| 163 | u32 reg_index{}; | ||
| 164 | u64 value{}; | ||
| 165 | }; | ||
| 166 | |||
| 167 | struct LoadRegisterMemoryOpcode { | ||
| 168 | u32 bit_width{}; | ||
| 169 | MemoryAccessType mem_type{}; | ||
| 170 | u32 reg_index{}; | ||
| 171 | bool load_from_reg{}; | ||
| 172 | u64 rel_address{}; | ||
| 173 | }; | ||
| 174 | |||
| 175 | struct StoreStaticToAddressOpcode { | ||
| 176 | u32 bit_width{}; | ||
| 177 | u32 reg_index{}; | ||
| 178 | bool increment_reg{}; | ||
| 179 | bool add_offset_reg{}; | ||
| 180 | u32 offset_reg_index{}; | ||
| 181 | u64 value{}; | ||
| 182 | }; | ||
| 183 | |||
| 184 | struct PerformArithmeticStaticOpcode { | ||
| 185 | u32 bit_width{}; | ||
| 186 | u32 reg_index{}; | ||
| 187 | RegisterArithmeticType math_type{}; | ||
| 188 | u32 value{}; | ||
| 189 | }; | ||
| 190 | |||
| 191 | struct BeginKeypressConditionalOpcode { | ||
| 192 | u32 key_mask{}; | ||
| 193 | }; | ||
| 194 | |||
| 195 | struct PerformArithmeticRegisterOpcode { | ||
| 196 | u32 bit_width{}; | ||
| 197 | RegisterArithmeticType math_type{}; | ||
| 198 | u32 dst_reg_index{}; | ||
| 199 | u32 src_reg_1_index{}; | ||
| 200 | u32 src_reg_2_index{}; | ||
| 201 | bool has_immediate{}; | ||
| 202 | VmInt value{}; | ||
| 203 | }; | ||
| 204 | |||
| 205 | struct StoreRegisterToAddressOpcode { | ||
| 206 | u32 bit_width{}; | ||
| 207 | u32 str_reg_index{}; | ||
| 208 | u32 addr_reg_index{}; | ||
| 209 | bool increment_reg{}; | ||
| 210 | StoreRegisterOffsetType ofs_type{}; | ||
| 211 | MemoryAccessType mem_type{}; | ||
| 212 | u32 ofs_reg_index{}; | ||
| 213 | u64 rel_address{}; | ||
| 214 | }; | ||
| 215 | |||
| 216 | struct BeginRegisterConditionalOpcode { | ||
| 217 | u32 bit_width{}; | ||
| 218 | ConditionalComparisonType cond_type{}; | ||
| 219 | u32 val_reg_index{}; | ||
| 220 | CompareRegisterValueType comp_type{}; | ||
| 221 | MemoryAccessType mem_type{}; | ||
| 222 | u32 addr_reg_index{}; | ||
| 223 | u32 other_reg_index{}; | ||
| 224 | u32 ofs_reg_index{}; | ||
| 225 | u64 rel_address{}; | ||
| 226 | VmInt value{}; | ||
| 227 | }; | ||
| 228 | |||
| 229 | struct SaveRestoreRegisterOpcode { | ||
| 230 | u32 dst_index{}; | ||
| 231 | u32 src_index{}; | ||
| 232 | SaveRestoreRegisterOpType op_type{}; | ||
| 233 | }; | ||
| 234 | |||
| 235 | struct SaveRestoreRegisterMaskOpcode { | ||
| 236 | SaveRestoreRegisterOpType op_type{}; | ||
| 237 | std::array<bool, 0x10> should_operate{}; | ||
| 238 | }; | ||
| 239 | |||
| 240 | struct DebugLogOpcode { | ||
| 241 | u32 bit_width{}; | ||
| 242 | u32 log_id{}; | ||
| 243 | DebugLogValueType val_type{}; | ||
| 244 | MemoryAccessType mem_type{}; | ||
| 245 | u32 addr_reg_index{}; | ||
| 246 | u32 val_reg_index{}; | ||
| 247 | u32 ofs_reg_index{}; | ||
| 248 | u64 rel_address{}; | ||
| 249 | }; | ||
| 250 | |||
| 251 | struct UnrecognizedInstruction { | ||
| 252 | CheatVmOpcodeType opcode{}; | ||
| 253 | }; | ||
| 254 | |||
| 255 | struct CheatVmOpcode { | ||
| 256 | bool begin_conditional_block{}; | ||
| 257 | std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, | ||
| 258 | LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, | ||
| 259 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, | ||
| 260 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, | ||
| 261 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, | ||
| 262 | SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction> | ||
| 263 | opcode{}; | ||
| 264 | }; | ||
| 265 | |||
| 266 | class DmntCheatVm { | ||
| 267 | public: | ||
| 268 | /// Helper Type for DmntCheatVm <=> yuzu Interface | ||
| 269 | class Callbacks { | ||
| 270 | public: | ||
| 271 | virtual ~Callbacks(); | ||
| 272 | |||
| 273 | virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; | ||
| 274 | virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; | ||
| 275 | |||
| 276 | virtual u64 HidKeysDown() = 0; | ||
| 277 | |||
| 278 | virtual void DebugLog(u8 id, u64 value) = 0; | ||
| 279 | virtual void CommandLog(std::string_view data) = 0; | ||
| 280 | }; | ||
| 281 | |||
| 282 | static constexpr std::size_t MaximumProgramOpcodeCount = 0x400; | ||
| 283 | static constexpr std::size_t NumRegisters = 0x10; | ||
| 284 | |||
| 285 | explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks); | ||
| 286 | ~DmntCheatVm(); | ||
| 287 | |||
| 288 | std::size_t GetProgramSize() const { | ||
| 289 | return this->num_opcodes; | ||
| 290 | } | ||
| 291 | |||
| 292 | bool LoadProgram(const std::vector<CheatEntry>& cheats); | ||
| 293 | void Execute(const CheatProcessMetadata& metadata); | ||
| 294 | |||
| 295 | private: | ||
| 296 | std::unique_ptr<Callbacks> callbacks; | ||
| 297 | |||
| 298 | std::size_t num_opcodes = 0; | ||
| 299 | std::size_t instruction_ptr = 0; | ||
| 300 | std::size_t condition_depth = 0; | ||
| 301 | bool decode_success = false; | ||
| 302 | std::array<u32, MaximumProgramOpcodeCount> program{}; | ||
| 303 | std::array<u64, NumRegisters> registers{}; | ||
| 304 | std::array<u64, NumRegisters> saved_values{}; | ||
| 305 | std::array<std::size_t, NumRegisters> loop_tops{}; | ||
| 306 | |||
| 307 | bool DecodeNextOpcode(CheatVmOpcode& out); | ||
| 308 | void SkipConditionalBlock(); | ||
| 309 | void ResetState(); | ||
| 310 | |||
| 311 | // For implementing the DebugLog opcode. | ||
| 312 | void DebugLog(u32 log_id, u64 value); | ||
| 313 | |||
| 314 | void LogOpcode(const CheatVmOpcode& opcode); | ||
| 315 | |||
| 316 | static u64 GetVmInt(VmInt value, u32 bit_width); | ||
| 317 | static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata, | ||
| 318 | MemoryAccessType mem_type, u64 rel_address); | ||
| 319 | }; | ||
| 320 | |||
| 321 | }; // namespace Memory | ||
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 4afd6c8a3..d2c69d1a0 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -4,8 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <chrono> | 6 | #include <chrono> |
| 7 | #include <iterator> | ||
| 7 | #include <mutex> | 8 | #include <mutex> |
| 9 | #include <numeric> | ||
| 10 | #include <sstream> | ||
| 8 | #include <thread> | 11 | #include <thread> |
| 12 | #include <fmt/chrono.h> | ||
| 13 | #include <fmt/format.h> | ||
| 14 | #include "common/file_util.h" | ||
| 9 | #include "common/math_util.h" | 15 | #include "common/math_util.h" |
| 10 | #include "core/perf_stats.h" | 16 | #include "core/perf_stats.h" |
| 11 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| @@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>; | |||
| 15 | using std::chrono::duration_cast; | 21 | using std::chrono::duration_cast; |
| 16 | using std::chrono::microseconds; | 22 | using std::chrono::microseconds; |
| 17 | 23 | ||
| 24 | // Purposefully ignore the first five frames, as there's a significant amount of overhead in | ||
| 25 | // booting that we shouldn't account for | ||
| 26 | constexpr std::size_t IgnoreFrames = 5; | ||
| 27 | |||
| 18 | namespace Core { | 28 | namespace Core { |
| 19 | 29 | ||
| 30 | PerfStats::PerfStats(u64 title_id) : title_id(title_id) {} | ||
| 31 | |||
| 32 | PerfStats::~PerfStats() { | ||
| 33 | if (!Settings::values.record_frame_times || title_id == 0) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | const std::time_t t = std::time(nullptr); | ||
| 38 | std::ostringstream stream; | ||
| 39 | std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, | ||
| 40 | std::ostream_iterator<double>(stream, "\n")); | ||
| 41 | const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); | ||
| 42 | // %F Date format expanded is "%Y-%m-%d" | ||
| 43 | const std::string filename = | ||
| 44 | fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); | ||
| 45 | FileUtil::IOFile file(filename, "w"); | ||
| 46 | file.WriteString(stream.str()); | ||
| 47 | } | ||
| 48 | |||
| 20 | void PerfStats::BeginSystemFrame() { | 49 | void PerfStats::BeginSystemFrame() { |
| 21 | std::lock_guard lock{object_mutex}; | 50 | std::lock_guard lock{object_mutex}; |
| 22 | 51 | ||
| @@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() { | |||
| 27 | std::lock_guard lock{object_mutex}; | 56 | std::lock_guard lock{object_mutex}; |
| 28 | 57 | ||
| 29 | auto frame_end = Clock::now(); | 58 | auto frame_end = Clock::now(); |
| 30 | accumulated_frametime += frame_end - frame_begin; | 59 | const auto frame_time = frame_end - frame_begin; |
| 60 | if (current_index < perf_history.size()) { | ||
| 61 | perf_history[current_index++] = | ||
| 62 | std::chrono::duration<double, std::milli>(frame_time).count(); | ||
| 63 | } | ||
| 64 | accumulated_frametime += frame_time; | ||
| 31 | system_frames += 1; | 65 | system_frames += 1; |
| 32 | 66 | ||
| 33 | previous_frame_length = frame_end - previous_frame_end; | 67 | previous_frame_length = frame_end - previous_frame_end; |
| @@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() { | |||
| 40 | game_frames += 1; | 74 | game_frames += 1; |
| 41 | } | 75 | } |
| 42 | 76 | ||
| 77 | double PerfStats::GetMeanFrametime() { | ||
| 78 | std::lock_guard lock{object_mutex}; | ||
| 79 | |||
| 80 | if (current_index <= IgnoreFrames) { | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, | ||
| 84 | perf_history.begin() + current_index, 0); | ||
| 85 | return sum / (current_index - IgnoreFrames); | ||
| 86 | } | ||
| 87 | |||
| 43 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { | 88 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { |
| 44 | std::lock_guard lock{object_mutex}; | 89 | std::lock_guard lock{object_mutex}; |
| 45 | 90 | ||
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 222ac1a63..d9a64f072 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h | |||
| @@ -4,7 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <chrono> | 8 | #include <chrono> |
| 9 | #include <cstddef> | ||
| 8 | #include <mutex> | 10 | #include <mutex> |
| 9 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 10 | 12 | ||
| @@ -27,6 +29,10 @@ struct PerfStatsResults { | |||
| 27 | */ | 29 | */ |
| 28 | class PerfStats { | 30 | class PerfStats { |
| 29 | public: | 31 | public: |
| 32 | explicit PerfStats(u64 title_id); | ||
| 33 | |||
| 34 | ~PerfStats(); | ||
| 35 | |||
| 30 | using Clock = std::chrono::high_resolution_clock; | 36 | using Clock = std::chrono::high_resolution_clock; |
| 31 | 37 | ||
| 32 | void BeginSystemFrame(); | 38 | void BeginSystemFrame(); |
| @@ -36,13 +42,26 @@ public: | |||
| 36 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); | 42 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); |
| 37 | 43 | ||
| 38 | /** | 44 | /** |
| 45 | * Returns the Arthimetic Mean of all frametime values stored in the performance history. | ||
| 46 | */ | ||
| 47 | double GetMeanFrametime(); | ||
| 48 | |||
| 49 | /** | ||
| 39 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is | 50 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is |
| 40 | * useful for scaling inputs or outputs moving between the two time domains. | 51 | * useful for scaling inputs or outputs moving between the two time domains. |
| 41 | */ | 52 | */ |
| 42 | double GetLastFrameTimeScale(); | 53 | double GetLastFrameTimeScale(); |
| 43 | 54 | ||
| 44 | private: | 55 | private: |
| 45 | std::mutex object_mutex; | 56 | std::mutex object_mutex{}; |
| 57 | |||
| 58 | /// Title ID for the game that is running. 0 if there is no game running yet | ||
| 59 | u64 title_id{0}; | ||
| 60 | /// Current index for writing to the perf_history array | ||
| 61 | std::size_t current_index{0}; | ||
| 62 | /// Stores an hour of historical frametime data useful for processing and tracking performance | ||
| 63 | /// regressions with code changes. | ||
| 64 | std::array<double, 216000> perf_history = {}; | ||
| 46 | 65 | ||
| 47 | /// Point when the cumulative counters were reset | 66 | /// Point when the cumulative counters were reset |
| 48 | Clock::time_point reset_point = Clock::now(); | 67 | Clock::time_point reset_point = Clock::now(); |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index cfe0771e2..9c657929e 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -304,8 +304,8 @@ void Reporter::SaveUnimplementedAppletReport( | |||
| 304 | SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); | 304 | SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp)); |
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, | 307 | void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, |
| 308 | std::optional<u128> user_id) const { | 308 | std::optional<u64> process_id, std::optional<u128> user_id) const { |
| 309 | if (!IsReportingEnabled()) { | 309 | if (!IsReportingEnabled()) { |
| 310 | return; | 310 | return; |
| 311 | } | 311 | } |
| @@ -321,7 +321,11 @@ void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vec | |||
| 321 | data_out.push_back(Common::HexToString(d)); | 321 | data_out.push_back(Common::HexToString(d)); |
| 322 | } | 322 | } |
| 323 | 323 | ||
| 324 | out["play_report_process_id"] = fmt::format("{:016X}", process_id); | 324 | if (process_id.has_value()) { |
| 325 | out["play_report_process_id"] = fmt::format("{:016X}", *process_id); | ||
| 326 | } | ||
| 327 | |||
| 328 | out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type)); | ||
| 325 | out["play_report_data"] = std::move(data_out); | 329 | out["play_report_data"] = std::move(data_out); |
| 326 | 330 | ||
| 327 | SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); | 331 | SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp)); |
diff --git a/src/core/reporter.h b/src/core/reporter.h index 44256de50..f08aa11fb 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h | |||
| @@ -46,8 +46,14 @@ public: | |||
| 46 | std::vector<std::vector<u8>> normal_channel, | 46 | std::vector<std::vector<u8>> normal_channel, |
| 47 | std::vector<std::vector<u8>> interactive_channel) const; | 47 | std::vector<std::vector<u8>> interactive_channel) const; |
| 48 | 48 | ||
| 49 | void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data, | 49 | enum class PlayReportType { |
| 50 | std::optional<u128> user_id = {}) const; | 50 | Old, |
| 51 | New, | ||
| 52 | System, | ||
| 53 | }; | ||
| 54 | |||
| 55 | void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data, | ||
| 56 | std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const; | ||
| 51 | 57 | ||
| 52 | void SaveErrorReport(u64 title_id, ResultCode result, | 58 | void SaveErrorReport(u64 title_id, ResultCode result, |
| 53 | std::optional<std::string> custom_text_main = {}, | 59 | std::optional<std::string> custom_text_main = {}, |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 0dd1632ac..7de3fd1e5 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/file_util.h" | ||
| 5 | #include "core/core.h" | 6 | #include "core/core.h" |
| 6 | #include "core/gdbstub/gdbstub.h" | 7 | #include "core/gdbstub/gdbstub.h" |
| 7 | #include "core/hle/service/hid/hid.h" | 8 | #include "core/hle/service/hid/hid.h" |
| @@ -97,8 +98,8 @@ void LogSettings() { | |||
| 97 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 98 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); |
| 98 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); | 99 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); |
| 99 | LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); | 100 | LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); |
| 100 | LogSetting("DataStorage_NandDir", Settings::values.nand_dir); | 101 | LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); |
| 101 | LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir); | 102 | LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); |
| 102 | LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); | 103 | LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); |
| 103 | LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); | 104 | LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); |
| 104 | LogSetting("Debugging_ProgramArgs", Settings::values.program_args); | 105 | LogSetting("Debugging_ProgramArgs", Settings::values.program_args); |
diff --git a/src/core/settings.h b/src/core/settings.h index 6638ce8f9..47bddfb30 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -346,6 +346,31 @@ struct TouchscreenInput { | |||
| 346 | u32 rotation_angle; | 346 | u32 rotation_angle; |
| 347 | }; | 347 | }; |
| 348 | 348 | ||
| 349 | enum class NANDTotalSize : u64 { | ||
| 350 | S29_1GB = 0x747C00000ULL, | ||
| 351 | }; | ||
| 352 | |||
| 353 | enum class NANDUserSize : u64 { | ||
| 354 | S26GB = 0x680000000ULL, | ||
| 355 | }; | ||
| 356 | |||
| 357 | enum class NANDSystemSize : u64 { | ||
| 358 | S2_5GB = 0xA0000000, | ||
| 359 | }; | ||
| 360 | |||
| 361 | enum class SDMCSize : u64 { | ||
| 362 | S1GB = 0x40000000, | ||
| 363 | S2GB = 0x80000000, | ||
| 364 | S4GB = 0x100000000ULL, | ||
| 365 | S8GB = 0x200000000ULL, | ||
| 366 | S16GB = 0x400000000ULL, | ||
| 367 | S32GB = 0x800000000ULL, | ||
| 368 | S64GB = 0x1000000000ULL, | ||
| 369 | S128GB = 0x2000000000ULL, | ||
| 370 | S256GB = 0x4000000000ULL, | ||
| 371 | S1TB = 0x10000000000ULL, | ||
| 372 | }; | ||
| 373 | |||
| 349 | struct Values { | 374 | struct Values { |
| 350 | // System | 375 | // System |
| 351 | bool use_docked_mode; | 376 | bool use_docked_mode; |
| @@ -382,8 +407,13 @@ struct Values { | |||
| 382 | 407 | ||
| 383 | // Data Storage | 408 | // Data Storage |
| 384 | bool use_virtual_sd; | 409 | bool use_virtual_sd; |
| 385 | std::string nand_dir; | 410 | bool gamecard_inserted; |
| 386 | std::string sdmc_dir; | 411 | bool gamecard_current_game; |
| 412 | std::string gamecard_path; | ||
| 413 | NANDTotalSize nand_total_size; | ||
| 414 | NANDSystemSize nand_system_size; | ||
| 415 | NANDUserSize nand_user_size; | ||
| 416 | SDMCSize sdmc_size; | ||
| 387 | 417 | ||
| 388 | // Renderer | 418 | // Renderer |
| 389 | float resolution_factor; | 419 | float resolution_factor; |
| @@ -409,6 +439,7 @@ struct Values { | |||
| 409 | float volume; | 439 | float volume; |
| 410 | 440 | ||
| 411 | // Debugging | 441 | // Debugging |
| 442 | bool record_frame_times; | ||
| 412 | bool use_gdbstub; | 443 | bool use_gdbstub; |
| 413 | u16 gdbstub_port; | 444 | u16 gdbstub_port; |
| 414 | std::string program_args; | 445 | std::string program_args; |
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 98a8b5337..7ff44f06d 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp | |||
| @@ -29,8 +29,8 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | void Fermi2D::HandleSurfaceCopy() { | 31 | void Fermi2D::HandleSurfaceCopy() { |
| 32 | LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", | 32 | LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}", |
| 33 | static_cast<u32>(regs.operation)); | 33 | static_cast<u32>(regs.operation)); |
| 34 | 34 | ||
| 35 | // TODO(Subv): Only raw copies are implemented. | 35 | // TODO(Subv): Only raw copies are implemented. |
| 36 | ASSERT(regs.operation == Operation::SrcCopy); | 36 | ASSERT(regs.operation == Operation::SrcCopy); |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index c7a3c85a0..b318aedb8 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -92,6 +92,10 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 92 | 92 | ||
| 93 | // Some games (like Super Mario Odyssey) assume that SRGB is enabled. | 93 | // Some games (like Super Mario Odyssey) assume that SRGB is enabled. |
| 94 | regs.framebuffer_srgb = 1; | 94 | regs.framebuffer_srgb = 1; |
| 95 | mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true; | ||
| 96 | mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true; | ||
| 97 | mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true; | ||
| 98 | mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; | ||
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | #define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name)) | 101 | #define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name)) |
| @@ -256,6 +260,9 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3 | |||
| 256 | 260 | ||
| 257 | // Execute the current macro. | 261 | // Execute the current macro. |
| 258 | macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); | 262 | macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); |
| 263 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | ||
| 264 | FlushMMEInlineDraw(); | ||
| 265 | } | ||
| 259 | } | 266 | } |
| 260 | 267 | ||
| 261 | void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | 268 | void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { |
| @@ -416,6 +423,97 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 416 | } | 423 | } |
| 417 | } | 424 | } |
| 418 | 425 | ||
| 426 | void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { | ||
| 427 | if (mme_draw.current_mode == MMEDrawMode::Undefined) { | ||
| 428 | if (mme_draw.gl_begin_consume) { | ||
| 429 | mme_draw.current_mode = expected_mode; | ||
| 430 | mme_draw.current_count = count; | ||
| 431 | mme_draw.instance_count = 1; | ||
| 432 | mme_draw.gl_begin_consume = false; | ||
| 433 | mme_draw.gl_end_count = 0; | ||
| 434 | } | ||
| 435 | return; | ||
| 436 | } else { | ||
| 437 | if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count && | ||
| 438 | mme_draw.instance_mode && mme_draw.gl_begin_consume) { | ||
| 439 | mme_draw.instance_count++; | ||
| 440 | mme_draw.gl_begin_consume = false; | ||
| 441 | return; | ||
| 442 | } else { | ||
| 443 | FlushMMEInlineDraw(); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | // Tail call in case it needs to retry. | ||
| 447 | StepInstance(expected_mode, count); | ||
| 448 | } | ||
| 449 | |||
| 450 | void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) { | ||
| 451 | const u32 method = method_call.method; | ||
| 452 | if (mme_inline[method]) { | ||
| 453 | regs.reg_array[method] = method_call.argument; | ||
| 454 | if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) || | ||
| 455 | method == MAXWELL3D_REG_INDEX(index_array.count)) { | ||
| 456 | const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count) | ||
| 457 | ? MMEDrawMode::Array | ||
| 458 | : MMEDrawMode::Indexed; | ||
| 459 | StepInstance(expected_mode, method_call.argument); | ||
| 460 | } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) { | ||
| 461 | mme_draw.instance_mode = | ||
| 462 | (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0); | ||
| 463 | mme_draw.gl_begin_consume = true; | ||
| 464 | } else { | ||
| 465 | mme_draw.gl_end_count++; | ||
| 466 | } | ||
| 467 | } else { | ||
| 468 | if (mme_draw.current_mode != MMEDrawMode::Undefined) { | ||
| 469 | FlushMMEInlineDraw(); | ||
| 470 | } | ||
| 471 | CallMethod(method_call); | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | void Maxwell3D::FlushMMEInlineDraw() { | ||
| 476 | LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), | ||
| 477 | regs.vertex_buffer.count); | ||
| 478 | ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); | ||
| 479 | ASSERT(mme_draw.instance_count == mme_draw.gl_end_count); | ||
| 480 | |||
| 481 | auto debug_context = system.GetGPUDebugContext(); | ||
| 482 | |||
| 483 | if (debug_context) { | ||
| 484 | debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr); | ||
| 485 | } | ||
| 486 | |||
| 487 | // Both instance configuration registers can not be set at the same time. | ||
| 488 | ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont, | ||
| 489 | "Illegal combination of instancing parameters"); | ||
| 490 | |||
| 491 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; | ||
| 492 | if (ShouldExecute()) { | ||
| 493 | rasterizer.DrawMultiBatch(is_indexed); | ||
| 494 | } | ||
| 495 | |||
| 496 | if (debug_context) { | ||
| 497 | debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); | ||
| 498 | } | ||
| 499 | |||
| 500 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if | ||
| 501 | // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - | ||
| 502 | // it's possible that it is incorrect and that there is some other register used to specify the | ||
| 503 | // drawing mode. | ||
| 504 | if (is_indexed) { | ||
| 505 | regs.index_array.count = 0; | ||
| 506 | } else { | ||
| 507 | regs.vertex_buffer.count = 0; | ||
| 508 | } | ||
| 509 | mme_draw.current_mode = MMEDrawMode::Undefined; | ||
| 510 | mme_draw.current_count = 0; | ||
| 511 | mme_draw.instance_count = 0; | ||
| 512 | mme_draw.instance_mode = false; | ||
| 513 | mme_draw.gl_begin_consume = false; | ||
| 514 | mme_draw.gl_end_count = 0; | ||
| 515 | } | ||
| 516 | |||
| 419 | void Maxwell3D::ProcessMacroUpload(u32 data) { | 517 | void Maxwell3D::ProcessMacroUpload(u32 data) { |
| 420 | ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), | 518 | ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), |
| 421 | "upload_address exceeded macro_memory size!"); | 519 | "upload_address exceeded macro_memory size!"); |
| @@ -541,7 +639,7 @@ void Maxwell3D::ProcessSyncPoint() { | |||
| 541 | } | 639 | } |
| 542 | 640 | ||
| 543 | void Maxwell3D::DrawArrays() { | 641 | void Maxwell3D::DrawArrays() { |
| 544 | LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), | 642 | LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()), |
| 545 | regs.vertex_buffer.count); | 643 | regs.vertex_buffer.count); |
| 546 | ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); | 644 | ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?"); |
| 547 | 645 | ||
| @@ -564,7 +662,9 @@ void Maxwell3D::DrawArrays() { | |||
| 564 | } | 662 | } |
| 565 | 663 | ||
| 566 | const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; | 664 | const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; |
| 567 | rasterizer.AccelerateDrawBatch(is_indexed); | 665 | if (ShouldExecute()) { |
| 666 | rasterizer.DrawBatch(is_indexed); | ||
| 667 | } | ||
| 568 | 668 | ||
| 569 | if (debug_context) { | 669 | if (debug_context) { |
| 570 | debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); | 670 | debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index e5ec90717..4c97759ed 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -811,8 +811,9 @@ public: | |||
| 811 | INSERT_PADDING_WORDS(0x21); | 811 | INSERT_PADDING_WORDS(0x21); |
| 812 | 812 | ||
| 813 | u32 vb_element_base; | 813 | u32 vb_element_base; |
| 814 | u32 vb_base_instance; | ||
| 814 | 815 | ||
| 815 | INSERT_PADDING_WORDS(0x36); | 816 | INSERT_PADDING_WORDS(0x35); |
| 816 | 817 | ||
| 817 | union { | 818 | union { |
| 818 | BitField<0, 1, u32> c0; | 819 | BitField<0, 1, u32> c0; |
| @@ -1238,6 +1239,11 @@ public: | |||
| 1238 | /// Write the value to the register identified by method. | 1239 | /// Write the value to the register identified by method. |
| 1239 | void CallMethod(const GPU::MethodCall& method_call); | 1240 | void CallMethod(const GPU::MethodCall& method_call); |
| 1240 | 1241 | ||
| 1242 | /// Write the value to the register identified by method. | ||
| 1243 | void CallMethodFromMME(const GPU::MethodCall& method_call); | ||
| 1244 | |||
| 1245 | void FlushMMEInlineDraw(); | ||
| 1246 | |||
| 1241 | /// Given a Texture Handle, returns the TSC and TIC entries. | 1247 | /// Given a Texture Handle, returns the TSC and TIC entries. |
| 1242 | Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, | 1248 | Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, |
| 1243 | std::size_t offset) const; | 1249 | std::size_t offset) const; |
| @@ -1263,6 +1269,21 @@ public: | |||
| 1263 | return execute_on; | 1269 | return execute_on; |
| 1264 | } | 1270 | } |
| 1265 | 1271 | ||
| 1272 | enum class MMEDrawMode : u32 { | ||
| 1273 | Undefined, | ||
| 1274 | Array, | ||
| 1275 | Indexed, | ||
| 1276 | }; | ||
| 1277 | |||
| 1278 | struct MMEDrawState { | ||
| 1279 | MMEDrawMode current_mode{MMEDrawMode::Undefined}; | ||
| 1280 | u32 current_count{}; | ||
| 1281 | u32 instance_count{}; | ||
| 1282 | bool instance_mode{}; | ||
| 1283 | bool gl_begin_consume{}; | ||
| 1284 | u32 gl_end_count{}; | ||
| 1285 | } mme_draw; | ||
| 1286 | |||
| 1266 | private: | 1287 | private: |
| 1267 | void InitializeRegisterDefaults(); | 1288 | void InitializeRegisterDefaults(); |
| 1268 | 1289 | ||
| @@ -1275,6 +1296,8 @@ private: | |||
| 1275 | /// Start offsets of each macro in macro_memory | 1296 | /// Start offsets of each macro in macro_memory |
| 1276 | std::array<u32, 0x80> macro_positions = {}; | 1297 | std::array<u32, 0x80> macro_positions = {}; |
| 1277 | 1298 | ||
| 1299 | std::array<bool, Regs::NUM_REGS> mme_inline{}; | ||
| 1300 | |||
| 1278 | /// Memory for macro code | 1301 | /// Memory for macro code |
| 1279 | MacroMemory macro_memory; | 1302 | MacroMemory macro_memory; |
| 1280 | 1303 | ||
| @@ -1346,6 +1369,9 @@ private: | |||
| 1346 | 1369 | ||
| 1347 | /// Handles a write to the VERTEX_END_GL register, triggering a draw. | 1370 | /// Handles a write to the VERTEX_END_GL register, triggering a draw. |
| 1348 | void DrawArrays(); | 1371 | void DrawArrays(); |
| 1372 | |||
| 1373 | // Handles a instance drawcall from MME | ||
| 1374 | void StepInstance(MMEDrawMode expected_mode, u32 count); | ||
| 1349 | }; | 1375 | }; |
| 1350 | 1376 | ||
| 1351 | #define ASSERT_REG_POSITION(field_name, position) \ | 1377 | #define ASSERT_REG_POSITION(field_name, position) \ |
| @@ -1402,6 +1428,7 @@ ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); | |||
| 1402 | ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); | 1428 | ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); |
| 1403 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); | 1429 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); |
| 1404 | ASSERT_REG_POSITION(vb_element_base, 0x50D); | 1430 | ASSERT_REG_POSITION(vb_element_base, 0x50D); |
| 1431 | ASSERT_REG_POSITION(vb_base_instance, 0x50E); | ||
| 1405 | ASSERT_REG_POSITION(clip_distance_enabled, 0x544); | 1432 | ASSERT_REG_POSITION(clip_distance_enabled, 0x544); |
| 1406 | ASSERT_REG_POSITION(point_size, 0x546); | 1433 | ASSERT_REG_POSITION(point_size, 0x546); |
| 1407 | ASSERT_REG_POSITION(zeta_enable, 0x54E); | 1434 | 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 { | |||
| 950 | } isetp; | 950 | } isetp; |
| 951 | 951 | ||
| 952 | union { | 952 | union { |
| 953 | BitField<48, 1, u64> is_signed; | ||
| 954 | BitField<49, 3, PredCondition> cond; | ||
| 955 | } icmp; | ||
| 956 | |||
| 957 | union { | ||
| 953 | BitField<0, 3, u64> pred0; | 958 | BitField<0, 3, u64> pred0; |
| 954 | BitField<3, 3, u64> pred3; | 959 | BitField<3, 3, u64> pred3; |
| 955 | BitField<12, 3, u64> pred12; | 960 | BitField<12, 3, u64> pred12; |
| @@ -1646,6 +1651,10 @@ public: | |||
| 1646 | SEL_C, | 1651 | SEL_C, |
| 1647 | SEL_R, | 1652 | SEL_R, |
| 1648 | SEL_IMM, | 1653 | SEL_IMM, |
| 1654 | ICMP_RC, | ||
| 1655 | ICMP_R, | ||
| 1656 | ICMP_CR, | ||
| 1657 | ICMP_IMM, | ||
| 1649 | MUFU, // Multi-Function Operator | 1658 | MUFU, // Multi-Function Operator |
| 1650 | RRO_C, // Range Reduction Operator | 1659 | RRO_C, // Range Reduction Operator |
| 1651 | RRO_R, | 1660 | RRO_R, |
| @@ -1912,6 +1921,10 @@ private: | |||
| 1912 | INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), | 1921 | INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), |
| 1913 | INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), | 1922 | INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), |
| 1914 | INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), | 1923 | INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), |
| 1924 | INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"), | ||
| 1925 | INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"), | ||
| 1926 | INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"), | ||
| 1927 | INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"), | ||
| 1915 | INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"), | 1928 | INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"), |
| 1916 | INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"), | 1929 | INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"), |
| 1917 | INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), | 1930 | INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 2c47541cb..76cfe8107 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -122,6 +122,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | |||
| 122 | case RenderTargetFormat::RGBA16_UINT: | 122 | case RenderTargetFormat::RGBA16_UINT: |
| 123 | case RenderTargetFormat::RGBA16_UNORM: | 123 | case RenderTargetFormat::RGBA16_UNORM: |
| 124 | case RenderTargetFormat::RGBA16_FLOAT: | 124 | case RenderTargetFormat::RGBA16_FLOAT: |
| 125 | case RenderTargetFormat::RGBX16_FLOAT: | ||
| 125 | case RenderTargetFormat::RG32_FLOAT: | 126 | case RenderTargetFormat::RG32_FLOAT: |
| 126 | case RenderTargetFormat::RG32_UINT: | 127 | case RenderTargetFormat::RG32_UINT: |
| 127 | return 8; | 128 | return 8; |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 78bc0601a..29fa8e95b 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -42,6 +42,7 @@ enum class RenderTargetFormat : u32 { | |||
| 42 | RGBA16_FLOAT = 0xCA, | 42 | RGBA16_FLOAT = 0xCA, |
| 43 | RG32_FLOAT = 0xCB, | 43 | RG32_FLOAT = 0xCB, |
| 44 | RG32_UINT = 0xCD, | 44 | RG32_UINT = 0xCD, |
| 45 | RGBX16_FLOAT = 0xCE, | ||
| 45 | BGRA8_UNORM = 0xCF, | 46 | BGRA8_UNORM = 0xCF, |
| 46 | BGRA8_SRGB = 0xD0, | 47 | BGRA8_SRGB = 0xD0, |
| 47 | RGB10_A2_UNORM = 0xD1, | 48 | RGB10_A2_UNORM = 0xD1, |
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 62afc0d11..dbaeac6db 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp | |||
| @@ -257,7 +257,7 @@ void MacroInterpreter::SetMethodAddress(u32 address) { | |||
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | void MacroInterpreter::Send(u32 value) { | 259 | void MacroInterpreter::Send(u32 value) { |
| 260 | maxwell3d.CallMethod({method_address.address, value}); | 260 | maxwell3d.CallMethodFromMME({method_address.address, value}); |
| 261 | // Increment the method address by the method increment. | 261 | // Increment the method address by the method increment. |
| 262 | method_address.address.Assign(method_address.address.Value() + | 262 | method_address.address.Assign(method_address.address.Value() + |
| 263 | method_address.increment.Value()); | 263 | method_address.increment.Value()); |
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index 084f85e67..ab71870ab 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp | |||
| @@ -83,6 +83,7 @@ static constexpr ConversionArray morton_to_linear_fns = { | |||
| 83 | MortonCopy<true, PixelFormat::RG8U>, | 83 | MortonCopy<true, PixelFormat::RG8U>, |
| 84 | MortonCopy<true, PixelFormat::RG8S>, | 84 | MortonCopy<true, PixelFormat::RG8S>, |
| 85 | MortonCopy<true, PixelFormat::RG32UI>, | 85 | MortonCopy<true, PixelFormat::RG32UI>, |
| 86 | MortonCopy<true, PixelFormat::RGBX16F>, | ||
| 86 | MortonCopy<true, PixelFormat::R32UI>, | 87 | MortonCopy<true, PixelFormat::R32UI>, |
| 87 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, | 88 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, |
| 88 | MortonCopy<true, PixelFormat::ASTC_2D_8X5>, | 89 | MortonCopy<true, PixelFormat::ASTC_2D_8X5>, |
| @@ -151,6 +152,7 @@ static constexpr ConversionArray linear_to_morton_fns = { | |||
| 151 | MortonCopy<false, PixelFormat::RG8U>, | 152 | MortonCopy<false, PixelFormat::RG8U>, |
| 152 | MortonCopy<false, PixelFormat::RG8S>, | 153 | MortonCopy<false, PixelFormat::RG8S>, |
| 153 | MortonCopy<false, PixelFormat::RG32UI>, | 154 | MortonCopy<false, PixelFormat::RG32UI>, |
| 155 | MortonCopy<false, PixelFormat::RGBX16F>, | ||
| 154 | MortonCopy<false, PixelFormat::R32UI>, | 156 | MortonCopy<false, PixelFormat::R32UI>, |
| 155 | nullptr, | 157 | nullptr, |
| 156 | nullptr, | 158 | nullptr, |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 6b3f2d50a..5b0eca9e2 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -29,7 +29,10 @@ public: | |||
| 29 | virtual ~RasterizerInterface() {} | 29 | virtual ~RasterizerInterface() {} |
| 30 | 30 | ||
| 31 | /// Draw the current batch of vertex arrays | 31 | /// Draw the current batch of vertex arrays |
| 32 | virtual void DrawArrays() = 0; | 32 | virtual bool DrawBatch(bool is_indexed) = 0; |
| 33 | |||
| 34 | /// Draw the current batch of multiple instances of vertex arrays | ||
| 35 | virtual bool DrawMultiBatch(bool is_indexed) = 0; | ||
| 33 | 36 | ||
| 34 | /// Clear the current framebuffer | 37 | /// Clear the current framebuffer |
| 35 | virtual void Clear() = 0; | 38 | virtual void Clear() = 0; |
| @@ -69,10 +72,6 @@ public: | |||
| 69 | return false; | 72 | return false; |
| 70 | } | 73 | } |
| 71 | 74 | ||
| 72 | virtual bool AccelerateDrawBatch(bool is_indexed) { | ||
| 73 | return false; | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Increase/decrease the number of object in pages touching the specified region | 75 | /// Increase/decrease the number of object in pages touching the specified region |
| 77 | virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} | 76 | virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} |
| 78 | 77 | ||
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp index 7c926bd48..a5d69d78d 100644 --- a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp | |||
| @@ -35,21 +35,16 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK | |||
| 35 | local_state.draw.draw_framebuffer = framebuffer.handle; | 35 | local_state.draw.draw_framebuffer = framebuffer.handle; |
| 36 | local_state.ApplyFramebufferState(); | 36 | local_state.ApplyFramebufferState(); |
| 37 | 37 | ||
| 38 | if (key.is_single_buffer) { | 38 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { |
| 39 | if (key.color_attachments[0] != GL_NONE && key.colors[0]) { | 39 | if (key.colors[index]) { |
| 40 | key.colors[0]->Attach(key.color_attachments[0], GL_DRAW_FRAMEBUFFER); | 40 | key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), |
| 41 | glDrawBuffer(key.color_attachments[0]); | 41 | GL_DRAW_FRAMEBUFFER); |
| 42 | } else { | ||
| 43 | glDrawBuffer(GL_NONE); | ||
| 44 | } | ||
| 45 | } else { | ||
| 46 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 47 | if (key.colors[index]) { | ||
| 48 | key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), | ||
| 49 | GL_DRAW_FRAMEBUFFER); | ||
| 50 | } | ||
| 51 | } | 42 | } |
| 43 | } | ||
| 44 | if (key.colors_count) { | ||
| 52 | glDrawBuffers(key.colors_count, key.color_attachments.data()); | 45 | glDrawBuffers(key.colors_count, key.color_attachments.data()); |
| 46 | } else { | ||
| 47 | glDrawBuffer(GL_NONE); | ||
| 53 | } | 48 | } |
| 54 | 49 | ||
| 55 | if (key.zeta) { | 50 | if (key.zeta) { |
| @@ -67,9 +62,9 @@ std::size_t FramebufferCacheKey::Hash() const { | |||
| 67 | } | 62 | } |
| 68 | 63 | ||
| 69 | bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const { | 64 | bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const { |
| 70 | return std::tie(is_single_buffer, stencil_enable, colors_count, color_attachments, colors, | 65 | return std::tie(stencil_enable, colors_count, color_attachments, colors, zeta) == |
| 71 | zeta) == std::tie(rhs.is_single_buffer, rhs.stencil_enable, rhs.colors_count, | 66 | std::tie(rhs.stencil_enable, rhs.colors_count, rhs.color_attachments, rhs.colors, |
| 72 | rhs.color_attachments, rhs.colors, rhs.zeta); | 67 | rhs.zeta); |
| 73 | } | 68 | } |
| 74 | 69 | ||
| 75 | } // namespace OpenGL | 70 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.h b/src/video_core/renderer_opengl/gl_framebuffer_cache.h index a3a996353..424344c48 100644 --- a/src/video_core/renderer_opengl/gl_framebuffer_cache.h +++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.h | |||
| @@ -19,7 +19,6 @@ | |||
| 19 | namespace OpenGL { | 19 | namespace OpenGL { |
| 20 | 20 | ||
| 21 | struct alignas(sizeof(u64)) FramebufferCacheKey { | 21 | struct alignas(sizeof(u64)) FramebufferCacheKey { |
| 22 | bool is_single_buffer = false; | ||
| 23 | bool stencil_enable = false; | 22 | bool stencil_enable = false; |
| 24 | u16 colors_count = 0; | 23 | u16 colors_count = 0; |
| 25 | 24 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4dd08bccb..6a17bed72 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -49,40 +49,6 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); | |||
| 49 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); | 49 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); |
| 50 | MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); | 50 | MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); |
| 51 | 51 | ||
| 52 | struct DrawParameters { | ||
| 53 | GLenum primitive_mode; | ||
| 54 | GLsizei count; | ||
| 55 | GLint current_instance; | ||
| 56 | bool use_indexed; | ||
| 57 | |||
| 58 | GLint vertex_first; | ||
| 59 | |||
| 60 | GLenum index_format; | ||
| 61 | GLint base_vertex; | ||
| 62 | GLintptr index_buffer_offset; | ||
| 63 | |||
| 64 | void DispatchDraw() const { | ||
| 65 | if (use_indexed) { | ||
| 66 | const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); | ||
| 67 | if (current_instance > 0) { | ||
| 68 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, | ||
| 69 | index_buffer_ptr, 1, base_vertex, | ||
| 70 | current_instance); | ||
| 71 | } else { | ||
| 72 | glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, | ||
| 73 | base_vertex); | ||
| 74 | } | ||
| 75 | } else { | ||
| 76 | if (current_instance > 0) { | ||
| 77 | glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1, | ||
| 78 | current_instance); | ||
| 79 | } else { | ||
| 80 | glDrawArrays(primitive_mode, vertex_first, count); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, | 52 | static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, |
| 87 | const GLShader::ConstBufferEntry& entry) { | 53 | const GLShader::ConstBufferEntry& entry) { |
| 88 | if (!entry.IsIndirect()) { | 54 | if (!entry.IsIndirect()) { |
| @@ -270,29 +236,6 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { | |||
| 270 | return offset; | 236 | return offset; |
| 271 | } | 237 | } |
| 272 | 238 | ||
| 273 | DrawParameters RasterizerOpenGL::SetupDraw(GLintptr index_buffer_offset) { | ||
| 274 | const auto& gpu = system.GPU().Maxwell3D(); | ||
| 275 | const auto& regs = gpu.regs; | ||
| 276 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | ||
| 277 | |||
| 278 | DrawParameters params{}; | ||
| 279 | params.current_instance = gpu.state.current_instance; | ||
| 280 | |||
| 281 | params.use_indexed = is_indexed; | ||
| 282 | params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 283 | |||
| 284 | if (is_indexed) { | ||
| 285 | params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 286 | params.count = regs.index_array.count; | ||
| 287 | params.index_buffer_offset = index_buffer_offset; | ||
| 288 | params.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 289 | } else { | ||
| 290 | params.count = regs.vertex_buffer.count; | ||
| 291 | params.vertex_first = regs.vertex_buffer.first; | ||
| 292 | } | ||
| 293 | return params; | ||
| 294 | } | ||
| 295 | |||
| 296 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | 239 | void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
| 297 | MICROPROFILE_SCOPE(OpenGL_Shader); | 240 | MICROPROFILE_SCOPE(OpenGL_Shader); |
| 298 | auto& gpu = system.GPU().Maxwell3D(); | 241 | auto& gpu = system.GPU().Maxwell3D(); |
| @@ -399,12 +342,6 @@ std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { | |||
| 399 | static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); | 342 | static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); |
| 400 | } | 343 | } |
| 401 | 344 | ||
| 402 | bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { | ||
| 403 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | ||
| 404 | DrawArrays(); | ||
| 405 | return true; | ||
| 406 | } | ||
| 407 | |||
| 408 | template <typename Map, typename Interval> | 345 | template <typename Map, typename Interval> |
| 409 | static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { | 346 | static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { |
| 410 | return boost::make_iterator_range(map.equal_range(interval)); | 347 | return boost::make_iterator_range(map.equal_range(interval)); |
| @@ -445,99 +382,51 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading, | |||
| 445 | shader_cache.LoadDiskCache(stop_loading, callback); | 382 | shader_cache.LoadDiskCache(stop_loading, callback); |
| 446 | } | 383 | } |
| 447 | 384 | ||
| 448 | std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( | 385 | void RasterizerOpenGL::ConfigureFramebuffers() { |
| 449 | OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents, | ||
| 450 | std::optional<std::size_t> single_color_target) { | ||
| 451 | MICROPROFILE_SCOPE(OpenGL_Framebuffer); | 386 | MICROPROFILE_SCOPE(OpenGL_Framebuffer); |
| 452 | auto& gpu = system.GPU().Maxwell3D(); | 387 | auto& gpu = system.GPU().Maxwell3D(); |
| 453 | const auto& regs = gpu.regs; | 388 | if (!gpu.dirty.render_settings) { |
| 454 | 389 | return; | |
| 455 | const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents, | ||
| 456 | single_color_target}; | ||
| 457 | if (fb_config_state == current_framebuffer_config_state && !gpu.dirty.render_settings) { | ||
| 458 | // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or | ||
| 459 | // single color targets). This is done because the guest registers may not change but the | ||
| 460 | // host framebuffer may contain different attachments | ||
| 461 | return current_depth_stencil_usage; | ||
| 462 | } | 390 | } |
| 463 | gpu.dirty.render_settings = false; | 391 | gpu.dirty.render_settings = false; |
| 464 | current_framebuffer_config_state = fb_config_state; | ||
| 465 | 392 | ||
| 466 | texture_cache.GuardRenderTargets(true); | 393 | texture_cache.GuardRenderTargets(true); |
| 467 | 394 | ||
| 468 | View depth_surface{}; | 395 | View depth_surface = texture_cache.GetDepthBufferSurface(true); |
| 469 | if (using_depth_fb) { | ||
| 470 | depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents); | ||
| 471 | } else { | ||
| 472 | texture_cache.SetEmptyDepthBuffer(); | ||
| 473 | } | ||
| 474 | 396 | ||
| 397 | const auto& regs = gpu.regs; | ||
| 398 | state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; | ||
| 475 | UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); | 399 | UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); |
| 476 | 400 | ||
| 477 | // Bind the framebuffer surfaces | 401 | // Bind the framebuffer surfaces |
| 478 | current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; | ||
| 479 | |||
| 480 | FramebufferCacheKey fbkey; | 402 | FramebufferCacheKey fbkey; |
| 403 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 404 | View color_surface{texture_cache.GetColorBufferSurface(index, true)}; | ||
| 481 | 405 | ||
| 482 | if (using_color_fb) { | 406 | if (color_surface) { |
| 483 | if (single_color_target) { | 407 | // Assume that a surface will be written to if it is used as a framebuffer, even |
| 484 | // Used when just a single color attachment is enabled, e.g. for clearing a color buffer | 408 | // if the shader doesn't actually write to it. |
| 485 | View color_surface{ | 409 | texture_cache.MarkColorBufferInUse(index); |
| 486 | texture_cache.GetColorBufferSurface(*single_color_target, preserve_contents)}; | ||
| 487 | |||
| 488 | if (color_surface) { | ||
| 489 | // Assume that a surface will be written to if it is used as a framebuffer, even if | ||
| 490 | // the shader doesn't actually write to it. | ||
| 491 | texture_cache.MarkColorBufferInUse(*single_color_target); | ||
| 492 | } | ||
| 493 | |||
| 494 | fbkey.is_single_buffer = true; | ||
| 495 | fbkey.color_attachments[0] = | ||
| 496 | GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target); | ||
| 497 | fbkey.colors[0] = color_surface; | ||
| 498 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 499 | if (index != *single_color_target) { | ||
| 500 | texture_cache.SetEmptyColorBuffer(index); | ||
| 501 | } | ||
| 502 | } | ||
| 503 | } else { | ||
| 504 | // Multiple color attachments are enabled | ||
| 505 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | ||
| 506 | View color_surface{texture_cache.GetColorBufferSurface(index, preserve_contents)}; | ||
| 507 | |||
| 508 | if (color_surface) { | ||
| 509 | // Assume that a surface will be written to if it is used as a framebuffer, even | ||
| 510 | // if the shader doesn't actually write to it. | ||
| 511 | texture_cache.MarkColorBufferInUse(index); | ||
| 512 | } | ||
| 513 | |||
| 514 | fbkey.color_attachments[index] = | ||
| 515 | GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); | ||
| 516 | fbkey.colors[index] = color_surface; | ||
| 517 | } | ||
| 518 | fbkey.is_single_buffer = false; | ||
| 519 | fbkey.colors_count = regs.rt_control.count; | ||
| 520 | } | 410 | } |
| 521 | } else { | 411 | |
| 522 | // No color attachments are enabled - leave them as zero | 412 | fbkey.color_attachments[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); |
| 523 | fbkey.is_single_buffer = true; | 413 | fbkey.colors[index] = std::move(color_surface); |
| 524 | } | 414 | } |
| 415 | fbkey.colors_count = regs.rt_control.count; | ||
| 525 | 416 | ||
| 526 | if (depth_surface) { | 417 | if (depth_surface) { |
| 527 | // Assume that a surface will be written to if it is used as a framebuffer, even if | 418 | // Assume that a surface will be written to if it is used as a framebuffer, even if |
| 528 | // the shader doesn't actually write to it. | 419 | // the shader doesn't actually write to it. |
| 529 | texture_cache.MarkDepthBufferInUse(); | 420 | texture_cache.MarkDepthBufferInUse(); |
| 530 | 421 | ||
| 531 | fbkey.zeta = depth_surface; | ||
| 532 | fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil; | 422 | fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil; |
| 423 | fbkey.zeta = std::move(depth_surface); | ||
| 533 | } | 424 | } |
| 534 | 425 | ||
| 535 | texture_cache.GuardRenderTargets(false); | 426 | texture_cache.GuardRenderTargets(false); |
| 536 | 427 | ||
| 537 | current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey); | 428 | state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey); |
| 538 | SyncViewport(current_state); | 429 | SyncViewport(state); |
| 539 | |||
| 540 | return current_depth_stencil_usage = {static_cast<bool>(depth_surface), fbkey.stencil_enable}; | ||
| 541 | } | 430 | } |
| 542 | 431 | ||
| 543 | void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, | 432 | void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, |
| @@ -688,17 +577,9 @@ void RasterizerOpenGL::Clear() { | |||
| 688 | } | 577 | } |
| 689 | } | 578 | } |
| 690 | 579 | ||
| 691 | void RasterizerOpenGL::DrawArrays() { | 580 | void RasterizerOpenGL::DrawPrelude() { |
| 692 | if (accelerate_draw == AccelDraw::Disabled) | ||
| 693 | return; | ||
| 694 | |||
| 695 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 696 | auto& gpu = system.GPU().Maxwell3D(); | 581 | auto& gpu = system.GPU().Maxwell3D(); |
| 697 | 582 | ||
| 698 | if (!gpu.ShouldExecute()) { | ||
| 699 | return; | ||
| 700 | } | ||
| 701 | |||
| 702 | SyncColorMask(); | 583 | SyncColorMask(); |
| 703 | SyncFragmentColorClampState(); | 584 | SyncFragmentColorClampState(); |
| 704 | SyncMultiSampleState(); | 585 | SyncMultiSampleState(); |
| @@ -743,10 +624,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 743 | // Upload vertex and index data. | 624 | // Upload vertex and index data. |
| 744 | SetupVertexBuffer(vao); | 625 | SetupVertexBuffer(vao); |
| 745 | SetupVertexInstances(vao); | 626 | SetupVertexInstances(vao); |
| 746 | const GLintptr index_buffer_offset = SetupIndexBuffer(); | 627 | index_buffer_offset = SetupIndexBuffer(); |
| 747 | |||
| 748 | // Setup draw parameters. It will automatically choose what glDraw* method to use. | ||
| 749 | const DrawParameters params = SetupDraw(index_buffer_offset); | ||
| 750 | 628 | ||
| 751 | // Prepare packed bindings. | 629 | // Prepare packed bindings. |
| 752 | bind_ubo_pushbuffer.Setup(0); | 630 | bind_ubo_pushbuffer.Setup(0); |
| @@ -754,10 +632,11 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 754 | 632 | ||
| 755 | // Setup shaders and their used resources. | 633 | // Setup shaders and their used resources. |
| 756 | texture_cache.GuardSamplers(true); | 634 | texture_cache.GuardSamplers(true); |
| 757 | SetupShaders(params.primitive_mode); | 635 | const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); |
| 636 | SetupShaders(primitive_mode); | ||
| 758 | texture_cache.GuardSamplers(false); | 637 | texture_cache.GuardSamplers(false); |
| 759 | 638 | ||
| 760 | ConfigureFramebuffers(state); | 639 | ConfigureFramebuffers(); |
| 761 | 640 | ||
| 762 | // Signal the buffer cache that we are not going to upload more things. | 641 | // Signal the buffer cache that we are not going to upload more things. |
| 763 | const bool invalidate = buffer_cache.Unmap(); | 642 | const bool invalidate = buffer_cache.Unmap(); |
| @@ -778,11 +657,107 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 778 | if (texture_cache.TextureBarrier()) { | 657 | if (texture_cache.TextureBarrier()) { |
| 779 | glTextureBarrier(); | 658 | glTextureBarrier(); |
| 780 | } | 659 | } |
| 660 | } | ||
| 661 | |||
| 662 | struct DrawParams { | ||
| 663 | bool is_indexed{}; | ||
| 664 | bool is_instanced{}; | ||
| 665 | GLenum primitive_mode{}; | ||
| 666 | GLint count{}; | ||
| 667 | GLint base_vertex{}; | ||
| 668 | |||
| 669 | // Indexed settings | ||
| 670 | GLenum index_format{}; | ||
| 671 | GLintptr index_buffer_offset{}; | ||
| 672 | |||
| 673 | // Instanced setting | ||
| 674 | GLint num_instances{}; | ||
| 675 | GLint base_instance{}; | ||
| 676 | |||
| 677 | void DispatchDraw() { | ||
| 678 | if (is_indexed) { | ||
| 679 | const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); | ||
| 680 | if (is_instanced) { | ||
| 681 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, | ||
| 682 | index_buffer_ptr, num_instances, | ||
| 683 | base_vertex, base_instance); | ||
| 684 | } else { | ||
| 685 | glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, | ||
| 686 | base_vertex); | ||
| 687 | } | ||
| 688 | } else { | ||
| 689 | if (is_instanced) { | ||
| 690 | glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, | ||
| 691 | base_instance); | ||
| 692 | } else { | ||
| 693 | glDrawArrays(primitive_mode, base_vertex, count); | ||
| 694 | } | ||
| 695 | } | ||
| 696 | } | ||
| 697 | }; | ||
| 781 | 698 | ||
| 782 | params.DispatchDraw(); | 699 | bool RasterizerOpenGL::DrawBatch(bool is_indexed) { |
| 700 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | ||
| 783 | 701 | ||
| 702 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 703 | |||
| 704 | DrawPrelude(); | ||
| 705 | |||
| 706 | auto& maxwell3d = system.GPU().Maxwell3D(); | ||
| 707 | const auto& regs = maxwell3d.regs; | ||
| 708 | const auto current_instance = maxwell3d.state.current_instance; | ||
| 709 | DrawParams draw_call{}; | ||
| 710 | draw_call.is_indexed = is_indexed; | ||
| 711 | draw_call.num_instances = static_cast<GLint>(1); | ||
| 712 | draw_call.base_instance = static_cast<GLint>(current_instance); | ||
| 713 | draw_call.is_instanced = current_instance > 0; | ||
| 714 | draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 715 | if (draw_call.is_indexed) { | ||
| 716 | draw_call.count = static_cast<GLint>(regs.index_array.count); | ||
| 717 | draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 718 | draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 719 | draw_call.index_buffer_offset = index_buffer_offset; | ||
| 720 | } else { | ||
| 721 | draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); | ||
| 722 | draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); | ||
| 723 | } | ||
| 724 | draw_call.DispatchDraw(); | ||
| 725 | |||
| 726 | maxwell3d.dirty.memory_general = false; | ||
| 784 | accelerate_draw = AccelDraw::Disabled; | 727 | accelerate_draw = AccelDraw::Disabled; |
| 785 | gpu.dirty.memory_general = false; | 728 | return true; |
| 729 | } | ||
| 730 | |||
| 731 | bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { | ||
| 732 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | ||
| 733 | |||
| 734 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 735 | |||
| 736 | DrawPrelude(); | ||
| 737 | |||
| 738 | auto& maxwell3d = system.GPU().Maxwell3D(); | ||
| 739 | const auto& regs = maxwell3d.regs; | ||
| 740 | const auto& draw_setup = maxwell3d.mme_draw; | ||
| 741 | DrawParams draw_call{}; | ||
| 742 | draw_call.is_indexed = is_indexed; | ||
| 743 | draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count); | ||
| 744 | draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance); | ||
| 745 | draw_call.is_instanced = draw_setup.instance_count > 1; | ||
| 746 | draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 747 | if (draw_call.is_indexed) { | ||
| 748 | draw_call.count = static_cast<GLint>(regs.index_array.count); | ||
| 749 | draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 750 | draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 751 | draw_call.index_buffer_offset = index_buffer_offset; | ||
| 752 | } else { | ||
| 753 | draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); | ||
| 754 | draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); | ||
| 755 | } | ||
| 756 | draw_call.DispatchDraw(); | ||
| 757 | |||
| 758 | maxwell3d.dirty.memory_general = false; | ||
| 759 | accelerate_draw = AccelDraw::Disabled; | ||
| 760 | return true; | ||
| 786 | } | 761 | } |
| 787 | 762 | ||
| 788 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | 763 | 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: | |||
| 57 | ScreenInfo& info); | 57 | ScreenInfo& info); |
| 58 | ~RasterizerOpenGL() override; | 58 | ~RasterizerOpenGL() override; |
| 59 | 59 | ||
| 60 | void DrawArrays() override; | 60 | bool DrawBatch(bool is_indexed) override; |
| 61 | bool DrawMultiBatch(bool is_indexed) override; | ||
| 61 | void Clear() override; | 62 | void Clear() override; |
| 62 | void DispatchCompute(GPUVAddr code_addr) override; | 63 | void DispatchCompute(GPUVAddr code_addr) override; |
| 63 | void FlushAll() override; | 64 | void FlushAll() override; |
| @@ -71,45 +72,13 @@ public: | |||
| 71 | const Tegra::Engines::Fermi2D::Config& copy_config) override; | 72 | const Tegra::Engines::Fermi2D::Config& copy_config) override; |
| 72 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, | 73 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, |
| 73 | u32 pixel_stride) override; | 74 | u32 pixel_stride) override; |
| 74 | bool AccelerateDrawBatch(bool is_indexed) override; | ||
| 75 | void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; | 75 | void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; |
| 76 | void LoadDiskResources(const std::atomic_bool& stop_loading, | 76 | void LoadDiskResources(const std::atomic_bool& stop_loading, |
| 77 | const VideoCore::DiskResourceLoadCallback& callback) override; | 77 | const VideoCore::DiskResourceLoadCallback& callback) override; |
| 78 | 78 | ||
| 79 | private: | 79 | private: |
| 80 | struct FramebufferConfigState { | 80 | /// Configures the color and depth framebuffer states. |
| 81 | bool using_color_fb{}; | 81 | void ConfigureFramebuffers(); |
| 82 | bool using_depth_fb{}; | ||
| 83 | bool preserve_contents{}; | ||
| 84 | std::optional<std::size_t> single_color_target; | ||
| 85 | |||
| 86 | bool operator==(const FramebufferConfigState& rhs) const { | ||
| 87 | return std::tie(using_color_fb, using_depth_fb, preserve_contents, | ||
| 88 | single_color_target) == std::tie(rhs.using_color_fb, rhs.using_depth_fb, | ||
| 89 | rhs.preserve_contents, | ||
| 90 | rhs.single_color_target); | ||
| 91 | } | ||
| 92 | bool operator!=(const FramebufferConfigState& rhs) const { | ||
| 93 | return !operator==(rhs); | ||
| 94 | } | ||
| 95 | }; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Configures the color and depth framebuffer states. | ||
| 99 | * | ||
| 100 | * @param current_state The current OpenGL state. | ||
| 101 | * @param using_color_fb If true, configure color framebuffers. | ||
| 102 | * @param using_depth_fb If true, configure the depth/stencil framebuffer. | ||
| 103 | * @param preserve_contents If true, tries to preserve data from a previously used | ||
| 104 | * framebuffer. | ||
| 105 | * @param single_color_target Specifies if a single color buffer target should be used. | ||
| 106 | * | ||
| 107 | * @returns If depth (first) or stencil (second) are being stored in the bound zeta texture | ||
| 108 | * (requires using_depth_fb to be true) | ||
| 109 | */ | ||
| 110 | std::pair<bool, bool> ConfigureFramebuffers( | ||
| 111 | OpenGLState& current_state, bool using_color_fb = true, bool using_depth_fb = true, | ||
| 112 | bool preserve_contents = true, std::optional<std::size_t> single_color_target = {}); | ||
| 113 | 82 | ||
| 114 | void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, | 83 | void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb, |
| 115 | bool using_depth_fb, bool using_stencil_fb); | 84 | bool using_depth_fb, bool using_stencil_fb); |
| @@ -136,6 +105,9 @@ private: | |||
| 136 | void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, | 105 | void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, |
| 137 | std::size_t size); | 106 | std::size_t size); |
| 138 | 107 | ||
| 108 | /// Syncs all the state, shaders, render targets and textures setting before a draw call. | ||
| 109 | void DrawPrelude(); | ||
| 110 | |||
| 139 | /// Configures the current textures to use for the draw command. Returns shaders texture buffer | 111 | /// Configures the current textures to use for the draw command. Returns shaders texture buffer |
| 140 | /// usage. | 112 | /// usage. |
| 141 | TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, | 113 | TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, |
| @@ -228,9 +200,6 @@ private: | |||
| 228 | OGLVertexArray> | 200 | OGLVertexArray> |
| 229 | vertex_array_cache; | 201 | vertex_array_cache; |
| 230 | 202 | ||
| 231 | FramebufferConfigState current_framebuffer_config_state; | ||
| 232 | std::pair<bool, bool> current_depth_stencil_usage{}; | ||
| 233 | |||
| 234 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 203 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 235 | OGLBufferCache buffer_cache; | 204 | OGLBufferCache buffer_cache; |
| 236 | 205 | ||
| @@ -250,7 +219,7 @@ private: | |||
| 250 | 219 | ||
| 251 | GLintptr SetupIndexBuffer(); | 220 | GLintptr SetupIndexBuffer(); |
| 252 | 221 | ||
| 253 | DrawParameters SetupDraw(GLintptr index_buffer_offset); | 222 | GLintptr index_buffer_offset; |
| 254 | 223 | ||
| 255 | void SetupShaders(GLenum primitive_mode); | 224 | void SetupShaders(GLenum primitive_mode); |
| 256 | 225 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 0f3a35f74..e6b36a0f2 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -461,6 +461,14 @@ private: | |||
| 461 | code.AddLine("float gl_PointSize;"); | 461 | code.AddLine("float gl_PointSize;"); |
| 462 | } | 462 | } |
| 463 | 463 | ||
| 464 | if (ir.UsesInstanceId()) { | ||
| 465 | code.AddLine("int gl_InstanceID;"); | ||
| 466 | } | ||
| 467 | |||
| 468 | if (ir.UsesVertexId()) { | ||
| 469 | code.AddLine("int gl_VertexID;"); | ||
| 470 | } | ||
| 471 | |||
| 464 | --code.scope; | 472 | --code.scope; |
| 465 | code.AddLine("}};"); | 473 | code.AddLine("}};"); |
| 466 | code.AddNewLine(); | 474 | code.AddNewLine(); |
| @@ -951,7 +959,7 @@ private: | |||
| 951 | switch (element) { | 959 | switch (element) { |
| 952 | case 2: | 960 | case 2: |
| 953 | // Config pack's first value is instance_id. | 961 | // Config pack's first value is instance_id. |
| 954 | return {"config_pack[0]", Type::Uint}; | 962 | return {"gl_InstanceID", Type::Int}; |
| 955 | case 3: | 963 | case 3: |
| 956 | return {"gl_VertexID", Type::Int}; | 964 | return {"gl_VertexID", Type::Int}; |
| 957 | } | 965 | } |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 4f135fe03..173b76c4e 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -97,6 +97,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format | |||
| 97 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U | 97 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U |
| 98 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S | 98 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S |
| 99 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI | 99 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI |
| 100 | {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBX16F | ||
| 100 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI | 101 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI |
| 101 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 | 102 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 |
| 102 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 | 103 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index ea77dd211..9ed738171 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -145,7 +145,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | |||
| 145 | case Tegra::Texture::TextureMipmapFilter::None: | 145 | case Tegra::Texture::TextureMipmapFilter::None: |
| 146 | return GL_LINEAR; | 146 | return GL_LINEAR; |
| 147 | case Tegra::Texture::TextureMipmapFilter::Nearest: | 147 | case Tegra::Texture::TextureMipmapFilter::Nearest: |
| 148 | return GL_NEAREST_MIPMAP_LINEAR; | 148 | return GL_LINEAR_MIPMAP_NEAREST; |
| 149 | case Tegra::Texture::TextureMipmapFilter::Linear: | 149 | case Tegra::Texture::TextureMipmapFilter::Linear: |
| 150 | return GL_LINEAR_MIPMAP_LINEAR; | 150 | return GL_LINEAR_MIPMAP_LINEAR; |
| 151 | } | 151 | } |
| @@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | |||
| 157 | case Tegra::Texture::TextureMipmapFilter::Nearest: | 157 | case Tegra::Texture::TextureMipmapFilter::Nearest: |
| 158 | return GL_NEAREST_MIPMAP_NEAREST; | 158 | return GL_NEAREST_MIPMAP_NEAREST; |
| 159 | case Tegra::Texture::TextureMipmapFilter::Linear: | 159 | case Tegra::Texture::TextureMipmapFilter::Linear: |
| 160 | return GL_LINEAR_MIPMAP_NEAREST; | 160 | return GL_NEAREST_MIPMAP_LINEAR; |
| 161 | } | 161 | } |
| 162 | } | 162 | } |
| 163 | } | 163 | } |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 0bbbf6851..3c5acda3e 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -143,6 +143,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex | |||
| 143 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U | 143 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U |
| 144 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S | 144 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S |
| 145 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI | 145 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI |
| 146 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // RGBX16F | ||
| 146 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI | 147 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI |
| 147 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8 | 148 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8 |
| 148 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5 | 149 | {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5 |
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index c8c1a7f40..b73f6536e 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -138,6 +138,35 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 138 | SetRegister(bb, instr.gpr0, value); | 138 | SetRegister(bb, instr.gpr0, value); |
| 139 | break; | 139 | break; |
| 140 | } | 140 | } |
| 141 | case OpCode::Id::ICMP_CR: | ||
| 142 | case OpCode::Id::ICMP_R: | ||
| 143 | case OpCode::Id::ICMP_RC: | ||
| 144 | case OpCode::Id::ICMP_IMM: { | ||
| 145 | const Node zero = Immediate(0); | ||
| 146 | |||
| 147 | const auto [op_b, test] = [&]() -> std::pair<Node, Node> { | ||
| 148 | switch (opcode->get().GetId()) { | ||
| 149 | case OpCode::Id::ICMP_CR: | ||
| 150 | return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset), | ||
| 151 | GetRegister(instr.gpr39)}; | ||
| 152 | case OpCode::Id::ICMP_R: | ||
| 153 | return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; | ||
| 154 | case OpCode::Id::ICMP_RC: | ||
| 155 | return {GetRegister(instr.gpr39), | ||
| 156 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)}; | ||
| 157 | case OpCode::Id::ICMP_IMM: | ||
| 158 | return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)}; | ||
| 159 | default: | ||
| 160 | UNREACHABLE(); | ||
| 161 | return {zero, zero}; | ||
| 162 | } | ||
| 163 | }(); | ||
| 164 | const Node op_a = GetRegister(instr.gpr8); | ||
| 165 | const Node comparison = | ||
| 166 | GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero); | ||
| 167 | SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_a, op_b)); | ||
| 168 | break; | ||
| 169 | } | ||
| 141 | case OpCode::Id::LOP_C: | 170 | case OpCode::Id::LOP_C: |
| 142 | case OpCode::Id::LOP_R: | 171 | case OpCode::Id::LOP_R: |
| 143 | case OpCode::Id::LOP_IMM: { | 172 | case OpCode::Id::LOP_IMM: { |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index bbbab0bca..2c357f310 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -114,6 +114,18 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff | |||
| 114 | break; | 114 | break; |
| 115 | } | 115 | } |
| 116 | } | 116 | } |
| 117 | if (index == Attribute::Index::TessCoordInstanceIDVertexID) { | ||
| 118 | switch (element) { | ||
| 119 | case 2: | ||
| 120 | uses_instance_id = true; | ||
| 121 | break; | ||
| 122 | case 3: | ||
| 123 | uses_vertex_id = true; | ||
| 124 | break; | ||
| 125 | default: | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | } | ||
| 117 | if (index == Attribute::Index::ClipDistances0123 || | 129 | if (index == Attribute::Index::ClipDistances0123 || |
| 118 | index == Attribute::Index::ClipDistances4567) { | 130 | index == Attribute::Index::ClipDistances4567) { |
| 119 | const auto clip_index = | 131 | const auto clip_index = |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index c3e147eea..6f666ee30 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -124,6 +124,14 @@ public: | |||
| 124 | return uses_point_size; | 124 | return uses_point_size; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | bool UsesInstanceId() const { | ||
| 128 | return uses_instance_id; | ||
| 129 | } | ||
| 130 | |||
| 131 | bool UsesVertexId() const { | ||
| 132 | return uses_vertex_id; | ||
| 133 | } | ||
| 134 | |||
| 127 | bool HasPhysicalAttributes() const { | 135 | bool HasPhysicalAttributes() const { |
| 128 | return uses_physical_attributes; | 136 | return uses_physical_attributes; |
| 129 | } | 137 | } |
| @@ -370,6 +378,8 @@ private: | |||
| 370 | bool uses_viewport_index{}; | 378 | bool uses_viewport_index{}; |
| 371 | bool uses_point_size{}; | 379 | bool uses_point_size{}; |
| 372 | bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes | 380 | bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes |
| 381 | bool uses_instance_id{}; | ||
| 382 | bool uses_vertex_id{}; | ||
| 373 | 383 | ||
| 374 | Tegra::Shader::Header header; | 384 | Tegra::Shader::Header header; |
| 375 | }; | 385 | }; |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 53d0142cb..250afc6d6 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -159,6 +159,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) | |||
| 159 | return PixelFormat::R32UI; | 159 | return PixelFormat::R32UI; |
| 160 | case Tegra::RenderTargetFormat::RG32_UINT: | 160 | case Tegra::RenderTargetFormat::RG32_UINT: |
| 161 | return PixelFormat::RG32UI; | 161 | return PixelFormat::RG32UI; |
| 162 | case Tegra::RenderTargetFormat::RGBX16_FLOAT: | ||
| 163 | return PixelFormat::RGBX16F; | ||
| 162 | default: | 164 | default: |
| 163 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); | 165 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); |
| 164 | UNREACHABLE(); | 166 | UNREACHABLE(); |
| @@ -415,6 +417,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { | |||
| 415 | case Tegra::RenderTargetFormat::RG8_SNORM: | 417 | case Tegra::RenderTargetFormat::RG8_SNORM: |
| 416 | return ComponentType::SNorm; | 418 | return ComponentType::SNorm; |
| 417 | case Tegra::RenderTargetFormat::RGBA16_FLOAT: | 419 | case Tegra::RenderTargetFormat::RGBA16_FLOAT: |
| 420 | case Tegra::RenderTargetFormat::RGBX16_FLOAT: | ||
| 418 | case Tegra::RenderTargetFormat::R11G11B10_FLOAT: | 421 | case Tegra::RenderTargetFormat::R11G11B10_FLOAT: |
| 419 | case Tegra::RenderTargetFormat::RGBA32_FLOAT: | 422 | case Tegra::RenderTargetFormat::RGBA32_FLOAT: |
| 420 | case Tegra::RenderTargetFormat::RG32_FLOAT: | 423 | case Tegra::RenderTargetFormat::RG32_FLOAT: |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 19268b7cd..1e1c432a5 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -57,36 +57,37 @@ enum class PixelFormat { | |||
| 57 | RG8U = 39, | 57 | RG8U = 39, |
| 58 | RG8S = 40, | 58 | RG8S = 40, |
| 59 | RG32UI = 41, | 59 | RG32UI = 41, |
| 60 | R32UI = 42, | 60 | RGBX16F = 42, |
| 61 | ASTC_2D_8X8 = 43, | 61 | R32UI = 43, |
| 62 | ASTC_2D_8X5 = 44, | 62 | ASTC_2D_8X8 = 44, |
| 63 | ASTC_2D_5X4 = 45, | 63 | ASTC_2D_8X5 = 45, |
| 64 | BGRA8_SRGB = 46, | 64 | ASTC_2D_5X4 = 46, |
| 65 | DXT1_SRGB = 47, | 65 | BGRA8_SRGB = 47, |
| 66 | DXT23_SRGB = 48, | 66 | DXT1_SRGB = 48, |
| 67 | DXT45_SRGB = 49, | 67 | DXT23_SRGB = 49, |
| 68 | BC7U_SRGB = 50, | 68 | DXT45_SRGB = 50, |
| 69 | ASTC_2D_4X4_SRGB = 51, | 69 | BC7U_SRGB = 51, |
| 70 | ASTC_2D_8X8_SRGB = 52, | 70 | ASTC_2D_4X4_SRGB = 52, |
| 71 | ASTC_2D_8X5_SRGB = 53, | 71 | ASTC_2D_8X8_SRGB = 53, |
| 72 | ASTC_2D_5X4_SRGB = 54, | 72 | ASTC_2D_8X5_SRGB = 54, |
| 73 | ASTC_2D_5X5 = 55, | 73 | ASTC_2D_5X4_SRGB = 55, |
| 74 | ASTC_2D_5X5_SRGB = 56, | 74 | ASTC_2D_5X5 = 56, |
| 75 | ASTC_2D_10X8 = 57, | 75 | ASTC_2D_5X5_SRGB = 57, |
| 76 | ASTC_2D_10X8_SRGB = 58, | 76 | ASTC_2D_10X8 = 58, |
| 77 | ASTC_2D_10X8_SRGB = 59, | ||
| 77 | 78 | ||
| 78 | MaxColorFormat, | 79 | MaxColorFormat, |
| 79 | 80 | ||
| 80 | // Depth formats | 81 | // Depth formats |
| 81 | Z32F = 59, | 82 | Z32F = 60, |
| 82 | Z16 = 60, | 83 | Z16 = 61, |
| 83 | 84 | ||
| 84 | MaxDepthFormat, | 85 | MaxDepthFormat, |
| 85 | 86 | ||
| 86 | // DepthStencil formats | 87 | // DepthStencil formats |
| 87 | Z24S8 = 61, | 88 | Z24S8 = 62, |
| 88 | S8Z24 = 62, | 89 | S8Z24 = 63, |
| 89 | Z32FS8 = 63, | 90 | Z32FS8 = 64, |
| 90 | 91 | ||
| 91 | MaxDepthStencilFormat, | 92 | MaxDepthStencilFormat, |
| 92 | 93 | ||
| @@ -166,6 +167,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{ | |||
| 166 | 0, // RG8U | 167 | 0, // RG8U |
| 167 | 0, // RG8S | 168 | 0, // RG8S |
| 168 | 0, // RG32UI | 169 | 0, // RG32UI |
| 170 | 0, // RGBX16F | ||
| 169 | 0, // R32UI | 171 | 0, // R32UI |
| 170 | 2, // ASTC_2D_8X8 | 172 | 2, // ASTC_2D_8X8 |
| 171 | 2, // ASTC_2D_8X5 | 173 | 2, // ASTC_2D_8X5 |
| @@ -249,6 +251,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ | |||
| 249 | 1, // RG8U | 251 | 1, // RG8U |
| 250 | 1, // RG8S | 252 | 1, // RG8S |
| 251 | 1, // RG32UI | 253 | 1, // RG32UI |
| 254 | 1, // RGBX16F | ||
| 252 | 1, // R32UI | 255 | 1, // R32UI |
| 253 | 8, // ASTC_2D_8X8 | 256 | 8, // ASTC_2D_8X8 |
| 254 | 8, // ASTC_2D_8X5 | 257 | 8, // ASTC_2D_8X5 |
| @@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ | |||
| 324 | 1, // RG8U | 327 | 1, // RG8U |
| 325 | 1, // RG8S | 328 | 1, // RG8S |
| 326 | 1, // RG32UI | 329 | 1, // RG32UI |
| 330 | 1, // RGBX16F | ||
| 327 | 1, // R32UI | 331 | 1, // R32UI |
| 328 | 8, // ASTC_2D_8X8 | 332 | 8, // ASTC_2D_8X8 |
| 329 | 5, // ASTC_2D_8X5 | 333 | 5, // ASTC_2D_8X5 |
| @@ -399,6 +403,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ | |||
| 399 | 16, // RG8U | 403 | 16, // RG8U |
| 400 | 16, // RG8S | 404 | 16, // RG8S |
| 401 | 64, // RG32UI | 405 | 64, // RG32UI |
| 406 | 64, // RGBX16F | ||
| 402 | 32, // R32UI | 407 | 32, // R32UI |
| 403 | 128, // ASTC_2D_8X8 | 408 | 128, // ASTC_2D_8X8 |
| 404 | 128, // ASTC_2D_8X5 | 409 | 128, // ASTC_2D_8X5 |
| @@ -489,6 +494,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table | |||
| 489 | SurfaceCompression::None, // RG8U | 494 | SurfaceCompression::None, // RG8U |
| 490 | SurfaceCompression::None, // RG8S | 495 | SurfaceCompression::None, // RG8S |
| 491 | SurfaceCompression::None, // RG32UI | 496 | SurfaceCompression::None, // RG32UI |
| 497 | SurfaceCompression::None, // RGBX16F | ||
| 492 | SurfaceCompression::None, // R32UI | 498 | SurfaceCompression::None, // R32UI |
| 493 | SurfaceCompression::Converted, // ASTC_2D_8X8 | 499 | SurfaceCompression::Converted, // ASTC_2D_8X8 |
| 494 | SurfaceCompression::Converted, // ASTC_2D_8X5 | 500 | SurfaceCompression::Converted, // ASTC_2D_8X5 |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index f051e17b4..dc6fa07fc 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -33,6 +33,9 @@ add_executable(yuzu | |||
| 33 | configuration/configure_debug.ui | 33 | configuration/configure_debug.ui |
| 34 | configuration/configure_dialog.cpp | 34 | configuration/configure_dialog.cpp |
| 35 | configuration/configure_dialog.h | 35 | configuration/configure_dialog.h |
| 36 | configuration/configure_filesystem.cpp | ||
| 37 | configuration/configure_filesystem.h | ||
| 38 | configuration/configure_filesystem.ui | ||
| 36 | configuration/configure_gamelist.cpp | 39 | configuration/configure_gamelist.cpp |
| 37 | configuration/configure_gamelist.h | 40 | configuration/configure_gamelist.h |
| 38 | configuration/configure_gamelist.ui | 41 | configuration/configure_gamelist.ui |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index f594106bf..92d9fb161 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -459,6 +459,49 @@ void Config::ReadDataStorageValues() { | |||
| 459 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) | 459 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) |
| 460 | .toString() | 460 | .toString() |
| 461 | .toStdString()); | 461 | .toStdString()); |
| 462 | FileUtil::GetUserPath( | ||
| 463 | FileUtil::UserPath::LoadDir, | ||
| 464 | qt_config | ||
| 465 | ->value(QStringLiteral("load_directory"), | ||
| 466 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))) | ||
| 467 | .toString() | ||
| 468 | .toStdString()); | ||
| 469 | FileUtil::GetUserPath( | ||
| 470 | FileUtil::UserPath::DumpDir, | ||
| 471 | qt_config | ||
| 472 | ->value(QStringLiteral("dump_directory"), | ||
| 473 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))) | ||
| 474 | .toString() | ||
| 475 | .toStdString()); | ||
| 476 | FileUtil::GetUserPath( | ||
| 477 | FileUtil::UserPath::CacheDir, | ||
| 478 | qt_config | ||
| 479 | ->value(QStringLiteral("cache_directory"), | ||
| 480 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))) | ||
| 481 | .toString() | ||
| 482 | .toStdString()); | ||
| 483 | Settings::values.gamecard_inserted = | ||
| 484 | ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); | ||
| 485 | Settings::values.gamecard_current_game = | ||
| 486 | ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); | ||
| 487 | Settings::values.gamecard_path = | ||
| 488 | ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); | ||
| 489 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||
| 490 | ReadSetting(QStringLiteral("nand_total_size"), | ||
| 491 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))) | ||
| 492 | .toULongLong()); | ||
| 493 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||
| 494 | ReadSetting(QStringLiteral("nand_user_size"), | ||
| 495 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))) | ||
| 496 | .toULongLong()); | ||
| 497 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 498 | ReadSetting(QStringLiteral("nand_system_size"), | ||
| 499 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))) | ||
| 500 | .toULongLong()); | ||
| 501 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||
| 502 | ReadSetting(QStringLiteral("sdmc_size"), | ||
| 503 | QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))) | ||
| 504 | .toULongLong()); | ||
| 462 | 505 | ||
| 463 | qt_config->endGroup(); | 506 | qt_config->endGroup(); |
| 464 | } | 507 | } |
| @@ -466,6 +509,9 @@ void Config::ReadDataStorageValues() { | |||
| 466 | void Config::ReadDebuggingValues() { | 509 | void Config::ReadDebuggingValues() { |
| 467 | qt_config->beginGroup(QStringLiteral("Debugging")); | 510 | qt_config->beginGroup(QStringLiteral("Debugging")); |
| 468 | 511 | ||
| 512 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 513 | Settings::values.record_frame_times = | ||
| 514 | qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); | ||
| 469 | Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); | 515 | Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); |
| 470 | Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); | 516 | Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); |
| 471 | Settings::values.program_args = | 517 | Settings::values.program_args = |
| @@ -872,13 +918,40 @@ void Config::SaveDataStorageValues() { | |||
| 872 | WriteSetting(QStringLiteral("sdmc_directory"), | 918 | WriteSetting(QStringLiteral("sdmc_directory"), |
| 873 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), | 919 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), |
| 874 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | 920 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); |
| 875 | 921 | WriteSetting(QStringLiteral("load_directory"), | |
| 922 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)), | ||
| 923 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||
| 924 | WriteSetting(QStringLiteral("dump_directory"), | ||
| 925 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)), | ||
| 926 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||
| 927 | WriteSetting(QStringLiteral("cache_directory"), | ||
| 928 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)), | ||
| 929 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||
| 930 | WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); | ||
| 931 | WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, | ||
| 932 | false); | ||
| 933 | WriteSetting(QStringLiteral("gamecard_path"), | ||
| 934 | QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); | ||
| 935 | WriteSetting(QStringLiteral("nand_total_size"), | ||
| 936 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)), | ||
| 937 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))); | ||
| 938 | WriteSetting(QStringLiteral("nand_user_size"), | ||
| 939 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)), | ||
| 940 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))); | ||
| 941 | WriteSetting(QStringLiteral("nand_system_size"), | ||
| 942 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)), | ||
| 943 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))); | ||
| 944 | WriteSetting(QStringLiteral("sdmc_size"), | ||
| 945 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)), | ||
| 946 | QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))); | ||
| 876 | qt_config->endGroup(); | 947 | qt_config->endGroup(); |
| 877 | } | 948 | } |
| 878 | 949 | ||
| 879 | void Config::SaveDebuggingValues() { | 950 | void Config::SaveDebuggingValues() { |
| 880 | qt_config->beginGroup(QStringLiteral("Debugging")); | 951 | qt_config->beginGroup(QStringLiteral("Debugging")); |
| 881 | 952 | ||
| 953 | // Intentionally not using the QT default setting as this is intended to be changed in the ini | ||
| 954 | qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); | ||
| 882 | WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); | 955 | WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); |
| 883 | WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); | 956 | WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); |
| 884 | WriteSetting(QStringLiteral("program_args"), | 957 | WriteSetting(QStringLiteral("program_args"), |
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 267717bc9..49fadd0ef 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui | |||
| @@ -63,6 +63,11 @@ | |||
| 63 | <string>Profiles</string> | 63 | <string>Profiles</string> |
| 64 | </attribute> | 64 | </attribute> |
| 65 | </widget> | 65 | </widget> |
| 66 | <widget class="ConfigureFilesystem" name="filesystemTab"> | ||
| 67 | <attribute name="title"> | ||
| 68 | <string>Filesystem</string> | ||
| 69 | </attribute> | ||
| 70 | </widget> | ||
| 66 | <widget class="ConfigureInputSimple" name="inputTab"> | 71 | <widget class="ConfigureInputSimple" name="inputTab"> |
| 67 | <attribute name="title"> | 72 | <attribute name="title"> |
| 68 | <string>Input</string> | 73 | <string>Input</string> |
| @@ -126,6 +131,12 @@ | |||
| 126 | <container>1</container> | 131 | <container>1</container> |
| 127 | </customwidget> | 132 | </customwidget> |
| 128 | <customwidget> | 133 | <customwidget> |
| 134 | <class>ConfigureFilesystem</class> | ||
| 135 | <extends>QWidget</extends> | ||
| 136 | <header>configuration/configure_filesystem.h</header> | ||
| 137 | <container>1</container> | ||
| 138 | </customwidget> | ||
| 139 | <customwidget> | ||
| 129 | <class>ConfigureAudio</class> | 140 | <class>ConfigureAudio</class> |
| 130 | <extends>QWidget</extends> | 141 | <extends>QWidget</extends> |
| 131 | <header>configuration/configure_audio.h</header> | 142 | <header>configuration/configure_audio.h</header> |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 5b7e03056..90c1f9459 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -34,8 +34,6 @@ void ConfigureDebug::SetConfiguration() { | |||
| 34 | ui->toggle_console->setChecked(UISettings::values.show_console); | 34 | ui->toggle_console->setChecked(UISettings::values.show_console); |
| 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); | 35 | ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); |
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); |
| 37 | ui->dump_exefs->setChecked(Settings::values.dump_exefs); | ||
| 38 | ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); | ||
| 39 | ui->reporting_services->setChecked(Settings::values.reporting_services); | 37 | ui->reporting_services->setChecked(Settings::values.reporting_services); |
| 40 | ui->quest_flag->setChecked(Settings::values.quest_flag); | 38 | ui->quest_flag->setChecked(Settings::values.quest_flag); |
| 41 | } | 39 | } |
| @@ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 46 | UISettings::values.show_console = ui->toggle_console->isChecked(); | 44 | UISettings::values.show_console = ui->toggle_console->isChecked(); |
| 47 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); | 45 | Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); |
| 48 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | 46 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); |
| 49 | Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | ||
| 50 | Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); | ||
| 51 | Settings::values.reporting_services = ui->reporting_services->isChecked(); | 47 | Settings::values.reporting_services = ui->reporting_services->isChecked(); |
| 52 | Settings::values.quest_flag = ui->quest_flag->isChecked(); | 48 | Settings::values.quest_flag = ui->quest_flag->isChecked(); |
| 53 | Debugger::ToggleConsole(); | 49 | Debugger::ToggleConsole(); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 7e109cef0..ce49569bb 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -103,58 +103,6 @@ | |||
| 103 | </item> | 103 | </item> |
| 104 | </layout> | 104 | </layout> |
| 105 | </item> | 105 | </item> |
| 106 | </layout> | ||
| 107 | </widget> | ||
| 108 | </item> | ||
| 109 | <item> | ||
| 110 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 111 | <property name="title"> | ||
| 112 | <string>Homebrew</string> | ||
| 113 | </property> | ||
| 114 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 115 | <item> | ||
| 116 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 117 | <item> | ||
| 118 | <widget class="QLabel" name="label_3"> | ||
| 119 | <property name="text"> | ||
| 120 | <string>Arguments String</string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 124 | <item> | ||
| 125 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </item> | ||
| 129 | </layout> | ||
| 130 | </widget> | ||
| 131 | </item> | ||
| 132 | <item> | ||
| 133 | <widget class="QGroupBox" name="groupBox_4"> | ||
| 134 | <property name="title"> | ||
| 135 | <string>Dump</string> | ||
| 136 | </property> | ||
| 137 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 138 | <item> | ||
| 139 | <widget class="QCheckBox" name="dump_decompressed_nso"> | ||
| 140 | <property name="whatsThis"> | ||
| 141 | <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> | ||
| 142 | </property> | ||
| 143 | <property name="text"> | ||
| 144 | <string>Dump Decompressed NSOs</string> | ||
| 145 | </property> | ||
| 146 | </widget> | ||
| 147 | </item> | ||
| 148 | <item> | ||
| 149 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 150 | <property name="whatsThis"> | ||
| 151 | <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||
| 152 | </property> | ||
| 153 | <property name="text"> | ||
| 154 | <string>Dump ExeFS</string> | ||
| 155 | </property> | ||
| 156 | </widget> | ||
| 157 | </item> | ||
| 158 | <item> | 106 | <item> |
| 159 | <widget class="QCheckBox" name="reporting_services"> | 107 | <widget class="QCheckBox" name="reporting_services"> |
| 160 | <property name="text"> | 108 | <property name="text"> |
| @@ -163,7 +111,7 @@ | |||
| 163 | </widget> | 111 | </widget> |
| 164 | </item> | 112 | </item> |
| 165 | <item> | 113 | <item> |
| 166 | <widget class="QLabel" name="label_3"> | 114 | <widget class="QLabel" name="label"> |
| 167 | <property name="font"> | 115 | <property name="font"> |
| 168 | <font> | 116 | <font> |
| 169 | <italic>true</italic> | 117 | <italic>true</italic> |
| @@ -197,10 +145,36 @@ | |||
| 197 | </widget> | 145 | </widget> |
| 198 | </item> | 146 | </item> |
| 199 | <item> | 147 | <item> |
| 148 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 149 | <property name="title"> | ||
| 150 | <string>Homebrew</string> | ||
| 151 | </property> | ||
| 152 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 153 | <item> | ||
| 154 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 155 | <item> | ||
| 156 | <widget class="QLabel" name="label_3"> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Arguments String</string> | ||
| 159 | </property> | ||
| 160 | </widget> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 164 | </item> | ||
| 165 | </layout> | ||
| 166 | </item> | ||
| 167 | </layout> | ||
| 168 | </widget> | ||
| 169 | </item> | ||
| 170 | <item> | ||
| 200 | <spacer name="verticalSpacer"> | 171 | <spacer name="verticalSpacer"> |
| 201 | <property name="orientation"> | 172 | <property name="orientation"> |
| 202 | <enum>Qt::Vertical</enum> | 173 | <enum>Qt::Vertical</enum> |
| 203 | </property> | 174 | </property> |
| 175 | <property name="sizeType"> | ||
| 176 | <enum>QSizePolicy::Expanding</enum> | ||
| 177 | </property> | ||
| 204 | <property name="sizeHint" stdset="0"> | 178 | <property name="sizeHint" stdset="0"> |
| 205 | <size> | 179 | <size> |
| 206 | <width>20</width> | 180 | <width>20</width> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 775e3f2ea..7c875ae87 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -37,6 +37,7 @@ void ConfigureDialog::ApplyConfiguration() { | |||
| 37 | ui->gameListTab->ApplyConfiguration(); | 37 | ui->gameListTab->ApplyConfiguration(); |
| 38 | ui->systemTab->ApplyConfiguration(); | 38 | ui->systemTab->ApplyConfiguration(); |
| 39 | ui->profileManagerTab->ApplyConfiguration(); | 39 | ui->profileManagerTab->ApplyConfiguration(); |
| 40 | ui->filesystemTab->applyConfiguration(); | ||
| 40 | ui->inputTab->ApplyConfiguration(); | 41 | ui->inputTab->ApplyConfiguration(); |
| 41 | ui->hotkeysTab->ApplyConfiguration(registry); | 42 | ui->hotkeysTab->ApplyConfiguration(registry); |
| 42 | ui->graphicsTab->ApplyConfiguration(); | 43 | ui->graphicsTab->ApplyConfiguration(); |
| @@ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); | |||
| 73 | void ConfigureDialog::PopulateSelectionList() { | 74 | void ConfigureDialog::PopulateSelectionList() { |
| 74 | const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ | 75 | const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ |
| 75 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, | 76 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, |
| 76 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}}, | 77 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}}, |
| 77 | {tr("Graphics"), {ui->graphicsTab}}, | 78 | {tr("Graphics"), {ui->graphicsTab}}, |
| 78 | {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, | 79 | {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, |
| 79 | }; | 80 | }; |
| @@ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 106 | {ui->debugTab, tr("Debug")}, | 107 | {ui->debugTab, tr("Debug")}, |
| 107 | {ui->webTab, tr("Web")}, | 108 | {ui->webTab, tr("Web")}, |
| 108 | {ui->gameListTab, tr("Game List")}, | 109 | {ui->gameListTab, tr("Game List")}, |
| 110 | {ui->filesystemTab, tr("Filesystem")}, | ||
| 109 | }; | 111 | }; |
| 110 | 112 | ||
| 111 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); | 113 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); |
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp new file mode 100644 index 000000000..29f540eb7 --- /dev/null +++ b/src/yuzu/configuration/configure_filesystem.cpp | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QFileDialog> | ||
| 6 | #include <QMessageBox> | ||
| 7 | #include "common/common_paths.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | #include "core/settings.h" | ||
| 10 | #include "ui_configure_filesystem.h" | ||
| 11 | #include "yuzu/configuration/configure_filesystem.h" | ||
| 12 | #include "yuzu/uisettings.h" | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | template <typename T> | ||
| 17 | void SetComboBoxFromData(QComboBox* combo_box, T data) { | ||
| 18 | const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data))); | ||
| 19 | if (index >= combo_box->count() || index < 0) | ||
| 20 | return; | ||
| 21 | |||
| 22 | combo_box->setCurrentIndex(index); | ||
| 23 | } | ||
| 24 | |||
| 25 | } // Anonymous namespace | ||
| 26 | |||
| 27 | ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) | ||
| 28 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) { | ||
| 29 | ui->setupUi(this); | ||
| 30 | this->setConfiguration(); | ||
| 31 | |||
| 32 | connect(ui->nand_directory_button, &QToolButton::pressed, this, | ||
| 33 | [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); }); | ||
| 34 | connect(ui->sdmc_directory_button, &QToolButton::pressed, this, | ||
| 35 | [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); }); | ||
| 36 | connect(ui->gamecard_path_button, &QToolButton::pressed, this, | ||
| 37 | [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); }); | ||
| 38 | connect(ui->dump_path_button, &QToolButton::pressed, this, | ||
| 39 | [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); }); | ||
| 40 | connect(ui->load_path_button, &QToolButton::pressed, this, | ||
| 41 | [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); }); | ||
| 42 | connect(ui->cache_directory_button, &QToolButton::pressed, this, | ||
| 43 | [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); }); | ||
| 44 | |||
| 45 | connect(ui->reset_game_list_cache, &QPushButton::pressed, this, | ||
| 46 | &ConfigureFilesystem::ResetMetadata); | ||
| 47 | |||
| 48 | connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this, | ||
| 49 | &ConfigureFilesystem::UpdateEnabledControls); | ||
| 50 | connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this, | ||
| 51 | &ConfigureFilesystem::UpdateEnabledControls); | ||
| 52 | } | ||
| 53 | |||
| 54 | ConfigureFilesystem::~ConfigureFilesystem() = default; | ||
| 55 | |||
| 56 | void ConfigureFilesystem::setConfiguration() { | ||
| 57 | ui->nand_directory_edit->setText( | ||
| 58 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | ||
| 59 | ui->sdmc_directory_edit->setText( | ||
| 60 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | ||
| 61 | ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); | ||
| 62 | ui->dump_path_edit->setText( | ||
| 63 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||
| 64 | ui->load_path_edit->setText( | ||
| 65 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||
| 66 | ui->cache_directory_edit->setText( | ||
| 67 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||
| 68 | |||
| 69 | ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); | ||
| 70 | ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); | ||
| 71 | ui->dump_exefs->setChecked(Settings::values.dump_exefs); | ||
| 72 | ui->dump_nso->setChecked(Settings::values.dump_nso); | ||
| 73 | |||
| 74 | ui->cache_game_list->setChecked(UISettings::values.cache_game_list); | ||
| 75 | |||
| 76 | SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size); | ||
| 77 | SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size); | ||
| 78 | SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size); | ||
| 79 | SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size); | ||
| 80 | |||
| 81 | UpdateEnabledControls(); | ||
| 82 | } | ||
| 83 | |||
| 84 | void ConfigureFilesystem::applyConfiguration() { | ||
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir, | ||
| 86 | ui->nand_directory_edit->text().toStdString()); | ||
| 87 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | ||
| 88 | ui->sdmc_directory_edit->text().toStdString()); | ||
| 89 | FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString()); | ||
| 90 | FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString()); | ||
| 91 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, | ||
| 92 | ui->cache_directory_edit->text().toStdString()); | ||
| 93 | Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString(); | ||
| 94 | |||
| 95 | Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); | ||
| 96 | Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked(); | ||
| 97 | Settings::values.dump_exefs = ui->dump_exefs->isChecked(); | ||
| 98 | Settings::values.dump_nso = ui->dump_nso->isChecked(); | ||
| 99 | |||
| 100 | UISettings::values.cache_game_list = ui->cache_game_list->isChecked(); | ||
| 101 | |||
| 102 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||
| 103 | ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong()); | ||
| 104 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 105 | ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong()); | ||
| 106 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||
| 107 | ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong()); | ||
| 108 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||
| 109 | ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong()); | ||
| 110 | } | ||
| 111 | |||
| 112 | void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) { | ||
| 113 | QString caption; | ||
| 114 | |||
| 115 | switch (target) { | ||
| 116 | case DirectoryTarget::NAND: | ||
| 117 | caption = tr("Select Emulated NAND Directory..."); | ||
| 118 | break; | ||
| 119 | case DirectoryTarget::SD: | ||
| 120 | caption = tr("Select Emulated SD Directory..."); | ||
| 121 | break; | ||
| 122 | case DirectoryTarget::Gamecard: | ||
| 123 | caption = tr("Select Gamecard Path..."); | ||
| 124 | break; | ||
| 125 | case DirectoryTarget::Dump: | ||
| 126 | caption = tr("Select Dump Directory..."); | ||
| 127 | break; | ||
| 128 | case DirectoryTarget::Load: | ||
| 129 | caption = tr("Select Mod Load Directory..."); | ||
| 130 | break; | ||
| 131 | case DirectoryTarget::Cache: | ||
| 132 | caption = tr("Select Cache Directory..."); | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | |||
| 136 | QString str; | ||
| 137 | if (target == DirectoryTarget::Gamecard) { | ||
| 138 | str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(), | ||
| 139 | QStringLiteral("NX Gamecard;*.xci")); | ||
| 140 | } else { | ||
| 141 | str = QFileDialog::getExistingDirectory(this, caption, edit->text()); | ||
| 142 | } | ||
| 143 | |||
| 144 | if (str.isEmpty()) | ||
| 145 | return; | ||
| 146 | |||
| 147 | edit->setText(str); | ||
| 148 | } | ||
| 149 | |||
| 150 | void ConfigureFilesystem::ResetMetadata() { | ||
| 151 | if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||
| 152 | "game_list")) { | ||
| 153 | QMessageBox::information(this, tr("Reset Metadata Cache"), | ||
| 154 | tr("The metadata cache is already empty.")); | ||
| 155 | } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | ||
| 156 | DIR_SEP + "game_list")) { | ||
| 157 | QMessageBox::information(this, tr("Reset Metadata Cache"), | ||
| 158 | tr("The operation completed successfully.")); | ||
| 159 | UISettings::values.is_game_list_reload_pending.exchange(true); | ||
| 160 | } else { | ||
| 161 | QMessageBox::warning( | ||
| 162 | this, tr("Reset Metadata Cache"), | ||
| 163 | tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | void ConfigureFilesystem::UpdateEnabledControls() { | ||
| 168 | ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked()); | ||
| 169 | ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() && | ||
| 170 | !ui->gamecard_current_game->isChecked()); | ||
| 171 | ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() && | ||
| 172 | !ui->gamecard_current_game->isChecked()); | ||
| 173 | } | ||
| 174 | |||
| 175 | void ConfigureFilesystem::retranslateUi() { | ||
| 176 | ui->retranslateUi(this); | ||
| 177 | } | ||
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h new file mode 100644 index 000000000..a79303760 --- /dev/null +++ b/src/yuzu/configuration/configure_filesystem.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <QWidget> | ||
| 9 | |||
| 10 | class QLineEdit; | ||
| 11 | |||
| 12 | namespace Ui { | ||
| 13 | class ConfigureFilesystem; | ||
| 14 | } | ||
| 15 | |||
| 16 | class ConfigureFilesystem : public QWidget { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 20 | explicit ConfigureFilesystem(QWidget* parent = nullptr); | ||
| 21 | ~ConfigureFilesystem() override; | ||
| 22 | |||
| 23 | void applyConfiguration(); | ||
| 24 | void retranslateUi(); | ||
| 25 | |||
| 26 | private: | ||
| 27 | void setConfiguration(); | ||
| 28 | |||
| 29 | enum class DirectoryTarget { | ||
| 30 | NAND, | ||
| 31 | SD, | ||
| 32 | Gamecard, | ||
| 33 | Dump, | ||
| 34 | Load, | ||
| 35 | Cache, | ||
| 36 | }; | ||
| 37 | |||
| 38 | void SetDirectory(DirectoryTarget target, QLineEdit* edit); | ||
| 39 | void ResetMetadata(); | ||
| 40 | void UpdateEnabledControls(); | ||
| 41 | |||
| 42 | std::unique_ptr<Ui::ConfigureFilesystem> ui; | ||
| 43 | }; | ||
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui new file mode 100644 index 000000000..58cd07f52 --- /dev/null +++ b/src/yuzu/configuration/configure_filesystem.ui | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureFilesystem</class> | ||
| 4 | <widget class="QWidget" name="ConfigureFilesystem"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>453</width> | ||
| 10 | <height>561</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Form</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="groupBox"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>Storage Directories</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QGridLayout" name="gridLayout"> | ||
| 25 | <item row="0" column="0"> | ||
| 26 | <widget class="QLabel" name="label"> | ||
| 27 | <property name="text"> | ||
| 28 | <string>NAND</string> | ||
| 29 | </property> | ||
| 30 | </widget> | ||
| 31 | </item> | ||
| 32 | <item row="0" column="3"> | ||
| 33 | <widget class="QToolButton" name="nand_directory_button"> | ||
| 34 | <property name="text"> | ||
| 35 | <string>...</string> | ||
| 36 | </property> | ||
| 37 | </widget> | ||
| 38 | </item> | ||
| 39 | <item row="0" column="2"> | ||
| 40 | <widget class="QLineEdit" name="nand_directory_edit"/> | ||
| 41 | </item> | ||
| 42 | <item row="1" column="2"> | ||
| 43 | <widget class="QLineEdit" name="sdmc_directory_edit"/> | ||
| 44 | </item> | ||
| 45 | <item row="1" column="0"> | ||
| 46 | <widget class="QLabel" name="label_2"> | ||
| 47 | <property name="text"> | ||
| 48 | <string>SD Card</string> | ||
| 49 | </property> | ||
| 50 | </widget> | ||
| 51 | </item> | ||
| 52 | <item row="1" column="3"> | ||
| 53 | <widget class="QToolButton" name="sdmc_directory_button"> | ||
| 54 | <property name="text"> | ||
| 55 | <string>...</string> | ||
| 56 | </property> | ||
| 57 | </widget> | ||
| 58 | </item> | ||
| 59 | <item row="0" column="1"> | ||
| 60 | <spacer name="horizontalSpacer"> | ||
| 61 | <property name="orientation"> | ||
| 62 | <enum>Qt::Horizontal</enum> | ||
| 63 | </property> | ||
| 64 | <property name="sizeType"> | ||
| 65 | <enum>QSizePolicy::Maximum</enum> | ||
| 66 | </property> | ||
| 67 | <property name="sizeHint" stdset="0"> | ||
| 68 | <size> | ||
| 69 | <width>60</width> | ||
| 70 | <height>20</height> | ||
| 71 | </size> | ||
| 72 | </property> | ||
| 73 | </spacer> | ||
| 74 | </item> | ||
| 75 | </layout> | ||
| 76 | </widget> | ||
| 77 | </item> | ||
| 78 | <item> | ||
| 79 | <widget class="QGroupBox" name="groupBox_2"> | ||
| 80 | <property name="title"> | ||
| 81 | <string>Gamecard</string> | ||
| 82 | </property> | ||
| 83 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 84 | <item row="2" column="1"> | ||
| 85 | <widget class="QLabel" name="label_3"> | ||
| 86 | <property name="text"> | ||
| 87 | <string>Path</string> | ||
| 88 | </property> | ||
| 89 | </widget> | ||
| 90 | </item> | ||
| 91 | <item row="2" column="2"> | ||
| 92 | <widget class="QLineEdit" name="gamecard_path_edit"/> | ||
| 93 | </item> | ||
| 94 | <item row="0" column="1"> | ||
| 95 | <widget class="QCheckBox" name="gamecard_inserted"> | ||
| 96 | <property name="text"> | ||
| 97 | <string>Inserted</string> | ||
| 98 | </property> | ||
| 99 | </widget> | ||
| 100 | </item> | ||
| 101 | <item row="1" column="1"> | ||
| 102 | <widget class="QCheckBox" name="gamecard_current_game"> | ||
| 103 | <property name="text"> | ||
| 104 | <string>Current Game</string> | ||
| 105 | </property> | ||
| 106 | </widget> | ||
| 107 | </item> | ||
| 108 | <item row="2" column="3"> | ||
| 109 | <widget class="QToolButton" name="gamecard_path_button"> | ||
| 110 | <property name="text"> | ||
| 111 | <string>...</string> | ||
| 112 | </property> | ||
| 113 | </widget> | ||
| 114 | </item> | ||
| 115 | </layout> | ||
| 116 | </widget> | ||
| 117 | </item> | ||
| 118 | <item> | ||
| 119 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 120 | <property name="title"> | ||
| 121 | <string>Storage Sizes</string> | ||
| 122 | </property> | ||
| 123 | <layout class="QGridLayout" name="gridLayout_3"> | ||
| 124 | <item row="3" column="0"> | ||
| 125 | <widget class="QLabel" name="label_5"> | ||
| 126 | <property name="text"> | ||
| 127 | <string>SD Card</string> | ||
| 128 | </property> | ||
| 129 | </widget> | ||
| 130 | </item> | ||
| 131 | <item row="1" column="0"> | ||
| 132 | <widget class="QLabel" name="label_4"> | ||
| 133 | <property name="text"> | ||
| 134 | <string>System NAND</string> | ||
| 135 | </property> | ||
| 136 | </widget> | ||
| 137 | </item> | ||
| 138 | <item row="1" column="1"> | ||
| 139 | <widget class="QComboBox" name="sysnand_size"> | ||
| 140 | <item> | ||
| 141 | <property name="text"> | ||
| 142 | <string>2.5 GB</string> | ||
| 143 | </property> | ||
| 144 | </item> | ||
| 145 | </widget> | ||
| 146 | </item> | ||
| 147 | <item row="3" column="1"> | ||
| 148 | <widget class="QComboBox" name="sdmc_size"> | ||
| 149 | <property name="currentText"> | ||
| 150 | <string>32 GB</string> | ||
| 151 | </property> | ||
| 152 | <item> | ||
| 153 | <property name="text"> | ||
| 154 | <string>1 GB</string> | ||
| 155 | </property> | ||
| 156 | </item> | ||
| 157 | <item> | ||
| 158 | <property name="text"> | ||
| 159 | <string>2 GB</string> | ||
| 160 | </property> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <property name="text"> | ||
| 164 | <string>4 GB</string> | ||
| 165 | </property> | ||
| 166 | </item> | ||
| 167 | <item> | ||
| 168 | <property name="text"> | ||
| 169 | <string>8 GB</string> | ||
| 170 | </property> | ||
| 171 | </item> | ||
| 172 | <item> | ||
| 173 | <property name="text"> | ||
| 174 | <string>16 GB</string> | ||
| 175 | </property> | ||
| 176 | </item> | ||
| 177 | <item> | ||
| 178 | <property name="text"> | ||
| 179 | <string>32 GB</string> | ||
| 180 | </property> | ||
| 181 | </item> | ||
| 182 | <item> | ||
| 183 | <property name="text"> | ||
| 184 | <string>64 GB</string> | ||
| 185 | </property> | ||
| 186 | </item> | ||
| 187 | <item> | ||
| 188 | <property name="text"> | ||
| 189 | <string>128 GB</string> | ||
| 190 | </property> | ||
| 191 | </item> | ||
| 192 | <item> | ||
| 193 | <property name="text"> | ||
| 194 | <string>256 GB</string> | ||
| 195 | </property> | ||
| 196 | </item> | ||
| 197 | <item> | ||
| 198 | <property name="text"> | ||
| 199 | <string>1 TB</string> | ||
| 200 | </property> | ||
| 201 | </item> | ||
| 202 | </widget> | ||
| 203 | </item> | ||
| 204 | <item row="2" column="1"> | ||
| 205 | <widget class="QComboBox" name="usrnand_size"> | ||
| 206 | <item> | ||
| 207 | <property name="text"> | ||
| 208 | <string>26 GB</string> | ||
| 209 | </property> | ||
| 210 | </item> | ||
| 211 | </widget> | ||
| 212 | </item> | ||
| 213 | <item row="2" column="0"> | ||
| 214 | <widget class="QLabel" name="label_6"> | ||
| 215 | <property name="text"> | ||
| 216 | <string>User NAND</string> | ||
| 217 | </property> | ||
| 218 | </widget> | ||
| 219 | </item> | ||
| 220 | <item row="0" column="0"> | ||
| 221 | <widget class="QLabel" name="label_7"> | ||
| 222 | <property name="text"> | ||
| 223 | <string>NAND</string> | ||
| 224 | </property> | ||
| 225 | </widget> | ||
| 226 | </item> | ||
| 227 | <item row="0" column="1"> | ||
| 228 | <widget class="QComboBox" name="nand_size"> | ||
| 229 | <item> | ||
| 230 | <property name="text"> | ||
| 231 | <string>29.1 GB</string> | ||
| 232 | </property> | ||
| 233 | </item> | ||
| 234 | </widget> | ||
| 235 | </item> | ||
| 236 | </layout> | ||
| 237 | </widget> | ||
| 238 | </item> | ||
| 239 | <item> | ||
| 240 | <widget class="QGroupBox" name="groupBox_4"> | ||
| 241 | <property name="title"> | ||
| 242 | <string>Patch Manager</string> | ||
| 243 | </property> | ||
| 244 | <layout class="QGridLayout" name="gridLayout_4"> | ||
| 245 | <item row="1" column="2"> | ||
| 246 | <widget class="QLineEdit" name="load_path_edit"/> | ||
| 247 | </item> | ||
| 248 | <item row="0" column="2"> | ||
| 249 | <widget class="QLineEdit" name="dump_path_edit"/> | ||
| 250 | </item> | ||
| 251 | <item row="0" column="3"> | ||
| 252 | <widget class="QToolButton" name="dump_path_button"> | ||
| 253 | <property name="text"> | ||
| 254 | <string>...</string> | ||
| 255 | </property> | ||
| 256 | </widget> | ||
| 257 | </item> | ||
| 258 | <item row="1" column="3"> | ||
| 259 | <widget class="QToolButton" name="load_path_button"> | ||
| 260 | <property name="text"> | ||
| 261 | <string>...</string> | ||
| 262 | </property> | ||
| 263 | </widget> | ||
| 264 | </item> | ||
| 265 | <item row="2" column="0" colspan="4"> | ||
| 266 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 267 | <item> | ||
| 268 | <widget class="QCheckBox" name="dump_nso"> | ||
| 269 | <property name="text"> | ||
| 270 | <string>Dump Decompressed NSOs</string> | ||
| 271 | </property> | ||
| 272 | </widget> | ||
| 273 | </item> | ||
| 274 | <item> | ||
| 275 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 276 | <property name="text"> | ||
| 277 | <string>Dump ExeFS</string> | ||
| 278 | </property> | ||
| 279 | </widget> | ||
| 280 | </item> | ||
| 281 | </layout> | ||
| 282 | </item> | ||
| 283 | <item row="1" column="0"> | ||
| 284 | <widget class="QLabel" name="label_9"> | ||
| 285 | <property name="text"> | ||
| 286 | <string>Mod Load Root</string> | ||
| 287 | </property> | ||
| 288 | </widget> | ||
| 289 | </item> | ||
| 290 | <item row="0" column="0"> | ||
| 291 | <widget class="QLabel" name="label_8"> | ||
| 292 | <property name="text"> | ||
| 293 | <string>Dump Root</string> | ||
| 294 | </property> | ||
| 295 | </widget> | ||
| 296 | </item> | ||
| 297 | <item row="0" column="1"> | ||
| 298 | <spacer name="horizontalSpacer_2"> | ||
| 299 | <property name="orientation"> | ||
| 300 | <enum>Qt::Horizontal</enum> | ||
| 301 | </property> | ||
| 302 | <property name="sizeType"> | ||
| 303 | <enum>QSizePolicy::Fixed</enum> | ||
| 304 | </property> | ||
| 305 | <property name="sizeHint" stdset="0"> | ||
| 306 | <size> | ||
| 307 | <width>40</width> | ||
| 308 | <height>20</height> | ||
| 309 | </size> | ||
| 310 | </property> | ||
| 311 | </spacer> | ||
| 312 | </item> | ||
| 313 | </layout> | ||
| 314 | </widget> | ||
| 315 | </item> | ||
| 316 | <item> | ||
| 317 | <widget class="QGroupBox" name="groupBox_5"> | ||
| 318 | <property name="title"> | ||
| 319 | <string>Caching</string> | ||
| 320 | </property> | ||
| 321 | <layout class="QGridLayout" name="gridLayout_5"> | ||
| 322 | <item row="0" column="0"> | ||
| 323 | <widget class="QLabel" name="label_10"> | ||
| 324 | <property name="text"> | ||
| 325 | <string>Cache Directory</string> | ||
| 326 | </property> | ||
| 327 | </widget> | ||
| 328 | </item> | ||
| 329 | <item row="0" column="1"> | ||
| 330 | <spacer name="horizontalSpacer_3"> | ||
| 331 | <property name="orientation"> | ||
| 332 | <enum>Qt::Horizontal</enum> | ||
| 333 | </property> | ||
| 334 | <property name="sizeType"> | ||
| 335 | <enum>QSizePolicy::Fixed</enum> | ||
| 336 | </property> | ||
| 337 | <property name="sizeHint" stdset="0"> | ||
| 338 | <size> | ||
| 339 | <width>40</width> | ||
| 340 | <height>20</height> | ||
| 341 | </size> | ||
| 342 | </property> | ||
| 343 | </spacer> | ||
| 344 | </item> | ||
| 345 | <item row="0" column="2"> | ||
| 346 | <widget class="QLineEdit" name="cache_directory_edit"/> | ||
| 347 | </item> | ||
| 348 | <item row="0" column="3"> | ||
| 349 | <widget class="QToolButton" name="cache_directory_button"> | ||
| 350 | <property name="text"> | ||
| 351 | <string>...</string> | ||
| 352 | </property> | ||
| 353 | </widget> | ||
| 354 | </item> | ||
| 355 | <item row="1" column="0" colspan="4"> | ||
| 356 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 357 | <item> | ||
| 358 | <widget class="QCheckBox" name="cache_game_list"> | ||
| 359 | <property name="text"> | ||
| 360 | <string>Cache Game List Metadata</string> | ||
| 361 | </property> | ||
| 362 | </widget> | ||
| 363 | </item> | ||
| 364 | <item> | ||
| 365 | <widget class="QPushButton" name="reset_game_list_cache"> | ||
| 366 | <property name="text"> | ||
| 367 | <string>Reset Metadata Cache</string> | ||
| 368 | </property> | ||
| 369 | </widget> | ||
| 370 | </item> | ||
| 371 | </layout> | ||
| 372 | </item> | ||
| 373 | </layout> | ||
| 374 | </widget> | ||
| 375 | </item> | ||
| 376 | </layout> | ||
| 377 | </item> | ||
| 378 | <item> | ||
| 379 | <spacer name="verticalSpacer"> | ||
| 380 | <property name="orientation"> | ||
| 381 | <enum>Qt::Vertical</enum> | ||
| 382 | </property> | ||
| 383 | <property name="sizeHint" stdset="0"> | ||
| 384 | <size> | ||
| 385 | <width>20</width> | ||
| 386 | <height>40</height> | ||
| 387 | </size> | ||
| 388 | </property> | ||
| 389 | </spacer> | ||
| 390 | </item> | ||
| 391 | </layout> | ||
| 392 | </widget> | ||
| 393 | <resources/> | ||
| 394 | <connections/> | ||
| 395 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 10bcd650e..98bc9b391 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QCheckBox> | ||
| 6 | #include <QSpinBox> | ||
| 5 | #include "core/core.h" | 7 | #include "core/core.h" |
| 6 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 7 | #include "ui_configure_general.h" | 9 | #include "ui_configure_general.h" |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 7613197f2..f2977719c 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -182,6 +182,8 @@ void ConfigureInput::UpdateUIEnabled() { | |||
| 182 | players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); | 182 | players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() && | ||
| 186 | !ui->use_docked_mode->isChecked()); | ||
| 185 | ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); | 187 | ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); |
| 186 | ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && | 188 | ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && |
| 187 | !ui->use_docked_mode->isChecked()); | 189 | !ui->use_docked_mode->isChecked()); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8304c6517..2d82df739 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -22,6 +22,8 @@ | |||
| 22 | #include "core/frontend/applets/general_frontend.h" | 22 | #include "core/frontend/applets/general_frontend.h" |
| 23 | #include "core/frontend/scope_acquire_window_context.h" | 23 | #include "core/frontend/scope_acquire_window_context.h" |
| 24 | #include "core/hle/service/acc/profile_manager.h" | 24 | #include "core/hle/service/acc/profile_manager.h" |
| 25 | #include "core/hle/service/am/applet_ae.h" | ||
| 26 | #include "core/hle/service/am/applet_oe.h" | ||
| 25 | #include "core/hle/service/am/applets/applets.h" | 27 | #include "core/hle/service/am/applets/applets.h" |
| 26 | #include "core/hle/service/hid/controllers/npad.h" | 28 | #include "core/hle/service/hid/controllers/npad.h" |
| 27 | #include "core/hle/service/hid/hid.h" | 29 | #include "core/hle/service/hid/hid.h" |
| @@ -54,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 54 | #include <QProgressDialog> | 56 | #include <QProgressDialog> |
| 55 | #include <QShortcut> | 57 | #include <QShortcut> |
| 56 | #include <QStatusBar> | 58 | #include <QStatusBar> |
| 59 | #include <QSysInfo> | ||
| 57 | #include <QtConcurrent/QtConcurrent> | 60 | #include <QtConcurrent/QtConcurrent> |
| 58 | 61 | ||
| 59 | #include <fmt/format.h> | 62 | #include <fmt/format.h> |
| @@ -66,6 +69,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 66 | #include "common/microprofile.h" | 69 | #include "common/microprofile.h" |
| 67 | #include "common/scm_rev.h" | 70 | #include "common/scm_rev.h" |
| 68 | #include "common/scope_exit.h" | 71 | #include "common/scope_exit.h" |
| 72 | #ifdef ARCHITECTURE_x86_64 | ||
| 73 | #include "common/x64/cpu_detect.h" | ||
| 74 | #endif | ||
| 69 | #include "common/telemetry.h" | 75 | #include "common/telemetry.h" |
| 70 | #include "core/core.h" | 76 | #include "core/core.h" |
| 71 | #include "core/crypto/key_manager.h" | 77 | #include "core/crypto/key_manager.h" |
| @@ -79,6 +85,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 79 | #include "core/file_sys/submission_package.h" | 85 | #include "core/file_sys/submission_package.h" |
| 80 | #include "core/frontend/applets/software_keyboard.h" | 86 | #include "core/frontend/applets/software_keyboard.h" |
| 81 | #include "core/hle/kernel/process.h" | 87 | #include "core/hle/kernel/process.h" |
| 88 | #include "core/hle/service/am/am.h" | ||
| 82 | #include "core/hle/service/filesystem/filesystem.h" | 89 | #include "core/hle/service/filesystem/filesystem.h" |
| 83 | #include "core/hle/service/nfp/nfp.h" | 90 | #include "core/hle/service/nfp/nfp.h" |
| 84 | #include "core/hle/service/sm/sm.h" | 91 | #include "core/hle/service/sm/sm.h" |
| @@ -205,6 +212,10 @@ GMainWindow::GMainWindow() | |||
| 205 | 212 | ||
| 206 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | 213 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |
| 207 | Common::g_scm_desc); | 214 | Common::g_scm_desc); |
| 215 | #ifdef ARCHITECTURE_x86_64 | ||
| 216 | LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); | ||
| 217 | #endif | ||
| 218 | LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); | ||
| 208 | UpdateWindowTitle(); | 219 | UpdateWindowTitle(); |
| 209 | 220 | ||
| 210 | show(); | 221 | show(); |
| @@ -213,7 +224,7 @@ GMainWindow::GMainWindow() | |||
| 213 | std::make_unique<FileSys::ContentProviderUnion>()); | 224 | std::make_unique<FileSys::ContentProviderUnion>()); |
| 214 | Core::System::GetInstance().RegisterContentProvider( | 225 | Core::System::GetInstance().RegisterContentProvider( |
| 215 | FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); | 226 | FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); |
| 216 | Service::FileSystem::CreateFactories(*vfs); | 227 | Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); |
| 217 | 228 | ||
| 218 | // Gen keys if necessary | 229 | // Gen keys if necessary |
| 219 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); | 230 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); |
| @@ -964,11 +975,11 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 964 | } | 975 | } |
| 965 | status_bar_update_timer.start(2000); | 976 | status_bar_update_timer.start(2000); |
| 966 | 977 | ||
| 978 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | ||
| 979 | |||
| 967 | std::string title_name; | 980 | std::string title_name; |
| 968 | const auto res = Core::System::GetInstance().GetGameName(title_name); | 981 | const auto res = Core::System::GetInstance().GetGameName(title_name); |
| 969 | if (res != Loader::ResultStatus::Success) { | 982 | if (res != Loader::ResultStatus::Success) { |
| 970 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | ||
| 971 | |||
| 972 | const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata(); | 983 | const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata(); |
| 973 | if (nacp != nullptr) | 984 | if (nacp != nullptr) |
| 974 | title_name = nacp->GetApplicationName(); | 985 | title_name = nacp->GetApplicationName(); |
| @@ -976,7 +987,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 976 | if (title_name.empty()) | 987 | if (title_name.empty()) |
| 977 | title_name = FileUtil::GetFilename(filename.toStdString()); | 988 | title_name = FileUtil::GetFilename(filename.toStdString()); |
| 978 | } | 989 | } |
| 979 | 990 | LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); | |
| 980 | UpdateWindowTitle(QString::fromStdString(title_name)); | 991 | UpdateWindowTitle(QString::fromStdString(title_name)); |
| 981 | 992 | ||
| 982 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | 993 | loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); |
| @@ -1499,15 +1510,19 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1499 | failed(); | 1510 | failed(); |
| 1500 | return; | 1511 | return; |
| 1501 | } | 1512 | } |
| 1502 | const auto res = | 1513 | const auto res = Core::System::GetInstance() |
| 1503 | Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy); | 1514 | .GetFileSystemController() |
| 1515 | .GetUserNANDContents() | ||
| 1516 | ->InstallEntry(*nsp, false, qt_raw_copy); | ||
| 1504 | if (res == FileSys::InstallResult::Success) { | 1517 | if (res == FileSys::InstallResult::Success) { |
| 1505 | success(); | 1518 | success(); |
| 1506 | } else { | 1519 | } else { |
| 1507 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 1520 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { |
| 1508 | if (overwrite()) { | 1521 | if (overwrite()) { |
| 1509 | const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( | 1522 | const auto res2 = Core::System::GetInstance() |
| 1510 | *nsp, true, qt_raw_copy); | 1523 | .GetFileSystemController() |
| 1524 | .GetUserNANDContents() | ||
| 1525 | ->InstallEntry(*nsp, true, qt_raw_copy); | ||
| 1511 | if (res2 == FileSys::InstallResult::Success) { | 1526 | if (res2 == FileSys::InstallResult::Success) { |
| 1512 | success(); | 1527 | success(); |
| 1513 | } else { | 1528 | } else { |
| @@ -1561,19 +1576,28 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1561 | 1576 | ||
| 1562 | FileSys::InstallResult res; | 1577 | FileSys::InstallResult res; |
| 1563 | if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { | 1578 | if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { |
| 1564 | res = Service::FileSystem::GetUserNANDContents()->InstallEntry( | 1579 | res = Core::System::GetInstance() |
| 1565 | *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | 1580 | .GetFileSystemController() |
| 1581 | .GetUserNANDContents() | ||
| 1582 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1583 | qt_raw_copy); | ||
| 1566 | } else { | 1584 | } else { |
| 1567 | res = Service::FileSystem::GetSystemNANDContents()->InstallEntry( | 1585 | res = Core::System::GetInstance() |
| 1568 | *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); | 1586 | .GetFileSystemController() |
| 1587 | .GetSystemNANDContents() | ||
| 1588 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1589 | qt_raw_copy); | ||
| 1569 | } | 1590 | } |
| 1570 | 1591 | ||
| 1571 | if (res == FileSys::InstallResult::Success) { | 1592 | if (res == FileSys::InstallResult::Success) { |
| 1572 | success(); | 1593 | success(); |
| 1573 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 1594 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { |
| 1574 | if (overwrite()) { | 1595 | if (overwrite()) { |
| 1575 | const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( | 1596 | const auto res2 = Core::System::GetInstance() |
| 1576 | *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); | 1597 | .GetFileSystemController() |
| 1598 | .GetUserNANDContents() | ||
| 1599 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), | ||
| 1600 | true, qt_raw_copy); | ||
| 1577 | if (res2 == FileSys::InstallResult::Success) { | 1601 | if (res2 == FileSys::InstallResult::Success) { |
| 1578 | success(); | 1602 | success(); |
| 1579 | } else { | 1603 | } else { |
| @@ -1603,7 +1627,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) | |||
| 1603 | FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir | 1627 | FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir |
| 1604 | : FileUtil::UserPath::NANDDir, | 1628 | : FileUtil::UserPath::NANDDir, |
| 1605 | dir_path.toStdString()); | 1629 | dir_path.toStdString()); |
| 1606 | Service::FileSystem::CreateFactories(*vfs); | 1630 | Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); |
| 1607 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1631 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 1608 | } | 1632 | } |
| 1609 | } | 1633 | } |
| @@ -1653,6 +1677,11 @@ void GMainWindow::OnStartGame() { | |||
| 1653 | } | 1677 | } |
| 1654 | 1678 | ||
| 1655 | void GMainWindow::OnPauseGame() { | 1679 | void GMainWindow::OnPauseGame() { |
| 1680 | Core::System& system{Core::System::GetInstance()}; | ||
| 1681 | if (system.GetExitLock() && !ConfirmForceLockedExit()) { | ||
| 1682 | return; | ||
| 1683 | } | ||
| 1684 | |||
| 1656 | emu_thread->SetRunning(false); | 1685 | emu_thread->SetRunning(false); |
| 1657 | 1686 | ||
| 1658 | ui.action_Start->setEnabled(true); | 1687 | ui.action_Start->setEnabled(true); |
| @@ -1664,6 +1693,11 @@ void GMainWindow::OnPauseGame() { | |||
| 1664 | } | 1693 | } |
| 1665 | 1694 | ||
| 1666 | void GMainWindow::OnStopGame() { | 1695 | void GMainWindow::OnStopGame() { |
| 1696 | Core::System& system{Core::System::GetInstance()}; | ||
| 1697 | if (system.GetExitLock() && !ConfirmForceLockedExit()) { | ||
| 1698 | return; | ||
| 1699 | } | ||
| 1700 | |||
| 1667 | ShutdownGame(); | 1701 | ShutdownGame(); |
| 1668 | } | 1702 | } |
| 1669 | 1703 | ||
| @@ -1988,7 +2022,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1988 | 2022 | ||
| 1989 | const auto function = [this, &keys, &pdm] { | 2023 | const auto function = [this, &keys, &pdm] { |
| 1990 | keys.PopulateFromPartitionData(pdm); | 2024 | keys.PopulateFromPartitionData(pdm); |
| 1991 | Service::FileSystem::CreateFactories(*vfs); | 2025 | Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); |
| 1992 | keys.DeriveETicket(pdm); | 2026 | keys.DeriveETicket(pdm); |
| 1993 | }; | 2027 | }; |
| 1994 | 2028 | ||
| @@ -2033,7 +2067,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 2033 | prog.close(); | 2067 | prog.close(); |
| 2034 | } | 2068 | } |
| 2035 | 2069 | ||
| 2036 | Service::FileSystem::CreateFactories(*vfs); | 2070 | Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); |
| 2037 | 2071 | ||
| 2038 | if (behavior == ReinitializeKeyBehavior::Warning) { | 2072 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 2039 | game_list->PopulateAsync(UISettings::values.game_dirs); | 2073 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| @@ -2161,13 +2195,41 @@ bool GMainWindow::ConfirmChangeGame() { | |||
| 2161 | if (emu_thread == nullptr) | 2195 | if (emu_thread == nullptr) |
| 2162 | return true; | 2196 | return true; |
| 2163 | 2197 | ||
| 2164 | auto answer = QMessageBox::question( | 2198 | const auto answer = QMessageBox::question( |
| 2165 | this, tr("yuzu"), | 2199 | this, tr("yuzu"), |
| 2166 | tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), | 2200 | tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), |
| 2167 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 2201 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); |
| 2168 | return answer != QMessageBox::No; | 2202 | return answer != QMessageBox::No; |
| 2169 | } | 2203 | } |
| 2170 | 2204 | ||
| 2205 | bool GMainWindow::ConfirmForceLockedExit() { | ||
| 2206 | if (emu_thread == nullptr) | ||
| 2207 | return true; | ||
| 2208 | |||
| 2209 | const auto answer = | ||
| 2210 | QMessageBox::question(this, tr("yuzu"), | ||
| 2211 | tr("The currently running application has requested yuzu to not " | ||
| 2212 | "exit.\n\nWould you like to bypass this and exit anyway?"), | ||
| 2213 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 2214 | return answer != QMessageBox::No; | ||
| 2215 | } | ||
| 2216 | |||
| 2217 | void GMainWindow::RequestGameExit() { | ||
| 2218 | auto& sm{Core::System::GetInstance().ServiceManager()}; | ||
| 2219 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); | ||
| 2220 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | ||
| 2221 | bool has_signalled = false; | ||
| 2222 | |||
| 2223 | if (applet_oe != nullptr) { | ||
| 2224 | applet_oe->GetMessageQueue()->RequestExit(); | ||
| 2225 | has_signalled = true; | ||
| 2226 | } | ||
| 2227 | |||
| 2228 | if (applet_ae != nullptr && !has_signalled) { | ||
| 2229 | applet_ae->GetMessageQueue()->RequestExit(); | ||
| 2230 | } | ||
| 2231 | } | ||
| 2232 | |||
| 2171 | void GMainWindow::filterBarSetChecked(bool state) { | 2233 | void GMainWindow::filterBarSetChecked(bool state) { |
| 2172 | ui.action_Show_Filter_Bar->setChecked(state); | 2234 | ui.action_Show_Filter_Bar->setChecked(state); |
| 2173 | emit(OnToggleFilterBar()); | 2235 | emit(OnToggleFilterBar()); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7d16188cb..e942d1248 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -172,6 +172,8 @@ private: | |||
| 172 | */ | 172 | */ |
| 173 | bool ConfirmClose(); | 173 | bool ConfirmClose(); |
| 174 | bool ConfirmChangeGame(); | 174 | bool ConfirmChangeGame(); |
| 175 | bool ConfirmForceLockedExit(); | ||
| 176 | void RequestGameExit(); | ||
| 175 | void closeEvent(QCloseEvent* event) override; | 177 | void closeEvent(QCloseEvent* event) override; |
| 176 | 178 | ||
| 177 | private slots: | 179 | 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() { | |||
| 316 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | 316 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, |
| 317 | sdl2_config->Get("Data Storage", "sdmc_directory", | 317 | sdl2_config->Get("Data Storage", "sdmc_directory", |
| 318 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | 318 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); |
| 319 | FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, | ||
| 320 | sdl2_config->Get("Data Storage", "load_directory", | ||
| 321 | FileUtil::GetUserPath(FileUtil::UserPath::LoadDir))); | ||
| 322 | FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, | ||
| 323 | sdl2_config->Get("Data Storage", "dump_directory", | ||
| 324 | FileUtil::GetUserPath(FileUtil::UserPath::DumpDir))); | ||
| 325 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir, | ||
| 326 | sdl2_config->Get("Data Storage", "cache_directory", | ||
| 327 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir))); | ||
| 328 | Settings::values.gamecard_inserted = | ||
| 329 | sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); | ||
| 330 | Settings::values.gamecard_current_game = | ||
| 331 | sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false); | ||
| 332 | Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); | ||
| 333 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger( | ||
| 334 | "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB))); | ||
| 335 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger( | ||
| 336 | "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB))); | ||
| 337 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 338 | sdl2_config->GetInteger("Data Storage", "nand_system_size", | ||
| 339 | static_cast<long>(Settings::NANDSystemSize::S2_5GB))); | ||
| 340 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger( | ||
| 341 | "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB))); | ||
| 319 | 342 | ||
| 320 | // System | 343 | // System |
| 321 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 344 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| @@ -374,6 +397,8 @@ void Config::ReadValues() { | |||
| 374 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); | 397 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); |
| 375 | 398 | ||
| 376 | // Debugging | 399 | // Debugging |
| 400 | Settings::values.record_frame_times = | ||
| 401 | sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||
| 377 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); | 402 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |
| 378 | Settings::values.gdbstub_port = | 403 | Settings::values.gdbstub_port = |
| 379 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | 404 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 0cfc111a6..a6171c3ed 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -173,6 +173,20 @@ volume = | |||
| 173 | # 1 (default): Yes, 0: No | 173 | # 1 (default): Yes, 0: No |
| 174 | use_virtual_sd = | 174 | use_virtual_sd = |
| 175 | 175 | ||
| 176 | # Whether or not to enable gamecard emulation | ||
| 177 | # 1: Yes, 0 (default): No | ||
| 178 | gamecard_inserted = | ||
| 179 | |||
| 180 | # Whether or not the gamecard should be emulated as the current game | ||
| 181 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 182 | # 1: Yes, 0 (default): No | ||
| 183 | gamecard_current_game = | ||
| 184 | |||
| 185 | # Path to an XCI file to use as the gamecard | ||
| 186 | # If 'gamecard_inserted' is 0 this setting is irrelevant | ||
| 187 | # If 'gamecard_current_game' is 1 this setting is irrelevant | ||
| 188 | gamecard_path = | ||
| 189 | |||
| 176 | [System] | 190 | [System] |
| 177 | # Whether the system is docked | 191 | # Whether the system is docked |
| 178 | # 1: Yes, 0 (default): No | 192 | # 1: Yes, 0 (default): No |
| @@ -213,6 +227,8 @@ region_value = | |||
| 213 | log_filter = *:Trace | 227 | log_filter = *:Trace |
| 214 | 228 | ||
| 215 | [Debugging] | 229 | [Debugging] |
| 230 | # Record frame time data, can be found in the log directory. Boolean value | ||
| 231 | record_frame_times = | ||
| 216 | # Port for listening to GDB connections. | 232 | # Port for listening to GDB connections. |
| 217 | use_gdbstub=false | 233 | use_gdbstub=false |
| 218 | gdbstub_port=24689 | 234 | 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) { | |||
| 184 | Core::System& system{Core::System::GetInstance()}; | 184 | Core::System& system{Core::System::GetInstance()}; |
| 185 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 185 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 186 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 186 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 187 | Service::FileSystem::CreateFactories(*system.GetFilesystem()); | 187 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| 188 | 188 | ||
| 189 | SCOPE_EXIT({ system.Shutdown(); }); | 189 | SCOPE_EXIT({ system.Shutdown(); }); |
| 190 | 190 | ||
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 0ee97aa54..94ad50cb3 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "common/telemetry.h" | 22 | #include "common/telemetry.h" |
| 23 | #include "core/core.h" | 23 | #include "core/core.h" |
| 24 | #include "core/crypto/key_manager.h" | 24 | #include "core/crypto/key_manager.h" |
| 25 | #include "core/file_sys/registered_cache.h" | ||
| 25 | #include "core/file_sys/vfs_real.h" | 26 | #include "core/file_sys/vfs_real.h" |
| 26 | #include "core/hle/service/filesystem/filesystem.h" | 27 | #include "core/hle/service/filesystem/filesystem.h" |
| 27 | #include "core/loader/loader.h" | 28 | #include "core/loader/loader.h" |
| @@ -216,8 +217,9 @@ int main(int argc, char** argv) { | |||
| 216 | }; | 217 | }; |
| 217 | 218 | ||
| 218 | Core::System& system{Core::System::GetInstance()}; | 219 | Core::System& system{Core::System::GetInstance()}; |
| 220 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | ||
| 219 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 221 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 220 | Service::FileSystem::CreateFactories(*system.GetFilesystem()); | 222 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| 221 | 223 | ||
| 222 | SCOPE_EXIT({ system.Shutdown(); }); | 224 | SCOPE_EXIT({ system.Shutdown(); }); |
| 223 | 225 | ||