summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/yuzu-mainline-step2.yml10
-rw-r--r--src/android/app/src/main/jni/native.cpp6
-rw-r--r--src/audio_core/device/device_session.cpp14
-rw-r--r--src/audio_core/device/device_session.h12
-rw-r--r--src/audio_core/in/audio_in_system.cpp2
-rw-r--r--src/audio_core/in/audio_in_system.h13
-rw-r--r--src/audio_core/out/audio_out_system.cpp4
-rw-r--r--src/audio_core/out/audio_out_system.h13
-rw-r--r--src/common/page_table.cpp34
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/nce/patcher.cpp83
-rw-r--r--src/core/arm/nce/patcher.h27
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/debugger/debugger.cpp39
-rw-r--r--src/core/debugger/gdbstub.cpp66
-rw-r--r--src/core/debugger/gdbstub.h15
-rw-r--r--src/core/file_sys/savedata_factory.cpp17
-rw-r--r--src/core/file_sys/savedata_factory.h10
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp34
-rw-r--r--src/core/hle/kernel/k_page_table_base.h1
-rw-r--r--src/core/hle/kernel/k_process.cpp10
-rw-r--r--src/core/hle/kernel/kernel.cpp31
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp8
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp19
-rw-r--r--src/core/hle/service/acc/profile_manager.h1
-rw-r--r--src/core/hle/service/am/am.cpp7
-rw-r--r--src/core/hle/service/audio/audin_u.cpp36
-rw-r--r--src/core/hle/service/audio/audout_u.cpp26
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp227
-rw-r--r--src/core/hle/service/filesystem/filesystem.h59
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp55
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h6
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.cpp37
-rw-r--r--src/core/hle/service/filesystem/romfs_controller.h31
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.cpp99
-rw-r--r--src/core/hle/service/filesystem/save_data_controller.h35
-rw-r--r--src/core/hle/service/glue/arp.cpp7
-rw-r--r--src/core/hle/service/hid/hid.cpp10
-rw-r--r--src/core/hle/service/pm/pm.cpp85
-rw-r--r--src/core/hle/service/server_manager.cpp9
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp89
-rw-r--r--src/core/loader/nca.cpp6
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nso.cpp41
-rw-r--r--src/core/loader/nso.h3
-rw-r--r--src/core/loader/nsp.cpp3
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp40
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp51
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h3
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp4
-rw-r--r--src/yuzu/main.cpp8
55 files changed, 929 insertions, 549 deletions
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml
index b294827f4..8bb0572f5 100644
--- a/.ci/yuzu-mainline-step2.yml
+++ b/.ci/yuzu-mainline-step2.yml
@@ -8,17 +8,7 @@ variables:
8 DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] 8 DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
9 9
10stages: 10stages:
11- stage: format
12 displayName: 'format'
13 jobs:
14 - job: format
15 displayName: 'clang'
16 pool:
17 vmImage: ubuntu-latest
18 steps:
19 - template: ./templates/format-check.yml
20- stage: build 11- stage: build
21 dependsOn: format
22 displayName: 'build' 12 displayName: 'build'
23 jobs: 13 jobs:
24 - job: build 14 - job: build
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index e436622e0..ed3b1353a 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -770,8 +770,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
770 ASSERT(user_id); 770 ASSERT(user_id);
771 771
772 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 772 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
773 EmulationSession::GetInstance().System(), vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 773 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 1,
774 FileSys::SaveDataType::SaveData, 1, user_id->AsU128(), 0); 774 user_id->AsU128(), 0);
775 775
776 const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 776 const auto full_path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
777 if (!Common::FS::CreateParentDirs(full_path)) { 777 if (!Common::FS::CreateParentDirs(full_path)) {
@@ -878,7 +878,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
878 FileSys::Mode::Read); 878 FileSys::Mode::Read);
879 879
880 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 880 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
881 system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, 881 {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
882 program_id, user_id->AsU128(), 0); 882 program_id, user_id->AsU128(), 0);
883 return ToJString(env, user_save_data_path); 883 return ToJString(env, user_save_data_path);
884} 884}
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index ee42ae529..3c214ec00 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -10,6 +10,8 @@
10#include "core/core_timing.h" 10#include "core/core_timing.h"
11#include "core/memory.h" 11#include "core/memory.h"
12 12
13#include "core/hle/kernel/k_process.h"
14
13namespace AudioCore { 15namespace AudioCore {
14 16
15using namespace std::literals; 17using namespace std::literals;
@@ -25,7 +27,7 @@ DeviceSession::~DeviceSession() {
25} 27}
26 28
27Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_, 29Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_format_,
28 u16 channel_count_, size_t session_id_, u32 handle_, 30 u16 channel_count_, size_t session_id_, Kernel::KProcess* handle_,
29 u64 applet_resource_user_id_, Sink::StreamType type_) { 31 u64 applet_resource_user_id_, Sink::StreamType type_) {
30 if (stream) { 32 if (stream) {
31 Finalize(); 33 Finalize();
@@ -36,6 +38,7 @@ Result DeviceSession::Initialize(std::string_view name_, SampleFormat sample_for
36 channel_count = channel_count_; 38 channel_count = channel_count_;
37 session_id = session_id_; 39 session_id = session_id_;
38 handle = handle_; 40 handle = handle_;
41 handle->Open();
39 applet_resource_user_id = applet_resource_user_id_; 42 applet_resource_user_id = applet_resource_user_id_;
40 43
41 if (type == Sink::StreamType::In) { 44 if (type == Sink::StreamType::In) {
@@ -54,6 +57,11 @@ void DeviceSession::Finalize() {
54 sink->CloseStream(stream); 57 sink->CloseStream(stream);
55 stream = nullptr; 58 stream = nullptr;
56 } 59 }
60
61 if (handle) {
62 handle->Close();
63 handle = nullptr;
64 }
57} 65}
58 66
59void DeviceSession::Start() { 67void DeviceSession::Start() {
@@ -91,7 +99,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
91 stream->AppendBuffer(new_buffer, tmp_samples); 99 stream->AppendBuffer(new_buffer, tmp_samples);
92 } else { 100 } else {
93 Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( 101 Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples(
94 system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16)); 102 handle->GetMemory(), buffer.samples, buffer.size / sizeof(s16));
95 stream->AppendBuffer(new_buffer, samples); 103 stream->AppendBuffer(new_buffer, samples);
96 } 104 }
97 } 105 }
@@ -100,7 +108,7 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
100void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const { 108void DeviceSession::ReleaseBuffer(const AudioBuffer& buffer) const {
101 if (type == Sink::StreamType::In) { 109 if (type == Sink::StreamType::In) {
102 auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))}; 110 auto samples{stream->ReleaseBuffer(buffer.size / sizeof(s16))};
103 system.ApplicationMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size); 111 handle->GetMemory().WriteBlockUnsafe(buffer.samples, samples.data(), buffer.size);
104 } 112 }
105} 113}
106 114
diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h
index 7d52f362d..f3fae2617 100644
--- a/src/audio_core/device/device_session.h
+++ b/src/audio_core/device/device_session.h
@@ -20,6 +20,10 @@ struct EventType;
20} // namespace Timing 20} // namespace Timing
21} // namespace Core 21} // namespace Core
22 22
23namespace Kernel {
24class KProcess;
25} // namespace Kernel
26
23namespace AudioCore { 27namespace AudioCore {
24 28
25namespace Sink { 29namespace Sink {
@@ -44,13 +48,13 @@ public:
44 * @param sample_format - Sample format for this device's output. 48 * @param sample_format - Sample format for this device's output.
45 * @param channel_count - Number of channels for this device (2 or 6). 49 * @param channel_count - Number of channels for this device (2 or 6).
46 * @param session_id - This session's id. 50 * @param session_id - This session's id.
47 * @param handle - Handle for this device session (unused). 51 * @param handle - Process handle for this device session.
48 * @param applet_resource_user_id - Applet resource user id for this device session (unused). 52 * @param applet_resource_user_id - Applet resource user id for this device session (unused).
49 * @param type - Type of this stream (Render, In, Out). 53 * @param type - Type of this stream (Render, In, Out).
50 * @return Result code for this call. 54 * @return Result code for this call.
51 */ 55 */
52 Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count, 56 Result Initialize(std::string_view name, SampleFormat sample_format, u16 channel_count,
53 size_t session_id, u32 handle, u64 applet_resource_user_id, 57 size_t session_id, Kernel::KProcess* handle, u64 applet_resource_user_id,
54 Sink::StreamType type); 58 Sink::StreamType type);
55 59
56 /** 60 /**
@@ -137,8 +141,8 @@ private:
137 u16 channel_count{}; 141 u16 channel_count{};
138 /// Session id of this device session 142 /// Session id of this device session
139 size_t session_id{}; 143 size_t session_id{};
140 /// Handle of this device session 144 /// Process handle of device memory owner
141 u32 handle{}; 145 Kernel::KProcess* handle{};
142 /// Applet resource user id of this device session 146 /// Applet resource user id of this device session
143 u64 applet_resource_user_id{}; 147 u64 applet_resource_user_id{};
144 /// Total number of samples played by this device session 148 /// Total number of samples played by this device session
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index 579129121..b2dd3ef9f 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -57,7 +57,7 @@ Result System::IsConfigValid(const std::string_view device_name,
57} 57}
58 58
59Result System::Initialize(std::string device_name, const AudioInParameter& in_params, 59Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
60 const u32 handle_, const u64 applet_resource_user_id_) { 60 Kernel::KProcess* handle_, const u64 applet_resource_user_id_) {
61 auto result{IsConfigValid(device_name, in_params)}; 61 auto result{IsConfigValid(device_name, in_params)};
62 if (result.IsError()) { 62 if (result.IsError()) {
63 return result; 63 return result;
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
index 1c5154638..ee048190c 100644
--- a/src/audio_core/in/audio_in_system.h
+++ b/src/audio_core/in/audio_in_system.h
@@ -19,7 +19,8 @@ class System;
19 19
20namespace Kernel { 20namespace Kernel {
21class KEvent; 21class KEvent;
22} 22class KProcess;
23} // namespace Kernel
23 24
24namespace AudioCore::AudioIn { 25namespace AudioCore::AudioIn {
25 26
@@ -93,12 +94,12 @@ public:
93 * 94 *
94 * @param device_name - The name of the requested input device. 95 * @param device_name - The name of the requested input device.
95 * @param in_params - Input parameters, see AudioInParameter. 96 * @param in_params - Input parameters, see AudioInParameter.
96 * @param handle - Unused. 97 * @param handle - Process handle.
97 * @param applet_resource_user_id - Unused. 98 * @param applet_resource_user_id - Unused.
98 * @return Result code. 99 * @return Result code.
99 */ 100 */
100 Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle, 101 Result Initialize(std::string device_name, const AudioInParameter& in_params,
101 u64 applet_resource_user_id); 102 Kernel::KProcess* handle, u64 applet_resource_user_id);
102 103
103 /** 104 /**
104 * Start this system. 105 * Start this system.
@@ -244,8 +245,8 @@ public:
244private: 245private:
245 /// Core system 246 /// Core system
246 Core::System& system; 247 Core::System& system;
247 /// (Unused) 248 /// Process handle
248 u32 handle{}; 249 Kernel::KProcess* handle{};
249 /// (Unused) 250 /// (Unused)
250 u64 applet_resource_user_id{}; 251 u64 applet_resource_user_id{};
251 /// Buffer event, signalled when a buffer is ready 252 /// Buffer event, signalled when a buffer is ready
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index 0adf64bd3..7b3ff4e88 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -48,8 +48,8 @@ Result System::IsConfigValid(std::string_view device_name,
48 return Service::Audio::ResultInvalidChannelCount; 48 return Service::Audio::ResultInvalidChannelCount;
49} 49}
50 50
51Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_, 51Result System::Initialize(std::string device_name, const AudioOutParameter& in_params,
52 u64 applet_resource_user_id_) { 52 Kernel::KProcess* handle_, u64 applet_resource_user_id_) {
53 auto result = IsConfigValid(device_name, in_params); 53 auto result = IsConfigValid(device_name, in_params);
54 if (result.IsError()) { 54 if (result.IsError()) {
55 return result; 55 return result;
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
index b95cb91be..82aada185 100644
--- a/src/audio_core/out/audio_out_system.h
+++ b/src/audio_core/out/audio_out_system.h
@@ -19,7 +19,8 @@ class System;
19 19
20namespace Kernel { 20namespace Kernel {
21class KEvent; 21class KEvent;
22} 22class KProcess;
23} // namespace Kernel
23 24
24namespace AudioCore::AudioOut { 25namespace AudioCore::AudioOut {
25 26
@@ -84,12 +85,12 @@ public:
84 * 85 *
85 * @param device_name - The name of the requested output device. 86 * @param device_name - The name of the requested output device.
86 * @param in_params - Input parameters, see AudioOutParameter. 87 * @param in_params - Input parameters, see AudioOutParameter.
87 * @param handle - Unused. 88 * @param handle - Process handle.
88 * @param applet_resource_user_id - Unused. 89 * @param applet_resource_user_id - Unused.
89 * @return Result code. 90 * @return Result code.
90 */ 91 */
91 Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle, 92 Result Initialize(std::string device_name, const AudioOutParameter& in_params,
92 u64 applet_resource_user_id); 93 Kernel::KProcess* handle, u64 applet_resource_user_id);
93 94
94 /** 95 /**
95 * Start this system. 96 * Start this system.
@@ -228,8 +229,8 @@ public:
228private: 229private:
229 /// Core system 230 /// Core system
230 Core::System& system; 231 Core::System& system;
231 /// (Unused) 232 /// Process handle
232 u32 handle{}; 233 Kernel::KProcess* handle{};
233 /// (Unused) 234 /// (Unused)
234 u64 applet_resource_user_id{}; 235 u64 applet_resource_user_id{};
235 /// Buffer event, signalled when a buffer is ready 236 /// Buffer event, signalled when a buffer is ready
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index 166dc3dce..85dc18c11 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/page_table.h" 4#include "common/page_table.h"
5#include "common/scope_exit.h"
5 6
6namespace Common { 7namespace Common {
7 8
@@ -11,29 +12,10 @@ PageTable::~PageTable() noexcept = default;
11 12
12bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context, 13bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
13 Common::ProcessAddress address) const { 14 Common::ProcessAddress address) const {
14 // Setup invalid defaults. 15 out_context->next_offset = GetInteger(address);
15 out_entry->phys_addr = 0; 16 out_context->next_page = address / page_size;
16 out_entry->block_size = page_size;
17 out_context->next_page = 0;
18
19 // Validate that we can read the actual entry.
20 const auto page = address / page_size;
21 if (page >= backing_addr.size()) {
22 return false;
23 }
24
25 // Validate that the entry is mapped.
26 const auto phys_addr = backing_addr[page];
27 if (phys_addr == 0) {
28 return false;
29 }
30 17
31 // Populate the results. 18 return this->ContinueTraversal(out_entry, out_context);
32 out_entry->phys_addr = phys_addr + GetInteger(address);
33 out_context->next_page = page + 1;
34 out_context->next_offset = GetInteger(address) + page_size;
35
36 return true;
37} 19}
38 20
39bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const { 21bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
@@ -41,6 +23,12 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
41 out_entry->phys_addr = 0; 23 out_entry->phys_addr = 0;
42 out_entry->block_size = page_size; 24 out_entry->block_size = page_size;
43 25
26 // Regardless of whether the page was mapped, advance on exit.
27 SCOPE_EXIT({
28 context->next_page += 1;
29 context->next_offset += page_size;
30 });
31
44 // Validate that we can read the actual entry. 32 // Validate that we can read the actual entry.
45 const auto page = context->next_page; 33 const auto page = context->next_page;
46 if (page >= backing_addr.size()) { 34 if (page >= backing_addr.size()) {
@@ -55,8 +43,6 @@ bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* c
55 43
56 // Populate the results. 44 // Populate the results.
57 out_entry->phys_addr = phys_addr + context->next_offset; 45 out_entry->phys_addr = phys_addr + context->next_offset;
58 context->next_page = page + 1;
59 context->next_offset += page_size;
60 46
61 return true; 47 return true;
62} 48}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 753f55ebe..293d9647b 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -490,6 +490,10 @@ add_library(core STATIC
490 hle/service/filesystem/fsp_pr.h 490 hle/service/filesystem/fsp_pr.h
491 hle/service/filesystem/fsp_srv.cpp 491 hle/service/filesystem/fsp_srv.cpp
492 hle/service/filesystem/fsp_srv.h 492 hle/service/filesystem/fsp_srv.h
493 hle/service/filesystem/romfs_controller.cpp
494 hle/service/filesystem/romfs_controller.h
495 hle/service/filesystem/save_data_controller.cpp
496 hle/service/filesystem/save_data_controller.h
493 hle/service/fgm/fgm.cpp 497 hle/service/fgm/fgm.cpp
494 hle/service/fgm/fgm.h 498 hle/service/fgm/fgm.h
495 hle/service/friend/friend.cpp 499 hle/service/friend/friend.cpp
diff --git a/src/core/arm/nce/patcher.cpp b/src/core/arm/nce/patcher.cpp
index 47a7a8880..c7285e3a0 100644
--- a/src/core/arm/nce/patcher.cpp
+++ b/src/core/arm/nce/patcher.cpp
@@ -22,14 +22,10 @@ using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
22constexpr size_t MaxRelativeBranch = 128_MiB; 22constexpr size_t MaxRelativeBranch = 128_MiB;
23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32); 23constexpr u32 ModuleCodeIndex = 0x24 / sizeof(u32);
24 24
25Patcher::Patcher() : c(m_patch_instructions) {} 25Patcher::Patcher() : c(m_patch_instructions) {
26 26 // The first word of the patch section is always a branch to the first instruction of the
27Patcher::~Patcher() = default; 27 // module.
28 28 c.dw(0);
29void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
30 const Kernel::CodeSet::Segment& code) {
31 // Branch to the first instruction of the module.
32 this->BranchToModule(0);
33 29
34 // Write save context helper function. 30 // Write save context helper function.
35 c.l(m_save_context); 31 c.l(m_save_context);
@@ -38,6 +34,25 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
38 // Write load context helper function. 34 // Write load context helper function.
39 c.l(m_load_context); 35 c.l(m_load_context);
40 WriteLoadContext(); 36 WriteLoadContext();
37}
38
39Patcher::~Patcher() = default;
40
41bool Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
42 const Kernel::CodeSet::Segment& code) {
43 // If we have patched modules but cannot reach the new module, then it needs its own patcher.
44 const size_t image_size = program_image.size();
45 if (total_program_size + image_size > MaxRelativeBranch && total_program_size > 0) {
46 return false;
47 }
48
49 // Add a new module patch to our list
50 modules.emplace_back();
51 curr_patch = &modules.back();
52
53 // The first word of the patch section is always a branch to the first instruction of the
54 // module.
55 curr_patch->m_branch_to_module_relocations.push_back({0, 0});
41 56
42 // Retrieve text segment data. 57 // Retrieve text segment data.
43 const auto text = std::span{program_image}.subspan(code.offset, code.size); 58 const auto text = std::span{program_image}.subspan(code.offset, code.size);
@@ -94,16 +109,17 @@ void Patcher::PatchText(const Kernel::PhysicalMemory& program_image,
94 } 109 }
95 110
96 if (auto exclusive = Exclusive{inst}; exclusive.Verify()) { 111 if (auto exclusive = Exclusive{inst}; exclusive.Verify()) {
97 m_exclusives.push_back(i); 112 curr_patch->m_exclusives.push_back(i);
98 } 113 }
99 } 114 }
100 115
101 // Determine patching mode for the final relocation step 116 // Determine patching mode for the final relocation step
102 const size_t image_size = program_image.size(); 117 total_program_size += image_size;
103 this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData; 118 this->mode = image_size > MaxRelativeBranch ? PatchMode::PreText : PatchMode::PostData;
119 return true;
104} 120}
105 121
106void Patcher::RelocateAndCopy(Common::ProcessAddress load_base, 122bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
107 const Kernel::CodeSet::Segment& code, 123 const Kernel::CodeSet::Segment& code,
108 Kernel::PhysicalMemory& program_image, 124 Kernel::PhysicalMemory& program_image,
109 EntryTrampolines* out_trampolines) { 125 EntryTrampolines* out_trampolines) {
@@ -120,7 +136,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
120 if (mode == PatchMode::PreText) { 136 if (mode == PatchMode::PreText) {
121 rc.B(rel.patch_offset - patch_size - rel.module_offset); 137 rc.B(rel.patch_offset - patch_size - rel.module_offset);
122 } else { 138 } else {
123 rc.B(image_size - rel.module_offset + rel.patch_offset); 139 rc.B(total_program_size - rel.module_offset + rel.patch_offset);
124 } 140 }
125 }; 141 };
126 142
@@ -129,7 +145,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
129 if (mode == PatchMode::PreText) { 145 if (mode == PatchMode::PreText) {
130 rc.B(patch_size - rel.patch_offset + rel.module_offset); 146 rc.B(patch_size - rel.patch_offset + rel.module_offset);
131 } else { 147 } else {
132 rc.B(rel.module_offset - image_size - rel.patch_offset); 148 rc.B(rel.module_offset - total_program_size - rel.patch_offset);
133 } 149 }
134 }; 150 };
135 151
@@ -137,7 +153,7 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
137 if (mode == PatchMode::PreText) { 153 if (mode == PatchMode::PreText) {
138 return GetInteger(load_base) + patch_offset; 154 return GetInteger(load_base) + patch_offset;
139 } else { 155 } else {
140 return GetInteger(load_base) + image_size + patch_offset; 156 return GetInteger(load_base) + total_program_size + patch_offset;
141 } 157 }
142 }; 158 };
143 159
@@ -150,39 +166,50 @@ void Patcher::RelocateAndCopy(Common::ProcessAddress load_base,
150 }; 166 };
151 167
152 // We are now ready to relocate! 168 // We are now ready to relocate!
153 for (const Relocation& rel : m_branch_to_patch_relocations) { 169 auto& patch = modules[m_relocate_module_index++];
170 for (const Relocation& rel : patch.m_branch_to_patch_relocations) {
154 ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel); 171 ApplyBranchToPatchRelocation(text_words.data() + rel.module_offset / sizeof(u32), rel);
155 } 172 }
156 for (const Relocation& rel : m_branch_to_module_relocations) { 173 for (const Relocation& rel : patch.m_branch_to_module_relocations) {
157 ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32), 174 ApplyBranchToModuleRelocation(m_patch_instructions.data() + rel.patch_offset / sizeof(u32),
158 rel); 175 rel);
159 } 176 }
160 177
161 // Rewrite PC constants and record post trampolines 178 // Rewrite PC constants and record post trampolines
162 for (const Relocation& rel : m_write_module_pc_relocations) { 179 for (const Relocation& rel : patch.m_write_module_pc_relocations) {
163 oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)}; 180 oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
164 rc.dx(RebasePc(rel.module_offset)); 181 rc.dx(RebasePc(rel.module_offset));
165 } 182 }
166 for (const Trampoline& rel : m_trampolines) { 183 for (const Trampoline& rel : patch.m_trampolines) {
167 out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)}); 184 out_trampolines->insert({RebasePc(rel.module_offset), RebasePatch(rel.patch_offset)});
168 } 185 }
169 186
170 // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not. 187 // Cortex-A57 seems to treat all exclusives as ordered, but newer processors do not.
171 // Convert to ordered to preserve this assumption. 188 // Convert to ordered to preserve this assumption.
172 for (const ModuleTextAddress i : m_exclusives) { 189 for (const ModuleTextAddress i : patch.m_exclusives) {
173 auto exclusive = Exclusive{text_words[i]}; 190 auto exclusive = Exclusive{text_words[i]};
174 text_words[i] = exclusive.AsOrdered(); 191 text_words[i] = exclusive.AsOrdered();
175 } 192 }
176 193
177 // Copy to program image 194 // Remove the patched module size from the total. This is done so total_program_size
178 if (this->mode == PatchMode::PreText) { 195 // always represents the distance from the currently patched module to the patch section.
179 std::memcpy(program_image.data(), m_patch_instructions.data(), 196 total_program_size -= image_size;
180 m_patch_instructions.size() * sizeof(u32)); 197
181 } else { 198 // Only copy to the program image of the last module
182 program_image.resize(image_size + patch_size); 199 if (m_relocate_module_index == modules.size()) {
183 std::memcpy(program_image.data() + image_size, m_patch_instructions.data(), 200 if (this->mode == PatchMode::PreText) {
184 m_patch_instructions.size() * sizeof(u32)); 201 ASSERT(image_size == total_program_size);
202 std::memcpy(program_image.data(), m_patch_instructions.data(),
203 m_patch_instructions.size() * sizeof(u32));
204 } else {
205 program_image.resize(image_size + patch_size);
206 std::memcpy(program_image.data() + image_size, m_patch_instructions.data(),
207 m_patch_instructions.size() * sizeof(u32));
208 }
209 return true;
185 } 210 }
211
212 return false;
186} 213}
187 214
188size_t Patcher::GetSectionSize() const noexcept { 215size_t Patcher::GetSectionSize() const noexcept {
@@ -322,7 +349,7 @@ void Patcher::WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) {
322 349
323 // Write the post-SVC trampoline address, which will jump back to the guest after restoring its 350 // Write the post-SVC trampoline address, which will jump back to the guest after restoring its
324 // state. 351 // state.
325 m_trampolines.push_back({c.offset(), module_dest}); 352 curr_patch->m_trampolines.push_back({c.offset(), module_dest});
326 353
327 // Host called this location. Save the return address so we can 354 // Host called this location. Save the return address so we can
328 // unwind the stack properly when jumping back. 355 // unwind the stack properly when jumping back.
diff --git a/src/core/arm/nce/patcher.h b/src/core/arm/nce/patcher.h
index c6d1608c1..a44f385e2 100644
--- a/src/core/arm/nce/patcher.h
+++ b/src/core/arm/nce/patcher.h
@@ -31,9 +31,9 @@ public:
31 explicit Patcher(); 31 explicit Patcher();
32 ~Patcher(); 32 ~Patcher();
33 33
34 void PatchText(const Kernel::PhysicalMemory& program_image, 34 bool PatchText(const Kernel::PhysicalMemory& program_image,
35 const Kernel::CodeSet::Segment& code); 35 const Kernel::CodeSet::Segment& code);
36 void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, 36 bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
37 Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines); 37 Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
38 size_t GetSectionSize() const noexcept; 38 size_t GetSectionSize() const noexcept;
39 39
@@ -61,16 +61,16 @@ private:
61 61
62private: 62private:
63 void BranchToPatch(uintptr_t module_dest) { 63 void BranchToPatch(uintptr_t module_dest) {
64 m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); 64 curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
65 } 65 }
66 66
67 void BranchToModule(uintptr_t module_dest) { 67 void BranchToModule(uintptr_t module_dest) {
68 m_branch_to_module_relocations.push_back({c.offset(), module_dest}); 68 curr_patch->m_branch_to_module_relocations.push_back({c.offset(), module_dest});
69 c.dw(0); 69 c.dw(0);
70 } 70 }
71 71
72 void WriteModulePc(uintptr_t module_dest) { 72 void WriteModulePc(uintptr_t module_dest) {
73 m_write_module_pc_relocations.push_back({c.offset(), module_dest}); 73 curr_patch->m_write_module_pc_relocations.push_back({c.offset(), module_dest});
74 c.dx(0); 74 c.dx(0);
75 } 75 }
76 76
@@ -84,15 +84,22 @@ private:
84 uintptr_t module_offset; ///< Offset in bytes from the start of the text section. 84 uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
85 }; 85 };
86 86
87 struct ModulePatch {
88 std::vector<Trampoline> m_trampolines;
89 std::vector<Relocation> m_branch_to_patch_relocations{};
90 std::vector<Relocation> m_branch_to_module_relocations{};
91 std::vector<Relocation> m_write_module_pc_relocations{};
92 std::vector<ModuleTextAddress> m_exclusives{};
93 };
94
87 oaknut::VectorCodeGenerator c; 95 oaknut::VectorCodeGenerator c;
88 std::vector<Trampoline> m_trampolines;
89 std::vector<Relocation> m_branch_to_patch_relocations{};
90 std::vector<Relocation> m_branch_to_module_relocations{};
91 std::vector<Relocation> m_write_module_pc_relocations{};
92 std::vector<ModuleTextAddress> m_exclusives{};
93 oaknut::Label m_save_context{}; 96 oaknut::Label m_save_context{};
94 oaknut::Label m_load_context{}; 97 oaknut::Label m_load_context{};
95 PatchMode mode{PatchMode::None}; 98 PatchMode mode{PatchMode::None};
99 size_t total_program_size{};
100 size_t m_relocate_module_index{};
101 std::vector<ModulePatch> modules;
102 ModulePatch* curr_patch;
96}; 103};
97 104
98} // namespace Core::NCE 105} // namespace Core::NCE
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c063f7719..461eea9c8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -413,6 +413,7 @@ struct System::Impl {
413 kernel.ShutdownCores(); 413 kernel.ShutdownCores();
414 services.reset(); 414 services.reset();
415 service_manager.reset(); 415 service_manager.reset();
416 fs_controller.Reset();
416 cheat_engine.reset(); 417 cheat_engine.reset();
417 telemetry_session.reset(); 418 telemetry_session.reset();
418 time_manager.Shutdown(); 419 time_manager.Shutdown();
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 0e270eb50..e86aae846 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -114,7 +114,7 @@ public:
114 } 114 }
115 115
116 Kernel::KThread* GetActiveThread() override { 116 Kernel::KThread* GetActiveThread() override {
117 return state->active_thread; 117 return state->active_thread.GetPointerUnsafe();
118 } 118 }
119 119
120private: 120private:
@@ -147,11 +147,14 @@ private:
147 147
148 std::scoped_lock lk{connection_lock}; 148 std::scoped_lock lk{connection_lock};
149 149
150 // Find the process we are going to debug.
151 SetDebugProcess();
152
150 // Ensure everything is stopped. 153 // Ensure everything is stopped.
151 PauseEmulation(); 154 PauseEmulation();
152 155
153 // Set up the new frontend. 156 // Set up the new frontend.
154 frontend = std::make_unique<GDBStub>(*this, system); 157 frontend = std::make_unique<GDBStub>(*this, system, debug_process.GetPointerUnsafe());
155 158
156 // Set the new state. This will tear down any existing state. 159 // Set the new state. This will tear down any existing state.
157 state = ConnectionState{ 160 state = ConnectionState{
@@ -194,15 +197,20 @@ private:
194 UpdateActiveThread(); 197 UpdateActiveThread();
195 198
196 if (state->info.type == SignalType::Watchpoint) { 199 if (state->info.type == SignalType::Watchpoint) {
197 frontend->Watchpoint(state->active_thread, *state->info.watchpoint); 200 frontend->Watchpoint(std::addressof(*state->active_thread),
201 *state->info.watchpoint);
198 } else { 202 } else {
199 frontend->Stopped(state->active_thread); 203 frontend->Stopped(std::addressof(*state->active_thread));
200 } 204 }
201 205
202 break; 206 break;
203 case SignalType::ShuttingDown: 207 case SignalType::ShuttingDown:
204 frontend->ShuttingDown(); 208 frontend->ShuttingDown();
205 209
210 // Release members.
211 state->active_thread.Reset(nullptr);
212 debug_process.Reset(nullptr);
213
206 // Wait for emulation to shut down gracefully now. 214 // Wait for emulation to shut down gracefully now.
207 state->signal_pipe.close(); 215 state->signal_pipe.close();
208 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); 216 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
@@ -222,7 +230,7 @@ private:
222 stopped = true; 230 stopped = true;
223 PauseEmulation(); 231 PauseEmulation();
224 UpdateActiveThread(); 232 UpdateActiveThread();
225 frontend->Stopped(state->active_thread); 233 frontend->Stopped(state->active_thread.GetPointerUnsafe());
226 break; 234 break;
227 } 235 }
228 case DebuggerAction::Continue: 236 case DebuggerAction::Continue:
@@ -232,7 +240,7 @@ private:
232 MarkResumed([&] { 240 MarkResumed([&] {
233 state->active_thread->SetStepState(Kernel::StepState::StepPending); 241 state->active_thread->SetStepState(Kernel::StepState::StepPending);
234 state->active_thread->Resume(Kernel::SuspendType::Debug); 242 state->active_thread->Resume(Kernel::SuspendType::Debug);
235 ResumeEmulation(state->active_thread); 243 ResumeEmulation(state->active_thread.GetPointerUnsafe());
236 }); 244 });
237 break; 245 break;
238 case DebuggerAction::StepThreadLocked: { 246 case DebuggerAction::StepThreadLocked: {
@@ -255,6 +263,7 @@ private:
255 } 263 }
256 264
257 void PauseEmulation() { 265 void PauseEmulation() {
266 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
258 Kernel::KScopedSchedulerLock sl{system.Kernel()}; 267 Kernel::KScopedSchedulerLock sl{system.Kernel()};
259 268
260 // Put all threads to sleep on next scheduler round. 269 // Put all threads to sleep on next scheduler round.
@@ -264,6 +273,9 @@ private:
264 } 273 }
265 274
266 void ResumeEmulation(Kernel::KThread* except = nullptr) { 275 void ResumeEmulation(Kernel::KThread* except = nullptr) {
276 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
277 Kernel::KScopedSchedulerLock sl{system.Kernel()};
278
267 // Wake up all threads. 279 // Wake up all threads.
268 for (auto& thread : ThreadList()) { 280 for (auto& thread : ThreadList()) {
269 if (std::addressof(thread) == except) { 281 if (std::addressof(thread) == except) {
@@ -277,15 +289,16 @@ private:
277 289
278 template <typename Callback> 290 template <typename Callback>
279 void MarkResumed(Callback&& cb) { 291 void MarkResumed(Callback&& cb) {
280 Kernel::KScopedSchedulerLock sl{system.Kernel()};
281 stopped = false; 292 stopped = false;
282 cb(); 293 cb();
283 } 294 }
284 295
285 void UpdateActiveThread() { 296 void UpdateActiveThread() {
297 Kernel::KScopedLightLock ll{debug_process->GetListLock()};
298
286 auto& threads{ThreadList()}; 299 auto& threads{ThreadList()};
287 for (auto& thread : threads) { 300 for (auto& thread : threads) {
288 if (std::addressof(thread) == state->active_thread) { 301 if (std::addressof(thread) == state->active_thread.GetPointerUnsafe()) {
289 // Thread is still alive, no need to update. 302 // Thread is still alive, no need to update.
290 return; 303 return;
291 } 304 }
@@ -293,12 +306,18 @@ private:
293 state->active_thread = std::addressof(threads.front()); 306 state->active_thread = std::addressof(threads.front());
294 } 307 }
295 308
309private:
310 void SetDebugProcess() {
311 debug_process = std::move(system.Kernel().GetProcessList().back());
312 }
313
296 Kernel::KProcess::ThreadList& ThreadList() { 314 Kernel::KProcess::ThreadList& ThreadList() {
297 return system.ApplicationProcess()->GetThreadList(); 315 return debug_process->GetThreadList();
298 } 316 }
299 317
300private: 318private:
301 System& system; 319 System& system;
320 Kernel::KScopedAutoObject<Kernel::KProcess> debug_process;
302 std::unique_ptr<DebuggerFrontend> frontend; 321 std::unique_ptr<DebuggerFrontend> frontend;
303 322
304 boost::asio::io_context io_context; 323 boost::asio::io_context io_context;
@@ -310,7 +329,7 @@ private:
310 boost::process::async_pipe signal_pipe; 329 boost::process::async_pipe signal_pipe;
311 330
312 SignalInfo info; 331 SignalInfo info;
313 Kernel::KThread* active_thread; 332 Kernel::KScopedAutoObject<Kernel::KThread> active_thread;
314 std::array<u8, 4096> client_data; 333 std::array<u8, 4096> client_data;
315 bool pipe_data; 334 bool pipe_data;
316 }; 335 };
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 4051ed4af..80091cc7e 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) {
108 return escaped; 108 return escaped;
109} 109}
110 110
111GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) 111GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
112 : DebuggerFrontend(backend_), system{system_} { 112 : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
113 if (system.ApplicationProcess()->Is64Bit()) { 113 if (GetProcess()->Is64Bit()) {
114 arch = std::make_unique<GDBStubA64>(); 114 arch = std::make_unique<GDBStubA64>();
115 } else { 115 } else {
116 arch = std::make_unique<GDBStubA32>(); 116 arch = std::make_unique<GDBStubA32>();
@@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
276 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; 276 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
277 277
278 std::vector<u8> mem(size); 278 std::vector<u8> mem(size);
279 if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) { 279 if (GetMemory().ReadBlock(addr, mem.data(), size)) {
280 // Restore any bytes belonging to replaced instructions. 280 // Restore any bytes belonging to replaced instructions.
281 auto it = replaced_instructions.lower_bound(addr); 281 auto it = replaced_instructions.lower_bound(addr);
282 for (; it != replaced_instructions.end() && it->first < addr + size; it++) { 282 for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
@@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
310 const auto mem_substr{std::string_view(command).substr(mem_sep)}; 310 const auto mem_substr{std::string_view(command).substr(mem_sep)};
311 const auto mem{Common::HexStringToVector(mem_substr, false)}; 311 const auto mem{Common::HexStringToVector(mem_substr, false)};
312 312
313 if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) { 313 if (GetMemory().WriteBlock(addr, mem.data(), size)) {
314 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size); 314 Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
315 SendReply(GDB_STUB_REPLY_OK); 315 SendReply(GDB_STUB_REPLY_OK);
316 } else { 316 } else {
317 SendReply(GDB_STUB_REPLY_ERR); 317 SendReply(GDB_STUB_REPLY_ERR);
@@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
353 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 353 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
354 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; 354 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
355 355
356 if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { 356 if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
357 SendReply(GDB_STUB_REPLY_ERR); 357 SendReply(GDB_STUB_REPLY_ERR);
358 return; 358 return;
359 } 359 }
@@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
362 362
363 switch (type) { 363 switch (type) {
364 case BreakpointType::Software: 364 case BreakpointType::Software:
365 replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); 365 replaced_instructions[addr] = GetMemory().Read32(addr);
366 system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); 366 GetMemory().Write32(addr, arch->BreakpointInstruction());
367 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); 367 Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
368 success = true; 368 success = true;
369 break; 369 break;
370 case BreakpointType::WriteWatch: 370 case BreakpointType::WriteWatch:
371 success = system.ApplicationProcess()->InsertWatchpoint(addr, size, 371 success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
372 Kernel::DebugWatchpointType::Write);
373 break; 372 break;
374 case BreakpointType::ReadWatch: 373 case BreakpointType::ReadWatch:
375 success = system.ApplicationProcess()->InsertWatchpoint(addr, size, 374 success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
376 Kernel::DebugWatchpointType::Read);
377 break; 375 break;
378 case BreakpointType::AccessWatch: 376 case BreakpointType::AccessWatch:
379 success = system.ApplicationProcess()->InsertWatchpoint( 377 success =
380 addr, size, Kernel::DebugWatchpointType::ReadOrWrite); 378 GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
381 break; 379 break;
382 case BreakpointType::Hardware: 380 case BreakpointType::Hardware:
383 default: 381 default:
@@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
400 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 398 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
401 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; 399 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
402 400
403 if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) { 401 if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
404 SendReply(GDB_STUB_REPLY_ERR); 402 SendReply(GDB_STUB_REPLY_ERR);
405 return; 403 return;
406 } 404 }
@@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
411 case BreakpointType::Software: { 409 case BreakpointType::Software: {
412 const auto orig_insn{replaced_instructions.find(addr)}; 410 const auto orig_insn{replaced_instructions.find(addr)};
413 if (orig_insn != replaced_instructions.end()) { 411 if (orig_insn != replaced_instructions.end()) {
414 system.ApplicationMemory().Write32(addr, orig_insn->second); 412 GetMemory().Write32(addr, orig_insn->second);
415 Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); 413 Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
416 replaced_instructions.erase(addr); 414 replaced_instructions.erase(addr);
417 success = true; 415 success = true;
418 } 416 }
419 break; 417 break;
420 } 418 }
421 case BreakpointType::WriteWatch: 419 case BreakpointType::WriteWatch:
422 success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, 420 success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
423 Kernel::DebugWatchpointType::Write);
424 break; 421 break;
425 case BreakpointType::ReadWatch: 422 case BreakpointType::ReadWatch:
426 success = system.ApplicationProcess()->RemoveWatchpoint(addr, size, 423 success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
427 Kernel::DebugWatchpointType::Read);
428 break; 424 break;
429 case BreakpointType::AccessWatch: 425 case BreakpointType::AccessWatch:
430 success = system.ApplicationProcess()->RemoveWatchpoint( 426 success =
431 addr, size, Kernel::DebugWatchpointType::ReadOrWrite); 427 GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
432 break; 428 break;
433 case BreakpointType::Hardware: 429 case BreakpointType::Hardware:
434 default: 430 default:
@@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) {
466 const auto target_xml{arch->GetTargetXML()}; 462 const auto target_xml{arch->GetTargetXML()};
467 SendReply(PaginateBuffer(target_xml, command.substr(30))); 463 SendReply(PaginateBuffer(target_xml, command.substr(30)));
468 } else if (command.starts_with("Offsets")) { 464 } else if (command.starts_with("Offsets")) {
469 const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess()); 465 const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
470 SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); 466 SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
471 } else if (command.starts_with("Xfer:libraries:read::")) { 467 } else if (command.starts_with("Xfer:libraries:read::")) {
472 auto modules = Core::FindModules(system.ApplicationProcess()); 468 auto modules = Core::FindModules(GetProcess());
473 469
474 std::string buffer; 470 std::string buffer;
475 buffer += R"(<?xml version="1.0"?>)"; 471 buffer += R"(<?xml version="1.0"?>)";
@@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) {
483 SendReply(PaginateBuffer(buffer, command.substr(21))); 479 SendReply(PaginateBuffer(buffer, command.substr(21)));
484 } else if (command.starts_with("fThreadInfo")) { 480 } else if (command.starts_with("fThreadInfo")) {
485 // beginning of list 481 // beginning of list
486 const auto& threads = system.ApplicationProcess()->GetThreadList(); 482 const auto& threads = GetProcess()->GetThreadList();
487 std::vector<std::string> thread_ids; 483 std::vector<std::string> thread_ids;
488 for (const auto& thread : threads) { 484 for (const auto& thread : threads) {
489 thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId())); 485 thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
@@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) {
497 buffer += R"(<?xml version="1.0"?>)"; 493 buffer += R"(<?xml version="1.0"?>)";
498 buffer += "<threads>"; 494 buffer += "<threads>";
499 495
500 const auto& threads = system.ApplicationProcess()->GetThreadList(); 496 const auto& threads = GetProcess()->GetThreadList();
501 for (const auto& thread : threads) { 497 for (const auto& thread : threads) {
502 auto thread_name{Core::GetThreadName(&thread)}; 498 auto thread_name{Core::GetThreadName(&thread)};
503 if (!thread_name) { 499 if (!thread_name) {
@@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
613 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; 609 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
614 std::string reply; 610 std::string reply;
615 611
616 auto* process = system.ApplicationProcess(); 612 auto* process = GetProcess();
617 auto& page_table = process->GetPageTable(); 613 auto& page_table = process->GetPageTable();
618 614
619 const char* commands = "Commands:\n" 615 const char* commands = "Commands:\n"
@@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
714} 710}
715 711
716Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 712Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
717 auto& threads{system.ApplicationProcess()->GetThreadList()}; 713 auto& threads{GetProcess()->GetThreadList()};
718 for (auto& thread : threads) { 714 for (auto& thread : threads) {
719 if (thread.GetThreadId() == thread_id) { 715 if (thread.GetThreadId() == thread_id) {
720 return std::addressof(thread); 716 return std::addressof(thread);
@@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) {
783 backend.WriteToClient(buf); 779 backend.WriteToClient(buf);
784} 780}
785 781
782Kernel::KProcess* GDBStub::GetProcess() {
783 return debug_process;
784}
785
786Core::Memory::Memory& GDBStub::GetMemory() {
787 return GetProcess()->GetMemory();
788}
789
786} // namespace Core 790} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 368197920..232dcf49f 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -12,13 +12,22 @@
12#include "core/debugger/debugger_interface.h" 12#include "core/debugger/debugger_interface.h"
13#include "core/debugger/gdbstub_arch.h" 13#include "core/debugger/gdbstub_arch.h"
14 14
15namespace Kernel {
16class KProcess;
17}
18
19namespace Core::Memory {
20class Memory;
21}
22
15namespace Core { 23namespace Core {
16 24
17class System; 25class System;
18 26
19class GDBStub : public DebuggerFrontend { 27class GDBStub : public DebuggerFrontend {
20public: 28public:
21 explicit GDBStub(DebuggerBackend& backend, Core::System& system); 29 explicit GDBStub(DebuggerBackend& backend, Core::System& system,
30 Kernel::KProcess* debug_process);
22 ~GDBStub() override; 31 ~GDBStub() override;
23 32
24 void Connected() override; 33 void Connected() override;
@@ -42,8 +51,12 @@ private:
42 void SendReply(std::string_view data); 51 void SendReply(std::string_view data);
43 void SendStatus(char status); 52 void SendStatus(char status);
44 53
54 Kernel::KProcess* GetProcess();
55 Core::Memory::Memory& GetMemory();
56
45private: 57private:
46 Core::System& system; 58 Core::System& system;
59 Kernel::KProcess* debug_process;
47 std::unique_ptr<GDBStubArch> arch; 60 std::unique_ptr<GDBStubArch> arch;
48 std::vector<char> current_command; 61 std::vector<char> current_command;
49 std::map<VAddr, u32> replaced_instructions; 62 std::map<VAddr, u32> replaced_instructions;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 12b3bd797..23196cd5f 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -97,8 +97,9 @@ std::string SaveDataAttribute::DebugInfo() const {
97 static_cast<u8>(rank), index); 97 static_cast<u8>(rank), index);
98} 98}
99 99
100SaveDataFactory::SaveDataFactory(Core::System& system_, VirtualDir save_directory_) 100SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
101 : dir{std::move(save_directory_)}, system{system_} { 101 VirtualDir save_directory_)
102 : system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
102 // Delete all temporary storages 103 // Delete all temporary storages
103 // On hardware, it is expected that temporary storage be empty at first use. 104 // On hardware, it is expected that temporary storage be empty at first use.
104 dir->DeleteSubdirectoryRecursive("temp"); 105 dir->DeleteSubdirectoryRecursive("temp");
@@ -110,7 +111,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
110 PrintSaveDataAttributeWarnings(meta); 111 PrintSaveDataAttributeWarnings(meta);
111 112
112 const auto save_directory = 113 const auto save_directory =
113 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 114 GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
114 115
115 return dir->CreateDirectoryRelative(save_directory); 116 return dir->CreateDirectoryRelative(save_directory);
116} 117}
@@ -118,7 +119,7 @@ VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribut
118VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const { 119VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
119 120
120 const auto save_directory = 121 const auto save_directory =
121 GetFullPath(system, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id); 122 GetFullPath(program_id, dir, space, meta.type, meta.title_id, meta.user_id, meta.save_id);
122 123
123 auto out = dir->GetDirectoryRelative(save_directory); 124 auto out = dir->GetDirectoryRelative(save_directory);
124 125
@@ -147,14 +148,14 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
147 } 148 }
148} 149}
149 150
150std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir, 151std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
151 SaveDataSpaceId space, SaveDataType type, u64 title_id, 152 SaveDataSpaceId space, SaveDataType type, u64 title_id,
152 u128 user_id, u64 save_id) { 153 u128 user_id, u64 save_id) {
153 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 154 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
154 // be interpreted as the title id of the current process. 155 // be interpreted as the title id of the current process.
155 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) { 156 if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
156 if (title_id == 0) { 157 if (title_id == 0) {
157 title_id = system.GetApplicationProcessProgramID(); 158 title_id = program_id;
158 } 159 }
159 } 160 }
160 161
@@ -201,7 +202,7 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
201SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, 202SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
202 u128 user_id) const { 203 u128 user_id) const {
203 const auto path = 204 const auto path =
204 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 205 GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
205 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 206 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
206 207
207 const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName()); 208 const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -220,7 +221,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
220void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 221void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
221 SaveDataSize new_value) const { 222 SaveDataSize new_value) const {
222 const auto path = 223 const auto path =
223 GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 224 GetFullPath(program_id, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
224 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path); 225 const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
225 226
226 const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName()); 227 const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index fd4887e99..30d96928e 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -87,10 +87,13 @@ constexpr const char* GetSaveDataSizeFileName() {
87 return ".yuzu_save_size"; 87 return ".yuzu_save_size";
88} 88}
89 89
90using ProgramId = u64;
91
90/// File system interface to the SaveData archive 92/// File system interface to the SaveData archive
91class SaveDataFactory { 93class SaveDataFactory {
92public: 94public:
93 explicit SaveDataFactory(Core::System& system_, VirtualDir save_directory_); 95 explicit SaveDataFactory(Core::System& system_, ProgramId program_id_,
96 VirtualDir save_directory_);
94 ~SaveDataFactory(); 97 ~SaveDataFactory();
95 98
96 VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; 99 VirtualDir Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
@@ -99,7 +102,7 @@ public:
99 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 102 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
100 103
101 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); 104 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
102 static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space, 105 static std::string GetFullPath(ProgramId program_id, VirtualDir dir, SaveDataSpaceId space,
103 SaveDataType type, u64 title_id, u128 user_id, u64 save_id); 106 SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
104 static std::string GetUserGameSaveDataRoot(u128 user_id, bool future); 107 static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
105 108
@@ -110,8 +113,9 @@ public:
110 void SetAutoCreate(bool state); 113 void SetAutoCreate(bool state);
111 114
112private: 115private:
113 VirtualDir dir;
114 Core::System& system; 116 Core::System& system;
117 ProgramId program_id;
118 VirtualDir dir;
115 bool auto_create{true}; 119 bool auto_create{true};
116}; 120};
117 121
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index 58a1e7216..f08a6e448 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -28,14 +28,14 @@ Result KMemoryBlockManager::Initialize(KProcessAddress st, KProcessAddress nd,
28} 28}
29 29
30void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager, 30void KMemoryBlockManager::Finalize(KMemoryBlockSlabManager* slab_manager,
31 HostUnmapCallback&& host_unmap_callback) { 31 BlockCallback&& block_callback) {
32 // Erase every block until we have none left. 32 // Erase every block until we have none left.
33 auto it = m_memory_block_tree.begin(); 33 auto it = m_memory_block_tree.begin();
34 while (it != m_memory_block_tree.end()) { 34 while (it != m_memory_block_tree.end()) {
35 KMemoryBlock* block = std::addressof(*it); 35 KMemoryBlock* block = std::addressof(*it);
36 it = m_memory_block_tree.erase(it); 36 it = m_memory_block_tree.erase(it);
37 block_callback(block->GetAddress(), block->GetSize());
37 slab_manager->Free(block); 38 slab_manager->Free(block);
38 host_unmap_callback(block->GetAddress(), block->GetSize());
39 } 39 }
40 40
41 ASSERT(m_memory_block_tree.empty()); 41 ASSERT(m_memory_block_tree.empty());
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index cb7b6f430..377628504 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -85,11 +85,11 @@ public:
85public: 85public:
86 KMemoryBlockManager(); 86 KMemoryBlockManager();
87 87
88 using HostUnmapCallback = std::function<void(Common::ProcessAddress, u64)>; 88 using BlockCallback = std::function<void(Common::ProcessAddress, u64)>;
89 89
90 Result Initialize(KProcessAddress st, KProcessAddress nd, 90 Result Initialize(KProcessAddress st, KProcessAddress nd,
91 KMemoryBlockSlabManager* slab_manager); 91 KMemoryBlockSlabManager* slab_manager);
92 void Finalize(KMemoryBlockSlabManager* slab_manager, HostUnmapCallback&& host_unmap_callback); 92 void Finalize(KMemoryBlockSlabManager* slab_manager, BlockCallback&& block_callback);
93 93
94 iterator end() { 94 iterator end() {
95 return m_memory_block_tree.end(); 95 return m_memory_block_tree.end();
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 73fbda331..3f0a39d33 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -431,15 +431,43 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
431 m_memory_block_slab_manager)); 431 m_memory_block_slab_manager));
432} 432}
433 433
434Result KPageTableBase::FinalizeProcess() {
435 // Only process tables should be finalized.
436 ASSERT(!this->IsKernel());
437
438 // NOTE: Here Nintendo calls an unknown OnFinalize function.
439 // this->OnFinalize();
440
441 // NOTE: Here Nintendo calls a second unknown OnFinalize function.
442 // this->OnFinalize2();
443
444 // NOTE: Here Nintendo does a page table walk to discover heap pages to free.
445 // We will use the block manager finalization below to free them.
446
447 R_SUCCEED();
448}
449
434void KPageTableBase::Finalize() { 450void KPageTableBase::Finalize() {
435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { 451 this->FinalizeProcess();
436 if (Settings::IsFastmemEnabled()) { 452
453 auto BlockCallback = [&](KProcessAddress addr, u64 size) {
454 if (m_impl->fastmem_arena) {
437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); 455 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
438 } 456 }
457
458 // Get physical pages.
459 KPageGroup pg(m_kernel, m_block_info_manager);
460 this->MakePageGroup(pg, addr, size / PageSize);
461
462 // Free the pages.
463 pg.CloseAndReset();
439 }; 464 };
440 465
441 // Finalize memory blocks. 466 // Finalize memory blocks.
442 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback)); 467 {
468 KScopedLightLock lk(m_general_lock);
469 m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(BlockCallback));
470 }
443 471
444 // Free any unsafe mapped memory. 472 // Free any unsafe mapped memory.
445 if (m_mapped_unsafe_physical_memory) { 473 if (m_mapped_unsafe_physical_memory) {
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 077cafc96..748419f86 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -241,6 +241,7 @@ public:
241 KResourceLimit* resource_limit, Core::Memory::Memory& memory, 241 KResourceLimit* resource_limit, Core::Memory::Memory& memory,
242 KProcessAddress aslr_space_start); 242 KProcessAddress aslr_space_start);
243 243
244 Result FinalizeProcess();
244 void Finalize(); 245 void Finalize();
245 246
246 bool IsKernel() const { 247 bool IsKernel() const {
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 068e71dff..53735a225 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -171,6 +171,12 @@ void KProcess::Finalize() {
171 m_resource_limit->Close(); 171 m_resource_limit->Close();
172 } 172 }
173 173
174 // Clear expensive resources, as the destructor is not called for guest objects.
175 for (auto& interface : m_arm_interfaces) {
176 interface.reset();
177 }
178 m_exclusive_monitor.reset();
179
174 // Perform inherited finalization. 180 // Perform inherited finalization.
175 KSynchronizationObject::Finalize(); 181 KSynchronizationObject::Finalize();
176} 182}
@@ -1233,10 +1239,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1239 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1234 1240
1235#ifdef HAS_NCE 1241#ifdef HAS_NCE
1236 if (this->IsApplication() && Settings::IsNceEnabled()) { 1242 const auto& patch = code_set.PatchSegment();
1243 if (this->IsApplication() && Settings::IsNceEnabled() && patch.size != 0) {
1237 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1244 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1238 const auto& code = code_set.CodeSegment(); 1245 const auto& code = code_set.CodeSegment();
1239 const auto& patch = code_set.PatchSegment();
1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size, 1246 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute); 1247 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, 1248 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1030f0c12..f3683cdcc 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -112,7 +112,14 @@ struct KernelCore::Impl {
112 old_process->Close(); 112 old_process->Close();
113 } 113 }
114 114
115 process_list.clear(); 115 {
116 std::scoped_lock lk{process_list_lock};
117 for (auto* const process : process_list) {
118 process->Terminate();
119 process->Close();
120 }
121 process_list.clear();
122 }
116 123
117 next_object_id = 0; 124 next_object_id = 0;
118 next_kernel_process_id = KProcess::InitialProcessIdMin; 125 next_kernel_process_id = KProcess::InitialProcessIdMin;
@@ -770,6 +777,7 @@ struct KernelCore::Impl {
770 std::atomic<u64> next_thread_id{1}; 777 std::atomic<u64> next_thread_id{1};
771 778
772 // Lists all processes that exist in the current session. 779 // Lists all processes that exist in the current session.
780 std::mutex process_list_lock;
773 std::vector<KProcess*> process_list; 781 std::vector<KProcess*> process_list;
774 std::atomic<KProcess*> application_process{}; 782 std::atomic<KProcess*> application_process{};
775 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 783 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
@@ -869,9 +877,19 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
869} 877}
870 878
871void KernelCore::AppendNewProcess(KProcess* process) { 879void KernelCore::AppendNewProcess(KProcess* process) {
880 process->Open();
881
882 std::scoped_lock lk{impl->process_list_lock};
872 impl->process_list.push_back(process); 883 impl->process_list.push_back(process);
873} 884}
874 885
886void KernelCore::RemoveProcess(KProcess* process) {
887 std::scoped_lock lk{impl->process_list_lock};
888 if (std::erase(impl->process_list, process)) {
889 process->Close();
890 }
891}
892
875void KernelCore::MakeApplicationProcess(KProcess* process) { 893void KernelCore::MakeApplicationProcess(KProcess* process) {
876 impl->MakeApplicationProcess(process); 894 impl->MakeApplicationProcess(process);
877} 895}
@@ -884,8 +902,15 @@ const KProcess* KernelCore::ApplicationProcess() const {
884 return impl->application_process; 902 return impl->application_process;
885} 903}
886 904
887const std::vector<KProcess*>& KernelCore::GetProcessList() const { 905std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
888 return impl->process_list; 906 std::list<KScopedAutoObject<KProcess>> processes;
907 std::scoped_lock lk{impl->process_list_lock};
908
909 for (auto* const process : impl->process_list) {
910 processes.emplace_back(process);
911 }
912
913 return processes;
889} 914}
890 915
891Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() { 916Kernel::GlobalSchedulerContext& KernelCore::GlobalSchedulerContext() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5d4102145..8ea5bed1c 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <functional> 7#include <functional>
8#include <list>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <unordered_map> 11#include <unordered_map>
@@ -116,8 +117,9 @@ public:
116 /// Retrieves a shared pointer to the system resource limit instance. 117 /// Retrieves a shared pointer to the system resource limit instance.
117 KResourceLimit* GetSystemResourceLimit(); 118 KResourceLimit* GetSystemResourceLimit();
118 119
119 /// Adds the given shared pointer to an internal list of active processes. 120 /// Adds/removes the given pointer to an internal list of active processes.
120 void AppendNewProcess(KProcess* process); 121 void AppendNewProcess(KProcess* process);
122 void RemoveProcess(KProcess* process);
121 123
122 /// Makes the given process the new application process. 124 /// Makes the given process the new application process.
123 void MakeApplicationProcess(KProcess* process); 125 void MakeApplicationProcess(KProcess* process);
@@ -129,7 +131,7 @@ public:
129 const KProcess* ApplicationProcess() const; 131 const KProcess* ApplicationProcess() const;
130 132
131 /// Retrieves the list of processes. 133 /// Retrieves the list of processes.
132 const std::vector<KProcess*>& GetProcessList() const; 134 std::list<KScopedAutoObject<KProcess>> GetProcessList();
133 135
134 /// Gets the sole instance of the global scheduler 136 /// Gets the sole instance of the global scheduler
135 Kernel::GlobalSchedulerContext& GlobalSchedulerContext(); 137 Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
index caa8bee9a..5c3e8829f 100644
--- a/src/core/hle/kernel/svc/svc_process.cpp
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -74,13 +74,15 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
74 } 74 }
75 75
76 auto& memory = GetCurrentMemory(kernel); 76 auto& memory = GetCurrentMemory(kernel);
77 const auto& process_list = kernel.GetProcessList(); 77 auto process_list = kernel.GetProcessList();
78 auto it = process_list.begin();
79
78 const auto num_processes = process_list.size(); 80 const auto num_processes = process_list.size();
79 const auto copy_amount = 81 const auto copy_amount =
80 std::min(static_cast<std::size_t>(out_process_ids_size), num_processes); 82 std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
81 83
82 for (std::size_t i = 0; i < copy_amount; ++i) { 84 for (std::size_t i = 0; i < copy_amount && it != process_list.end(); ++i, ++it) {
83 memory.Write64(out_process_ids, process_list[i]->GetProcessId()); 85 memory.Write64(out_process_ids, (*it)->GetProcessId());
84 out_process_ids += sizeof(u64); 86 out_process_ids += sizeof(u64);
85 } 87 }
86 88
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 5542d6cbc..683f44e27 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -61,9 +61,7 @@ ProfileManager::ProfileManager() {
61 OpenUser(*GetUser(current)); 61 OpenUser(*GetUser(current));
62} 62}
63 63
64ProfileManager::~ProfileManager() { 64ProfileManager::~ProfileManager() = default;
65 WriteUserSaveFile();
66}
67 65
68/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 66/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
69/// internal management of the users profiles 67/// internal management of the users profiles
@@ -113,6 +111,8 @@ Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username)
113 return ERROR_USER_ALREADY_EXISTS; 111 return ERROR_USER_ALREADY_EXISTS;
114 } 112 }
115 113
114 is_save_needed = true;
115
116 return AddUser({ 116 return AddUser({
117 .user_uuid = uuid, 117 .user_uuid = uuid,
118 .username = username, 118 .username = username,
@@ -326,6 +326,9 @@ bool ProfileManager::RemoveUser(UUID uuid) {
326 profiles[*index] = ProfileInfo{}; 326 profiles[*index] = ProfileInfo{};
327 std::stable_partition(profiles.begin(), profiles.end(), 327 std::stable_partition(profiles.begin(), profiles.end(),
328 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); }); 328 [](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
329
330 is_save_needed = true;
331
329 return true; 332 return true;
330} 333}
331 334
@@ -340,6 +343,8 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
340 profile.username = profile_new.username; 343 profile.username = profile_new.username;
341 profile.creation_time = profile_new.timestamp; 344 profile.creation_time = profile_new.timestamp;
342 345
346 is_save_needed = true;
347
343 return true; 348 return true;
344} 349}
345 350
@@ -348,6 +353,7 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase&
348 const auto index = GetUserIndex(uuid); 353 const auto index = GetUserIndex(uuid);
349 if (index.has_value() && SetProfileBase(uuid, profile_new)) { 354 if (index.has_value() && SetProfileBase(uuid, profile_new)) {
350 profiles[*index].data = data_new; 355 profiles[*index].data = data_new;
356 is_save_needed = true;
351 return true; 357 return true;
352 } 358 }
353 359
@@ -391,6 +397,10 @@ void ProfileManager::ParseUserSaveFile() {
391} 397}
392 398
393void ProfileManager::WriteUserSaveFile() { 399void ProfileManager::WriteUserSaveFile() {
400 if (!is_save_needed) {
401 return;
402 }
403
394 ProfileDataRaw raw{}; 404 ProfileDataRaw raw{};
395 405
396 for (std::size_t i = 0; i < MAX_USERS; ++i) { 406 for (std::size_t i = 0; i < MAX_USERS; ++i) {
@@ -423,7 +433,10 @@ void ProfileManager::WriteUserSaveFile() {
423 if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { 433 if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) {
424 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " 434 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
425 "made in current session will be saved."); 435 "made in current session will be saved.");
436 return;
426 } 437 }
438
439 is_save_needed = false;
427} 440}
428 441
429}; // namespace Service::Account 442}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 900e32200..e21863e64 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -103,6 +103,7 @@ private:
103 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); 103 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
104 bool RemoveProfileAtIndex(std::size_t index); 104 bool RemoveProfileAtIndex(std::size_t index);
105 105
106 bool is_save_needed{};
106 std::array<ProfileInfo, MAX_USERS> profiles{}; 107 std::array<ProfileInfo, MAX_USERS> profiles{};
107 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{}; 108 std::array<ProfileInfo, MAX_USERS> stored_opened_profiles{};
108 std::size_t user_count{}; 109 std::size_t user_count{};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 9e05bdafa..a768bdc54 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -36,6 +36,7 @@
36#include "core/hle/service/caps/caps_su.h" 36#include "core/hle/service/caps/caps_su.h"
37#include "core/hle/service/caps/caps_types.h" 37#include "core/hle/service/caps/caps_types.h"
38#include "core/hle/service/filesystem/filesystem.h" 38#include "core/hle/service/filesystem/filesystem.h"
39#include "core/hle/service/filesystem/save_data_controller.h"
39#include "core/hle/service/ipc_helpers.h" 40#include "core/hle/service/ipc_helpers.h"
40#include "core/hle/service/ns/ns.h" 41#include "core/hle/service/ns/ns.h"
41#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 42#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -2178,7 +2179,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
2178 attribute.type = FileSys::SaveDataType::SaveData; 2179 attribute.type = FileSys::SaveDataType::SaveData;
2179 2180
2180 FileSys::VirtualDir save_data{}; 2181 FileSys::VirtualDir save_data{};
2181 const auto res = system.GetFileSystemController().CreateSaveData( 2182 const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
2182 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); 2183 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
2183 2184
2184 IPC::ResponseBuilder rb{ctx, 4}; 2185 IPC::ResponseBuilder rb{ctx, 4};
@@ -2353,7 +2354,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
2353 "new_journal={:016X}", 2354 "new_journal={:016X}",
2354 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); 2355 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
2355 2356
2356 system.GetFileSystemController().WriteSaveDataSize( 2357 system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
2357 type, system.GetApplicationProcessProgramID(), user_id, 2358 type, system.GetApplicationProcessProgramID(), user_id,
2358 {new_normal_size, new_journal_size}); 2359 {new_normal_size, new_journal_size});
2359 2360
@@ -2378,7 +2379,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
2378 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], 2379 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
2379 user_id[0]); 2380 user_id[0]);
2380 2381
2381 const auto size = system.GetFileSystemController().ReadSaveDataSize( 2382 const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
2382 type, system.GetApplicationProcessProgramID(), user_id); 2383 type, system.GetApplicationProcessProgramID(), user_id);
2383 2384
2384 IPC::ResponseBuilder rb{ctx, 6}; 2385 IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 56fee4591..de2aa6906 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -18,11 +18,11 @@ using namespace AudioCore::AudioIn;
18class IAudioIn final : public ServiceFramework<IAudioIn> { 18class IAudioIn final : public ServiceFramework<IAudioIn> {
19public: 19public:
20 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id, 20 explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
21 const std::string& device_name, const AudioInParameter& in_params, u32 handle, 21 const std::string& device_name, const AudioInParameter& in_params,
22 u64 applet_resource_user_id) 22 Kernel::KProcess* handle, u64 applet_resource_user_id)
23 : ServiceFramework{system_, "IAudioIn"}, 23 : ServiceFramework{system_, "IAudioIn"},
24 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")}, 24 service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
25 impl{std::make_shared<In>(system_, manager, event, session_id)} { 25 process{handle}, impl{std::make_shared<In>(system_, manager, event, session_id)} {
26 // clang-format off 26 // clang-format off
27 static const FunctionInfo functions[] = { 27 static const FunctionInfo functions[] = {
28 {0, &IAudioIn::GetAudioInState, "GetAudioInState"}, 28 {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
@@ -45,6 +45,8 @@ public:
45 45
46 RegisterHandlers(functions); 46 RegisterHandlers(functions);
47 47
48 process->Open();
49
48 if (impl->GetSystem() 50 if (impl->GetSystem()
49 .Initialize(device_name, in_params, handle, applet_resource_user_id) 51 .Initialize(device_name, in_params, handle, applet_resource_user_id)
50 .IsError()) { 52 .IsError()) {
@@ -55,6 +57,7 @@ public:
55 ~IAudioIn() override { 57 ~IAudioIn() override {
56 impl->Free(); 58 impl->Free();
57 service_context.CloseEvent(event); 59 service_context.CloseEvent(event);
60 process->Close();
58 } 61 }
59 62
60 [[nodiscard]] std::shared_ptr<In> GetImpl() { 63 [[nodiscard]] std::shared_ptr<In> GetImpl() {
@@ -196,6 +199,7 @@ private:
196 199
197 KernelHelpers::ServiceContext service_context; 200 KernelHelpers::ServiceContext service_context;
198 Kernel::KEvent* event; 201 Kernel::KEvent* event;
202 Kernel::KProcess* process;
199 std::shared_ptr<AudioCore::AudioIn::In> impl; 203 std::shared_ptr<AudioCore::AudioIn::In> impl;
200 Common::ScratchBuffer<u64> released_buffer; 204 Common::ScratchBuffer<u64> released_buffer;
201}; 205};
@@ -267,6 +271,14 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
267 auto device_name = Common::StringFromBuffer(device_name_data); 271 auto device_name = Common::StringFromBuffer(device_name_data);
268 auto handle{ctx.GetCopyHandle(0)}; 272 auto handle{ctx.GetCopyHandle(0)};
269 273
274 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
275 if (process.IsNull()) {
276 LOG_ERROR(Service_Audio, "Failed to get process handle");
277 IPC::ResponseBuilder rb{ctx, 2};
278 rb.Push(ResultUnknown);
279 return;
280 }
281
270 std::scoped_lock l{impl->mutex}; 282 std::scoped_lock l{impl->mutex};
271 auto link{impl->LinkToManager()}; 283 auto link{impl->LinkToManager()};
272 if (link.IsError()) { 284 if (link.IsError()) {
@@ -287,8 +299,9 @@ void AudInU::OpenAudioIn(HLERequestContext& ctx) {
287 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, 299 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
288 impl->num_free_sessions); 300 impl->num_free_sessions);
289 301
290 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, 302 auto audio_in =
291 in_params, handle, applet_resource_user_id); 303 std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
304 process.GetPointerUnsafe(), applet_resource_user_id);
292 impl->sessions[new_session_id] = audio_in->GetImpl(); 305 impl->sessions[new_session_id] = audio_in->GetImpl();
293 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; 306 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
294 307
@@ -318,6 +331,14 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
318 auto device_name = Common::StringFromBuffer(device_name_data); 331 auto device_name = Common::StringFromBuffer(device_name_data);
319 auto handle{ctx.GetCopyHandle(0)}; 332 auto handle{ctx.GetCopyHandle(0)};
320 333
334 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
335 if (process.IsNull()) {
336 LOG_ERROR(Service_Audio, "Failed to get process handle");
337 IPC::ResponseBuilder rb{ctx, 2};
338 rb.Push(ResultUnknown);
339 return;
340 }
341
321 std::scoped_lock l{impl->mutex}; 342 std::scoped_lock l{impl->mutex};
322 auto link{impl->LinkToManager()}; 343 auto link{impl->LinkToManager()};
323 if (link.IsError()) { 344 if (link.IsError()) {
@@ -338,8 +359,9 @@ void AudInU::OpenAudioInProtocolSpecified(HLERequestContext& ctx) {
338 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id, 359 LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
339 impl->num_free_sessions); 360 impl->num_free_sessions);
340 361
341 auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, 362 auto audio_in =
342 in_params, handle, applet_resource_user_id); 363 std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name, in_params,
364 process.GetPointerUnsafe(), applet_resource_user_id);
343 impl->sessions[new_session_id] = audio_in->GetImpl(); 365 impl->sessions[new_session_id] = audio_in->GetImpl();
344 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id; 366 impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
345 367
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index ca683d72c..8cc7b69f4 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -26,9 +26,10 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
26public: 26public:
27 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, 27 explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
28 size_t session_id, const std::string& device_name, 28 size_t session_id, const std::string& device_name,
29 const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) 29 const AudioOutParameter& in_params, Kernel::KProcess* handle,
30 u64 applet_resource_user_id)
30 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, 31 : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
31 event{service_context.CreateEvent("AudioOutEvent")}, 32 event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
32 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { 33 impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
33 34
34 // clang-format off 35 // clang-format off
@@ -50,11 +51,14 @@ public:
50 }; 51 };
51 // clang-format on 52 // clang-format on
52 RegisterHandlers(functions); 53 RegisterHandlers(functions);
54
55 process->Open();
53 } 56 }
54 57
55 ~IAudioOut() override { 58 ~IAudioOut() override {
56 impl->Free(); 59 impl->Free();
57 service_context.CloseEvent(event); 60 service_context.CloseEvent(event);
61 process->Close();
58 } 62 }
59 63
60 [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() { 64 [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
@@ -206,6 +210,7 @@ private:
206 210
207 KernelHelpers::ServiceContext service_context; 211 KernelHelpers::ServiceContext service_context;
208 Kernel::KEvent* event; 212 Kernel::KEvent* event;
213 Kernel::KProcess* process;
209 std::shared_ptr<AudioCore::AudioOut::Out> impl; 214 std::shared_ptr<AudioCore::AudioOut::Out> impl;
210 Common::ScratchBuffer<u64> released_buffer; 215 Common::ScratchBuffer<u64> released_buffer;
211}; 216};
@@ -257,6 +262,14 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
257 auto device_name = Common::StringFromBuffer(device_name_data); 262 auto device_name = Common::StringFromBuffer(device_name_data);
258 auto handle{ctx.GetCopyHandle(0)}; 263 auto handle{ctx.GetCopyHandle(0)};
259 264
265 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(handle)};
266 if (process.IsNull()) {
267 LOG_ERROR(Service_Audio, "Failed to get process handle");
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(ResultUnknown);
270 return;
271 }
272
260 auto link{impl->LinkToManager()}; 273 auto link{impl->LinkToManager()};
261 if (link.IsError()) { 274 if (link.IsError()) {
262 LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager"); 275 LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
@@ -276,10 +289,11 @@ void AudOutU::OpenAudioOut(HLERequestContext& ctx) {
276 LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, 289 LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
277 impl->num_free_sessions); 290 impl->num_free_sessions);
278 291
279 auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, 292 auto audio_out =
280 in_params, handle, applet_resource_user_id); 293 std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name, in_params,
281 result = audio_out->GetImpl()->GetSystem().Initialize(device_name, in_params, handle, 294 process.GetPointerUnsafe(), applet_resource_user_id);
282 applet_resource_user_id); 295 result = audio_out->GetImpl()->GetSystem().Initialize(
296 device_name, in_params, process.GetPointerUnsafe(), applet_resource_user_id);
283 if (result.IsError()) { 297 if (result.IsError()) {
284 LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!"); 298 LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
285 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 780f8c74d..ca6d8d607 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -24,15 +24,13 @@
24#include "core/hle/service/filesystem/fsp_ldr.h" 24#include "core/hle/service/filesystem/fsp_ldr.h"
25#include "core/hle/service/filesystem/fsp_pr.h" 25#include "core/hle/service/filesystem/fsp_pr.h"
26#include "core/hle/service/filesystem/fsp_srv.h" 26#include "core/hle/service/filesystem/fsp_srv.h"
27#include "core/hle/service/filesystem/romfs_controller.h"
28#include "core/hle/service/filesystem/save_data_controller.h"
27#include "core/hle/service/server_manager.h" 29#include "core/hle/service/server_manager.h"
28#include "core/loader/loader.h" 30#include "core/loader/loader.h"
29 31
30namespace Service::FileSystem { 32namespace Service::FileSystem {
31 33
32// A default size for normal/journal save data size if application control metadata cannot be found.
33// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
34constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
35
36static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, 34static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
37 std::string_view dir_name_) { 35 std::string_view dir_name_) {
38 std::string dir_name(Common::FS::SanitizePath(dir_name_)); 36 std::string dir_name(Common::FS::SanitizePath(dir_name_));
@@ -297,145 +295,65 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
297 295
298FileSystemController::~FileSystemController() = default; 296FileSystemController::~FileSystemController() = default;
299 297
300Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { 298Result FileSystemController::RegisterProcess(
301 romfs_factory = std::move(factory); 299 ProcessId process_id, ProgramId program_id,
302 LOG_DEBUG(Service_FS, "Registered RomFS"); 300 std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
303 return ResultSuccess; 301 std::scoped_lock lk{registration_lock};
304}
305
306Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
307 ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
308 save_data_factory = std::move(factory);
309 LOG_DEBUG(Service_FS, "Registered save data");
310 return ResultSuccess;
311}
312 302
313Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { 303 registrations.emplace(process_id, Registration{
314 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); 304 .program_id = program_id,
315 sdmc_factory = std::move(factory); 305 .romfs_factory = std::move(romfs_factory),
316 LOG_DEBUG(Service_FS, "Registered SDMC"); 306 .save_data_factory = CreateSaveDataFactory(program_id),
317 return ResultSuccess; 307 });
318}
319 308
320Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { 309 LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
321 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
322 bis_factory = std::move(factory);
323 LOG_DEBUG(Service_FS, "Registered BIS");
324 return ResultSuccess; 310 return ResultSuccess;
325} 311}
326 312
327void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { 313Result FileSystemController::OpenProcess(
328 LOG_TRACE(Service_FS, "Setting packed update for romfs"); 314 ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
329 315 std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
330 if (romfs_factory == nullptr) 316 std::scoped_lock lk{registration_lock};
331 return;
332
333 romfs_factory->SetPackedUpdate(std::move(update_raw));
334}
335
336FileSys::VirtualFile FileSystemController::OpenRomFSCurrentProcess() const {
337 LOG_TRACE(Service_FS, "Opening RomFS for current process");
338
339 if (romfs_factory == nullptr) {
340 return nullptr;
341 }
342
343 return romfs_factory->OpenCurrentProcess(system.GetApplicationProcessProgramID());
344}
345
346FileSys::VirtualFile FileSystemController::OpenPatchedRomFS(u64 title_id,
347 FileSys::ContentRecordType type) const {
348 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}", title_id);
349
350 if (romfs_factory == nullptr) {
351 return nullptr;
352 }
353
354 return romfs_factory->OpenPatchedRomFS(title_id, type);
355}
356
357FileSys::VirtualFile FileSystemController::OpenPatchedRomFSWithProgramIndex(
358 u64 title_id, u8 program_index, FileSys::ContentRecordType type) const {
359 LOG_TRACE(Service_FS, "Opening patched RomFS for title_id={:016X}, program_index={}", title_id,
360 program_index);
361
362 if (romfs_factory == nullptr) {
363 return nullptr;
364 }
365
366 return romfs_factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
367}
368
369FileSys::VirtualFile FileSystemController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
370 FileSys::ContentRecordType type) const {
371 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
372 title_id, storage_id, type);
373
374 if (romfs_factory == nullptr) {
375 return nullptr;
376 }
377
378 return romfs_factory->Open(title_id, storage_id, type);
379}
380
381std::shared_ptr<FileSys::NCA> FileSystemController::OpenBaseNca(
382 u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
383 return romfs_factory->GetEntry(title_id, storage_id, type);
384}
385
386Result FileSystemController::CreateSaveData(FileSys::VirtualDir* out_save_data,
387 FileSys::SaveDataSpaceId space,
388 const FileSys::SaveDataAttribute& save_struct) const {
389 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
390 save_struct.DebugInfo());
391 317
392 if (save_data_factory == nullptr) { 318 const auto it = registrations.find(process_id);
319 if (it == registrations.end()) {
393 return FileSys::ERROR_ENTITY_NOT_FOUND; 320 return FileSys::ERROR_ENTITY_NOT_FOUND;
394 } 321 }
395 322
396 auto save_data = save_data_factory->Create(space, save_struct); 323 *out_program_id = it->second.program_id;
397 if (save_data == nullptr) { 324 *out_save_data_controller =
398 return FileSys::ERROR_ENTITY_NOT_FOUND; 325 std::make_shared<SaveDataController>(system, it->second.save_data_factory);
399 } 326 *out_romfs_controller =
400 327 std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
401 *out_save_data = save_data;
402 return ResultSuccess; 328 return ResultSuccess;
403} 329}
404 330
405Result FileSystemController::OpenSaveData(FileSys::VirtualDir* out_save_data, 331void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) {
406 FileSys::SaveDataSpaceId space, 332 LOG_TRACE(Service_FS, "Setting packed update for romfs");
407 const FileSys::SaveDataAttribute& attribute) const {
408 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", space,
409 attribute.DebugInfo());
410
411 if (save_data_factory == nullptr) {
412 return FileSys::ERROR_ENTITY_NOT_FOUND;
413 }
414 333
415 auto save_data = save_data_factory->Open(space, attribute); 334 std::scoped_lock lk{registration_lock};
416 if (save_data == nullptr) { 335 const auto it = registrations.find(process_id);
417 return FileSys::ERROR_ENTITY_NOT_FOUND; 336 if (it == registrations.end()) {
337 return;
418 } 338 }
419 339
420 *out_save_data = save_data; 340 it->second.romfs_factory->SetPackedUpdate(std::move(update_raw));
421 return ResultSuccess;
422} 341}
423 342
424Result FileSystemController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space, 343std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() {
425 FileSys::SaveDataSpaceId space) const { 344 return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{}));
426 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", space); 345}
427
428 if (save_data_factory == nullptr) {
429 return FileSys::ERROR_ENTITY_NOT_FOUND;
430 }
431 346
432 auto save_data_space = save_data_factory->GetSaveDataSpaceDirectory(space); 347std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
433 if (save_data_space == nullptr) { 348 ProgramId program_id) {
434 return FileSys::ERROR_ENTITY_NOT_FOUND; 349 using YuzuPath = Common::FS::YuzuPath;
435 } 350 const auto rw_mode = FileSys::Mode::ReadWrite;
436 351
437 *out_save_data_space = save_data_space; 352 auto vfs = system.GetFilesystem();
438 return ResultSuccess; 353 const auto nand_directory =
354 vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
355 return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
356 std::move(nand_directory));
439} 357}
440 358
441Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { 359Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
@@ -540,48 +458,6 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
540 return 0; 458 return 0;
541} 459}
542 460
543FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
544 u64 title_id, u128 user_id) const {
545 if (save_data_factory == nullptr) {
546 return {0, 0};
547 }
548
549 const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
550
551 if (value.normal == 0 && value.journal == 0) {
552 FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
553
554 FileSys::NACP nacp;
555 const auto res = system.GetAppLoader().ReadControlData(nacp);
556
557 if (res != Loader::ResultStatus::Success) {
558 const FileSys::PatchManager pm{system.GetApplicationProcessProgramID(),
559 system.GetFileSystemController(),
560 system.GetContentProvider()};
561 const auto metadata = pm.GetControlMetadata();
562 const auto& nacp_unique = metadata.first;
563
564 if (nacp_unique != nullptr) {
565 new_size = {nacp_unique->GetDefaultNormalSaveSize(),
566 nacp_unique->GetDefaultJournalSaveSize()};
567 }
568 } else {
569 new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
570 }
571
572 WriteSaveDataSize(type, title_id, user_id, new_size);
573 return new_size;
574 }
575
576 return value;
577}
578
579void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
580 FileSys::SaveDataSize new_value) const {
581 if (save_data_factory != nullptr)
582 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
583}
584
585void FileSystemController::SetGameCard(FileSys::VirtualFile file) { 461void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
586 gamecard = std::make_unique<FileSys::XCI>(file); 462 gamecard = std::make_unique<FileSys::XCI>(file);
587 const auto dir = gamecard->ConcatenatedPseudoDirectory(); 463 const auto dir = gamecard->ConcatenatedPseudoDirectory();
@@ -801,14 +677,9 @@ FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
801 return bis_factory->GetBCATDirectory(title_id); 677 return bis_factory->GetBCATDirectory(title_id);
802} 678}
803 679
804void FileSystemController::SetAutoSaveDataCreation(bool enable) {
805 save_data_factory->SetAutoCreate(enable);
806}
807
808void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 680void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
809 if (overwrite) { 681 if (overwrite) {
810 bis_factory = nullptr; 682 bis_factory = nullptr;
811 save_data_factory = nullptr;
812 sdmc_factory = nullptr; 683 sdmc_factory = nullptr;
813 } 684 }
814 685
@@ -836,11 +707,6 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
836 bis_factory->GetUserNANDContents()); 707 bis_factory->GetUserNANDContents());
837 } 708 }
838 709
839 if (save_data_factory == nullptr) {
840 save_data_factory =
841 std::make_unique<FileSys::SaveDataFactory>(system, std::move(nand_directory));
842 }
843
844 if (sdmc_factory == nullptr) { 710 if (sdmc_factory == nullptr) {
845 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory), 711 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
846 std::move(sd_load_directory)); 712 std::move(sd_load_directory));
@@ -849,12 +715,19 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
849 } 715 }
850} 716}
851 717
718void FileSystemController::Reset() {
719 std::scoped_lock lk{registration_lock};
720 registrations.clear();
721}
722
852void LoopProcess(Core::System& system) { 723void LoopProcess(Core::System& system) {
853 auto server_manager = std::make_unique<ServerManager>(system); 724 auto server_manager = std::make_unique<ServerManager>(system);
854 725
726 const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
727
855 server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); 728 server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
856 server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system)); 729 server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
857 server_manager->RegisterNamedService("fsp-srv", std::make_shared<FSP_SRV>(system)); 730 server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
858 ServerManager::RunServer(std::move(server_manager)); 731 ServerManager::RunServer(std::move(server_manager));
859} 732}
860 733
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 276d264e1..48f37d289 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -43,6 +43,9 @@ class ServiceManager;
43 43
44namespace FileSystem { 44namespace FileSystem {
45 45
46class RomFsController;
47class SaveDataController;
48
46enum class ContentStorageId : u32 { 49enum class ContentStorageId : u32 {
47 System, 50 System,
48 User, 51 User,
@@ -61,32 +64,24 @@ enum class OpenDirectoryMode : u64 {
61}; 64};
62DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); 65DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
63 66
67using ProcessId = u64;
68using ProgramId = u64;
69
64class FileSystemController { 70class FileSystemController {
65public: 71public:
66 explicit FileSystemController(Core::System& system_); 72 explicit FileSystemController(Core::System& system_);
67 ~FileSystemController(); 73 ~FileSystemController();
68 74
69 Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); 75 Result RegisterProcess(ProcessId process_id, ProgramId program_id,
70 Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); 76 std::shared_ptr<FileSys::RomFSFactory>&& factory);
71 Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); 77 Result OpenProcess(ProgramId* out_program_id,
72 Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 78 std::shared_ptr<SaveDataController>* out_save_data_controller,
73 79 std::shared_ptr<RomFsController>* out_romfs_controller,
74 void SetPackedUpdate(FileSys::VirtualFile update_raw); 80 ProcessId process_id);
75 FileSys::VirtualFile OpenRomFSCurrentProcess() const; 81 void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
76 FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type) const; 82
77 FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index, 83 std::shared_ptr<SaveDataController> OpenSaveDataController();
78 FileSys::ContentRecordType type) const; 84
79 FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
80 FileSys::ContentRecordType type) const;
81 std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
82 FileSys::ContentRecordType type) const;
83
84 Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
85 const FileSys::SaveDataAttribute& save_struct) const;
86 Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
87 const FileSys::SaveDataAttribute& save_struct) const;
88 Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
89 FileSys::SaveDataSpaceId space) const;
90 Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const; 85 Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
91 Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition, 86 Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
92 FileSys::BisPartitionId id) const; 87 FileSys::BisPartitionId id) const;
@@ -96,11 +91,6 @@ public:
96 u64 GetFreeSpaceSize(FileSys::StorageId id) const; 91 u64 GetFreeSpaceSize(FileSys::StorageId id) const;
97 u64 GetTotalSpaceSize(FileSys::StorageId id) const; 92 u64 GetTotalSpaceSize(FileSys::StorageId id) const;
98 93
99 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
100 u128 user_id) const;
101 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
102 FileSys::SaveDataSize new_value) const;
103
104 void SetGameCard(FileSys::VirtualFile file); 94 void SetGameCard(FileSys::VirtualFile file);
105 FileSys::XCI* GetGameCard() const; 95 FileSys::XCI* GetGameCard() const;
106 96
@@ -133,15 +123,24 @@ public:
133 123
134 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const; 124 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
135 125
136 void SetAutoSaveDataCreation(bool enable);
137
138 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 126 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
139 // above is called. 127 // above is called.
140 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 128 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
141 129
130 void Reset();
131
142private: 132private:
143 std::unique_ptr<FileSys::RomFSFactory> romfs_factory; 133 std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id);
144 std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; 134
135 struct Registration {
136 ProgramId program_id;
137 std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
138 std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
139 };
140
141 std::mutex registration_lock;
142 std::map<ProcessId, Registration> registrations;
143
145 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; 144 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
146 std::unique_ptr<FileSys::BISFactory> bis_factory; 145 std::unique_ptr<FileSys::BISFactory> bis_factory;
147 146
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 82ecc1b90..a2397bec4 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -27,6 +27,8 @@
27#include "core/hle/result.h" 27#include "core/hle/result.h"
28#include "core/hle/service/filesystem/filesystem.h" 28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/hle/service/filesystem/fsp_srv.h" 29#include "core/hle/service/filesystem/fsp_srv.h"
30#include "core/hle/service/filesystem/romfs_controller.h"
31#include "core/hle/service/filesystem/save_data_controller.h"
30#include "core/hle/service/hle_ipc.h" 32#include "core/hle/service/hle_ipc.h"
31#include "core/hle/service/ipc_helpers.h" 33#include "core/hle/service/ipc_helpers.h"
32#include "core/reporter.h" 34#include "core/reporter.h"
@@ -577,9 +579,11 @@ private:
577 579
578class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 580class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
579public: 581public:
580 explicit ISaveDataInfoReader(Core::System& system_, FileSys::SaveDataSpaceId space, 582 explicit ISaveDataInfoReader(Core::System& system_,
581 FileSystemController& fsc_) 583 std::shared_ptr<SaveDataController> save_data_controller_,
582 : ServiceFramework{system_, "ISaveDataInfoReader"}, fsc{fsc_} { 584 FileSys::SaveDataSpaceId space)
585 : ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{
586 save_data_controller_} {
583 static const FunctionInfo functions[] = { 587 static const FunctionInfo functions[] = {
584 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, 588 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
585 }; 589 };
@@ -626,7 +630,7 @@ private:
626 630
627 void FindAllSaves(FileSys::SaveDataSpaceId space) { 631 void FindAllSaves(FileSys::SaveDataSpaceId space) {
628 FileSys::VirtualDir save_root{}; 632 FileSys::VirtualDir save_root{};
629 const auto result = fsc.OpenSaveDataSpace(&save_root, space); 633 const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space);
630 634
631 if (result != ResultSuccess || save_root == nullptr) { 635 if (result != ResultSuccess || save_root == nullptr) {
632 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space); 636 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
@@ -723,7 +727,8 @@ private:
723 }; 727 };
724 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 728 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
725 729
726 FileSystemController& fsc; 730 ProcessId process_id = 0;
731 std::shared_ptr<SaveDataController> save_data_controller;
727 std::vector<SaveDataInfo> info; 732 std::vector<SaveDataInfo> info;
728 u64 next_entry_index = 0; 733 u64 next_entry_index = 0;
729}; 734};
@@ -863,21 +868,20 @@ FSP_SRV::FSP_SRV(Core::System& system_)
863 if (Settings::values.enable_fs_access_log) { 868 if (Settings::values.enable_fs_access_log) {
864 access_log_mode = AccessLogMode::SdCard; 869 access_log_mode = AccessLogMode::SdCard;
865 } 870 }
866
867 // This should be true on creation
868 fsc.SetAutoSaveDataCreation(true);
869} 871}
870 872
871FSP_SRV::~FSP_SRV() = default; 873FSP_SRV::~FSP_SRV() = default;
872 874
873void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { 875void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
874 IPC::RequestParser rp{ctx}; 876 current_process_id = ctx.GetPID();
875 current_process_id = rp.Pop<u64>();
876 877
877 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id); 878 LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
878 879
880 const auto res =
881 fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id);
882
879 IPC::ResponseBuilder rb{ctx, 2}; 883 IPC::ResponseBuilder rb{ctx, 2};
880 rb.Push(ResultSuccess); 884 rb.Push(res);
881} 885}
882 886
883void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { 887void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
@@ -916,7 +920,8 @@ void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
916 uid[1], uid[0]); 920 uid[1], uid[0]);
917 921
918 FileSys::VirtualDir save_data_dir{}; 922 FileSys::VirtualDir save_data_dir{};
919 fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser, save_struct); 923 save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser,
924 save_struct);
920 925
921 IPC::ResponseBuilder rb{ctx, 2}; 926 IPC::ResponseBuilder rb{ctx, 2};
922 rb.Push(ResultSuccess); 927 rb.Push(ResultSuccess);
@@ -931,7 +936,8 @@ void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx)
931 LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo()); 936 LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
932 937
933 FileSys::VirtualDir save_data_dir{}; 938 FileSys::VirtualDir save_data_dir{};
934 fsc.CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem, save_struct); 939 save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem,
940 save_struct);
935 941
936 IPC::ResponseBuilder rb{ctx, 2}; 942 IPC::ResponseBuilder rb{ctx, 2};
937 rb.Push(ResultSuccess); 943 rb.Push(ResultSuccess);
@@ -950,7 +956,8 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
950 LOG_INFO(Service_FS, "called."); 956 LOG_INFO(Service_FS, "called.");
951 957
952 FileSys::VirtualDir dir{}; 958 FileSys::VirtualDir dir{};
953 auto result = fsc.OpenSaveData(&dir, parameters.space_id, parameters.attribute); 959 auto result =
960 save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
954 if (result != ResultSuccess) { 961 if (result != ResultSuccess) {
955 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 962 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
956 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 963 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@@ -1001,7 +1008,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
1001 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1008 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1002 rb.Push(ResultSuccess); 1009 rb.Push(ResultSuccess);
1003 rb.PushIpcInterface<ISaveDataInfoReader>( 1010 rb.PushIpcInterface<ISaveDataInfoReader>(
1004 std::make_shared<ISaveDataInfoReader>(system, space, fsc)); 1011 std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space));
1005} 1012}
1006 1013
1007void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) { 1014void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
@@ -1009,8 +1016,8 @@ void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
1009 1016
1010 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1017 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1011 rb.Push(ResultSuccess); 1018 rb.Push(ResultSuccess);
1012 rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage, 1019 rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller,
1013 fsc); 1020 FileSys::SaveDataSpaceId::TemporaryStorage);
1014} 1021}
1015 1022
1016void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) { 1023void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
@@ -1050,7 +1057,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
1050 LOG_DEBUG(Service_FS, "called"); 1057 LOG_DEBUG(Service_FS, "called");
1051 1058
1052 if (!romfs) { 1059 if (!romfs) {
1053 auto current_romfs = fsc.OpenRomFSCurrentProcess(); 1060 auto current_romfs = romfs_controller->OpenRomFSCurrentProcess();
1054 if (!current_romfs) { 1061 if (!current_romfs) {
1055 // TODO (bunnei): Find the right error code to use here 1062 // TODO (bunnei): Find the right error code to use here
1056 LOG_CRITICAL(Service_FS, "no file system interface available!"); 1063 LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -1078,7 +1085,7 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
1078 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", 1085 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
1079 storage_id, unknown, title_id); 1086 storage_id, unknown, title_id);
1080 1087
1081 auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 1088 auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
1082 1089
1083 if (!data) { 1090 if (!data) {
1084 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); 1091 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
@@ -1101,7 +1108,8 @@ void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
1101 1108
1102 const FileSys::PatchManager pm{title_id, fsc, content_provider}; 1109 const FileSys::PatchManager pm{title_id, fsc, content_provider};
1103 1110
1104 auto base = fsc.OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data); 1111 auto base =
1112 romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
1105 auto storage = std::make_shared<IStorage>( 1113 auto storage = std::make_shared<IStorage>(
1106 system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data)); 1114 system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data));
1107 1115
@@ -1129,9 +1137,8 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
1129 1137
1130 LOG_DEBUG(Service_FS, "called, program_index={}", program_index); 1138 LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
1131 1139
1132 auto patched_romfs = 1140 auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex(
1133 fsc.OpenPatchedRomFSWithProgramIndex(system.GetApplicationProcessProgramID(), program_index, 1141 program_id, program_index, FileSys::ContentRecordType::Program);
1134 FileSys::ContentRecordType::Program);
1135 1142
1136 if (!patched_romfs) { 1143 if (!patched_romfs) {
1137 // TODO: Find the right error code to use here 1144 // TODO: Find the right error code to use here
@@ -1152,7 +1159,7 @@ void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
1152void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) { 1159void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) {
1153 LOG_DEBUG(Service_FS, "called"); 1160 LOG_DEBUG(Service_FS, "called");
1154 1161
1155 fsc.SetAutoSaveDataCreation(false); 1162 save_data_controller->SetAutoCreate(false);
1156 1163
1157 IPC::ResponseBuilder rb{ctx, 2}; 1164 IPC::ResponseBuilder rb{ctx, 2};
1158 rb.Push(ResultSuccess); 1165 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 280bc9867..26980af99 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -17,6 +17,9 @@ class FileSystemBackend;
17 17
18namespace Service::FileSystem { 18namespace Service::FileSystem {
19 19
20class RomFsController;
21class SaveDataController;
22
20enum class AccessLogVersion : u32 { 23enum class AccessLogVersion : u32 {
21 V7_0_0 = 2, 24 V7_0_0 = 2,
22 25
@@ -67,6 +70,9 @@ private:
67 u64 current_process_id = 0; 70 u64 current_process_id = 0;
68 u32 access_log_program_index = 0; 71 u32 access_log_program_index = 0;
69 AccessLogMode access_log_mode = AccessLogMode::None; 72 AccessLogMode access_log_mode = AccessLogMode::None;
73 u64 program_id = 0;
74 std::shared_ptr<SaveDataController> save_data_controller;
75 std::shared_ptr<RomFsController> romfs_controller;
70}; 76};
71 77
72} // namespace Service::FileSystem 78} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/romfs_controller.cpp b/src/core/hle/service/filesystem/romfs_controller.cpp
new file mode 100644
index 000000000..19c9cec72
--- /dev/null
+++ b/src/core/hle/service/filesystem/romfs_controller.cpp
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/filesystem/romfs_controller.h"
5
6namespace Service::FileSystem {
7
8RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_)
9 : factory{std::move(factory_)}, program_id{program_id_} {}
10RomFsController::~RomFsController() = default;
11
12FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() {
13 return factory->OpenCurrentProcess(program_id);
14}
15
16FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id,
17 FileSys::ContentRecordType type) {
18 return factory->OpenPatchedRomFS(title_id, type);
19}
20
21FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex(
22 u64 title_id, u8 program_index, FileSys::ContentRecordType type) {
23 return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
24}
25
26FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
27 FileSys::ContentRecordType type) {
28 return factory->Open(title_id, storage_id, type);
29}
30
31std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id,
32 FileSys::StorageId storage_id,
33 FileSys::ContentRecordType type) {
34 return factory->GetEntry(title_id, storage_id, type);
35}
36
37} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h
new file mode 100644
index 000000000..9a478f71d
--- /dev/null
+++ b/src/core/hle/service/filesystem/romfs_controller.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/romfs_factory.h"
8#include "core/file_sys/vfs_types.h"
9
10namespace Service::FileSystem {
11
12class RomFsController {
13public:
14 explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_);
15 ~RomFsController();
16
17 FileSys::VirtualFile OpenRomFSCurrentProcess();
18 FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type);
19 FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
20 FileSys::ContentRecordType type);
21 FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
22 FileSys::ContentRecordType type);
23 std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
24 FileSys::ContentRecordType type);
25
26private:
27 const std::shared_ptr<FileSys::RomFSFactory> factory;
28 const u64 program_id;
29};
30
31} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp
new file mode 100644
index 000000000..d19b3ea1e
--- /dev/null
+++ b/src/core/hle/service/filesystem/save_data_controller.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/file_sys/control_metadata.h"
6#include "core/file_sys/errors.h"
7#include "core/file_sys/patch_manager.h"
8#include "core/hle/service/filesystem/save_data_controller.h"
9#include "core/loader/loader.h"
10
11namespace Service::FileSystem {
12
13namespace {
14
15// A default size for normal/journal save data size if application control metadata cannot be found.
16// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
17constexpr u64 SufficientSaveDataSize = 0xF0000000;
18
19FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) {
20 const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
21 system.GetContentProvider()};
22 const auto metadata = pm.GetControlMetadata();
23 const auto& nacp = metadata.first;
24
25 if (nacp != nullptr) {
26 return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()};
27 }
28
29 return {SufficientSaveDataSize, SufficientSaveDataSize};
30}
31
32} // namespace
33
34SaveDataController::SaveDataController(Core::System& system_,
35 std::shared_ptr<FileSys::SaveDataFactory> factory_)
36 : system{system_}, factory{std::move(factory_)} {}
37SaveDataController::~SaveDataController() = default;
38
39Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
40 FileSys::SaveDataSpaceId space,
41 const FileSys::SaveDataAttribute& attribute) {
42 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
43 attribute.DebugInfo());
44
45 auto save_data = factory->Create(space, attribute);
46 if (save_data == nullptr) {
47 return FileSys::ERROR_ENTITY_NOT_FOUND;
48 }
49
50 *out_save_data = save_data;
51 return ResultSuccess;
52}
53
54Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
55 FileSys::SaveDataSpaceId space,
56 const FileSys::SaveDataAttribute& attribute) {
57 auto save_data = factory->Open(space, attribute);
58 if (save_data == nullptr) {
59 return FileSys::ERROR_ENTITY_NOT_FOUND;
60 }
61
62 *out_save_data = save_data;
63 return ResultSuccess;
64}
65
66Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
67 FileSys::SaveDataSpaceId space) {
68 auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
69 if (save_data_space == nullptr) {
70 return FileSys::ERROR_ENTITY_NOT_FOUND;
71 }
72
73 *out_save_data_space = save_data_space;
74 return ResultSuccess;
75}
76
77FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
78 u128 user_id) {
79 const auto value = factory->ReadSaveDataSize(type, title_id, user_id);
80
81 if (value.normal == 0 && value.journal == 0) {
82 const auto size = GetDefaultSaveDataSize(system, title_id);
83 factory->WriteSaveDataSize(type, title_id, user_id, size);
84 return size;
85 }
86
87 return value;
88}
89
90void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
91 FileSys::SaveDataSize new_value) {
92 factory->WriteSaveDataSize(type, title_id, user_id, new_value);
93}
94
95void SaveDataController::SetAutoCreate(bool state) {
96 factory->SetAutoCreate(state);
97}
98
99} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h
new file mode 100644
index 000000000..863188e4c
--- /dev/null
+++ b/src/core/hle/service/filesystem/save_data_controller.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/savedata_factory.h"
8#include "core/file_sys/vfs_types.h"
9
10namespace Service::FileSystem {
11
12class SaveDataController {
13public:
14 explicit SaveDataController(Core::System& system,
15 std::shared_ptr<FileSys::SaveDataFactory> factory_);
16 ~SaveDataController();
17
18 Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
19 const FileSys::SaveDataAttribute& attribute);
20 Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
21 const FileSys::SaveDataAttribute& attribute);
22 Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
23 FileSys::SaveDataSpaceId space);
24
25 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
26 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
27 FileSys::SaveDataSize new_value);
28 void SetAutoCreate(bool state);
29
30private:
31 Core::System& system;
32 const std::shared_ptr<FileSys::SaveDataFactory> factory;
33};
34
35} // namespace Service::FileSystem
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 6f1151b03..1254b6d49 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -15,9 +15,10 @@
15namespace Service::Glue { 15namespace Service::Glue {
16 16
17namespace { 17namespace {
18std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) { 18std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) {
19 const auto& list = system.Kernel().GetProcessList(); 19 auto list = system.Kernel().GetProcessList();
20 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { 20
21 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) {
21 return process->GetProcessId() == process_id; 22 return process->GetProcessId() == process_id;
22 }); 23 });
23 24
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fc03a0a5f..4ce0a9834 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -22,12 +22,10 @@ void LoopProcess(Core::System& system) {
22 std::shared_ptr<HidFirmwareSettings> firmware_settings = 22 std::shared_ptr<HidFirmwareSettings> firmware_settings =
23 std::make_shared<HidFirmwareSettings>(); 23 std::make_shared<HidFirmwareSettings>();
24 24
25 // TODO: Remove this hack until this service is emulated properly. 25 // TODO: Remove this hack when am is emulated properly.
26 const auto process_list = system.Kernel().GetProcessList(); 26 resource_manager->Initialize();
27 if (!process_list.empty()) { 27 resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
28 resource_manager->Initialize(); 28 true);
29 resource_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
30 }
31 29
32 server_manager->RegisterNamedService( 30 server_manager->RegisterNamedService(
33 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); 31 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index d92499f05..b52468e41 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -22,27 +22,26 @@ constexpr Result ResultProcessNotFound{ErrorModule::PM, 1};
22 22
23constexpr u64 NO_PROCESS_FOUND_PID{0}; 23constexpr u64 NO_PROCESS_FOUND_PID{0};
24 24
25std::optional<Kernel::KProcess*> SearchProcessList( 25using ProcessList = std::list<Kernel::KScopedAutoObject<Kernel::KProcess>>;
26 const std::vector<Kernel::KProcess*>& process_list, 26
27 std::function<bool(Kernel::KProcess*)> predicate) { 27template <typename F>
28Kernel::KScopedAutoObject<Kernel::KProcess> SearchProcessList(ProcessList& process_list,
29 F&& predicate) {
28 const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); 30 const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate);
29 31
30 if (iter == process_list.end()) { 32 if (iter == process_list.end()) {
31 return std::nullopt; 33 return nullptr;
32 } 34 }
33 35
34 return *iter; 36 return iter->GetPointerUnsafe();
35} 37}
36 38
37void GetApplicationPidGeneric(HLERequestContext& ctx, 39void GetApplicationPidGeneric(HLERequestContext& ctx, ProcessList& process_list) {
38 const std::vector<Kernel::KProcess*>& process_list) { 40 auto process = SearchProcessList(process_list, [](auto& p) { return p->IsApplication(); });
39 const auto process = SearchProcessList(process_list, [](const auto& proc) {
40 return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin;
41 });
42 41
43 IPC::ResponseBuilder rb{ctx, 4}; 42 IPC::ResponseBuilder rb{ctx, 4};
44 rb.Push(ResultSuccess); 43 rb.Push(ResultSuccess);
45 rb.Push(process.has_value() ? (*process)->GetProcessId() : NO_PROCESS_FOUND_PID); 44 rb.Push(process.IsNull() ? NO_PROCESS_FOUND_PID : process->GetProcessId());
46} 45}
47 46
48} // Anonymous namespace 47} // Anonymous namespace
@@ -80,8 +79,7 @@ private:
80 79
81class DebugMonitor final : public ServiceFramework<DebugMonitor> { 80class DebugMonitor final : public ServiceFramework<DebugMonitor> {
82public: 81public:
83 explicit DebugMonitor(Core::System& system_) 82 explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "pm:dmnt"} {
84 : ServiceFramework{system_, "pm:dmnt"}, kernel{system_.Kernel()} {
85 // clang-format off 83 // clang-format off
86 static const FunctionInfo functions[] = { 84 static const FunctionInfo functions[] = {
87 {0, nullptr, "GetJitDebugProcessIdList"}, 85 {0, nullptr, "GetJitDebugProcessIdList"},
@@ -106,12 +104,11 @@ private:
106 104
107 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); 105 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
108 106
109 const auto process = 107 auto list = kernel.GetProcessList();
110 SearchProcessList(kernel.GetProcessList(), [program_id](const auto& proc) { 108 auto process = SearchProcessList(
111 return proc->GetProgramId() == program_id; 109 list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
112 });
113 110
114 if (!process.has_value()) { 111 if (process.IsNull()) {
115 IPC::ResponseBuilder rb{ctx, 2}; 112 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(ResultProcessNotFound); 113 rb.Push(ResultProcessNotFound);
117 return; 114 return;
@@ -119,12 +116,13 @@ private:
119 116
120 IPC::ResponseBuilder rb{ctx, 4}; 117 IPC::ResponseBuilder rb{ctx, 4};
121 rb.Push(ResultSuccess); 118 rb.Push(ResultSuccess);
122 rb.Push((*process)->GetProcessId()); 119 rb.Push(process->GetProcessId());
123 } 120 }
124 121
125 void GetApplicationProcessId(HLERequestContext& ctx) { 122 void GetApplicationProcessId(HLERequestContext& ctx) {
126 LOG_DEBUG(Service_PM, "called"); 123 LOG_DEBUG(Service_PM, "called");
127 GetApplicationPidGeneric(ctx, kernel.GetProcessList()); 124 auto list = kernel.GetProcessList();
125 GetApplicationPidGeneric(ctx, list);
128 } 126 }
129 127
130 void AtmosphereGetProcessInfo(HLERequestContext& ctx) { 128 void AtmosphereGetProcessInfo(HLERequestContext& ctx) {
@@ -135,11 +133,10 @@ private:
135 133
136 LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); 134 LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
137 135
138 const auto process = SearchProcessList(kernel.GetProcessList(), [pid](const auto& proc) { 136 auto list = kernel.GetProcessList();
139 return proc->GetProcessId() == pid; 137 auto process = SearchProcessList(list, [pid](auto& p) { return p->GetProcessId() == pid; });
140 });
141 138
142 if (!process.has_value()) { 139 if (process.IsNull()) {
143 IPC::ResponseBuilder rb{ctx, 2}; 140 IPC::ResponseBuilder rb{ctx, 2};
144 rb.Push(ResultProcessNotFound); 141 rb.Push(ResultProcessNotFound);
145 return; 142 return;
@@ -159,7 +156,7 @@ private:
159 156
160 OverrideStatus override_status{}; 157 OverrideStatus override_status{};
161 ProgramLocation program_location{ 158 ProgramLocation program_location{
162 .program_id = (*process)->GetProgramId(), 159 .program_id = process->GetProgramId(),
163 .storage_id = 0, 160 .storage_id = 0,
164 }; 161 };
165 162
@@ -169,14 +166,11 @@ private:
169 rb.PushRaw(program_location); 166 rb.PushRaw(program_location);
170 rb.PushRaw(override_status); 167 rb.PushRaw(override_status);
171 } 168 }
172
173 const Kernel::KernelCore& kernel;
174}; 169};
175 170
176class Info final : public ServiceFramework<Info> { 171class Info final : public ServiceFramework<Info> {
177public: 172public:
178 explicit Info(Core::System& system_, const std::vector<Kernel::KProcess*>& process_list_) 173 explicit Info(Core::System& system_) : ServiceFramework{system_, "pm:info"} {
179 : ServiceFramework{system_, "pm:info"}, process_list{process_list_} {
180 static const FunctionInfo functions[] = { 174 static const FunctionInfo functions[] = {
181 {0, &Info::GetProgramId, "GetProgramId"}, 175 {0, &Info::GetProgramId, "GetProgramId"},
182 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"}, 176 {65000, &Info::AtmosphereGetProcessId, "AtmosphereGetProcessId"},
@@ -193,11 +187,11 @@ private:
193 187
194 LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); 188 LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
195 189
196 const auto process = SearchProcessList(process_list, [process_id](const auto& proc) { 190 auto list = kernel.GetProcessList();
197 return proc->GetProcessId() == process_id; 191 auto process = SearchProcessList(
198 }); 192 list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
199 193
200 if (!process.has_value()) { 194 if (process.IsNull()) {
201 IPC::ResponseBuilder rb{ctx, 2}; 195 IPC::ResponseBuilder rb{ctx, 2};
202 rb.Push(ResultProcessNotFound); 196 rb.Push(ResultProcessNotFound);
203 return; 197 return;
@@ -205,7 +199,7 @@ private:
205 199
206 IPC::ResponseBuilder rb{ctx, 4}; 200 IPC::ResponseBuilder rb{ctx, 4};
207 rb.Push(ResultSuccess); 201 rb.Push(ResultSuccess);
208 rb.Push((*process)->GetProgramId()); 202 rb.Push(process->GetProgramId());
209 } 203 }
210 204
211 void AtmosphereGetProcessId(HLERequestContext& ctx) { 205 void AtmosphereGetProcessId(HLERequestContext& ctx) {
@@ -214,11 +208,11 @@ private:
214 208
215 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id); 209 LOG_DEBUG(Service_PM, "called, program_id={:016X}", program_id);
216 210
217 const auto process = SearchProcessList(process_list, [program_id](const auto& proc) { 211 auto list = system.Kernel().GetProcessList();
218 return proc->GetProgramId() == program_id; 212 auto process = SearchProcessList(
219 }); 213 list, [program_id](auto& p) { return p->GetProgramId() == program_id; });
220 214
221 if (!process.has_value()) { 215 if (process.IsNull()) {
222 IPC::ResponseBuilder rb{ctx, 2}; 216 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ResultProcessNotFound); 217 rb.Push(ResultProcessNotFound);
224 return; 218 return;
@@ -226,16 +220,13 @@ private:
226 220
227 IPC::ResponseBuilder rb{ctx, 4}; 221 IPC::ResponseBuilder rb{ctx, 4};
228 rb.Push(ResultSuccess); 222 rb.Push(ResultSuccess);
229 rb.Push((*process)->GetProcessId()); 223 rb.Push(process->GetProcessId());
230 } 224 }
231
232 const std::vector<Kernel::KProcess*>& process_list;
233}; 225};
234 226
235class Shell final : public ServiceFramework<Shell> { 227class Shell final : public ServiceFramework<Shell> {
236public: 228public:
237 explicit Shell(Core::System& system_) 229 explicit Shell(Core::System& system_) : ServiceFramework{system_, "pm:shell"} {
238 : ServiceFramework{system_, "pm:shell"}, kernel{system_.Kernel()} {
239 // clang-format off 230 // clang-format off
240 static const FunctionInfo functions[] = { 231 static const FunctionInfo functions[] = {
241 {0, nullptr, "LaunchProgram"}, 232 {0, nullptr, "LaunchProgram"},
@@ -257,10 +248,9 @@ public:
257private: 248private:
258 void GetApplicationProcessIdForShell(HLERequestContext& ctx) { 249 void GetApplicationProcessIdForShell(HLERequestContext& ctx) {
259 LOG_DEBUG(Service_PM, "called"); 250 LOG_DEBUG(Service_PM, "called");
260 GetApplicationPidGeneric(ctx, kernel.GetProcessList()); 251 auto list = kernel.GetProcessList();
252 GetApplicationPidGeneric(ctx, list);
261 } 253 }
262
263 const Kernel::KernelCore& kernel;
264}; 254};
265 255
266void LoopProcess(Core::System& system) { 256void LoopProcess(Core::System& system) {
@@ -268,8 +258,7 @@ void LoopProcess(Core::System& system) {
268 258
269 server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system)); 259 server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system));
270 server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system)); 260 server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system));
271 server_manager->RegisterNamedService( 261 server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system));
272 "pm:info", std::make_shared<Info>(system, system.Kernel().GetProcessList()));
273 server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system)); 262 server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system));
274 ServerManager::RunServer(std::move(server_manager)); 263 ServerManager::RunServer(std::move(server_manager));
275} 264}
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 15edb23e0..8ef49387d 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -256,8 +256,13 @@ Result ServerManager::WaitAndProcessImpl() {
256 256
257 // Wait for a signal. 257 // Wait for a signal.
258 s32 out_index{-1}; 258 s32 out_index{-1};
259 R_TRY(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), 259 R_TRY_CATCH(Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index,
260 num_objs, -1)); 260 wait_objs.data(), num_objs, -1)) {
261 R_CATCH(Kernel::ResultSessionClosed) {
262 // On session closed, index is updated and we don't want to return an error.
263 }
264 }
265 R_END_TRY_CATCH;
261 ASSERT(out_index >= 0 && out_index < num_objs); 266 ASSERT(out_index >= 0 && out_index < num_objs);
262 267
263 // Set the output index. 268 // Set the output index.
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index c9f8707b7..9b75c660c 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -19,8 +19,54 @@
19#include "core/arm/nce/patcher.h" 19#include "core/arm/nce/patcher.h"
20#endif 20#endif
21 21
22#ifndef HAS_NCE
23namespace Core::NCE {
24class Patcher {};
25} // namespace Core::NCE
26#endif
27
22namespace Loader { 28namespace Loader {
23 29
30struct PatchCollection {
31 explicit PatchCollection(bool is_application_) : is_application{is_application_} {
32 module_patcher_indices.fill(-1);
33 patchers.emplace_back();
34 }
35
36 std::vector<Core::NCE::Patcher>* GetPatchers() {
37 if (is_application && Settings::IsNceEnabled()) {
38 return &patchers;
39 }
40 return nullptr;
41 }
42
43 size_t GetTotalPatchSize() const {
44 size_t total_size{};
45#ifdef HAS_NCE
46 for (auto& patcher : patchers) {
47 total_size += patcher.GetSectionSize();
48 }
49#endif
50 return total_size;
51 }
52
53 void SaveIndex(size_t module) {
54 module_patcher_indices[module] = static_cast<s32>(patchers.size() - 1);
55 }
56
57 s32 GetIndex(size_t module) const {
58 return module_patcher_indices[module];
59 }
60
61 s32 GetLastIndex() const {
62 return static_cast<s32>(patchers.size()) - 1;
63 }
64
65 bool is_application;
66 std::vector<Core::NCE::Patcher> patchers;
67 std::array<s32, 13> module_patcher_indices{};
68};
69
24AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, 70AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
25 bool override_update_) 71 bool override_update_)
26 : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) { 72 : AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) {
@@ -142,18 +188,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
142 std::size_t code_size{}; 188 std::size_t code_size{};
143 189
144 // Define an nce patch context for each potential module. 190 // Define an nce patch context for each potential module.
145#ifdef HAS_NCE 191 PatchCollection patch_ctx{is_application};
146 std::array<Core::NCE::Patcher, 13> module_patchers;
147#endif
148
149 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
150#ifdef HAS_NCE
151 if (is_application && Settings::IsNceEnabled()) {
152 return &module_patchers[i];
153 }
154#endif
155 return nullptr;
156 };
157 192
158 // Use the NSO module loader to figure out the code layout 193 // Use the NSO module loader to figure out the code layout
159 for (size_t i = 0; i < static_modules.size(); i++) { 194 for (size_t i = 0; i < static_modules.size(); i++) {
@@ -164,13 +199,14 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
164 } 199 }
165 200
166 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 201 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
167 const auto tentative_next_load_addr = 202 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
168 AppLoader_NSO::LoadModule(process, system, *module_file, code_size, 203 process, system, *module_file, code_size, should_pass_arguments, false, {},
169 should_pass_arguments, false, {}, GetPatcher(i)); 204 patch_ctx.GetPatchers(), patch_ctx.GetLastIndex());
170 if (!tentative_next_load_addr) { 205 if (!tentative_next_load_addr) {
171 return {ResultStatus::ErrorLoadingNSO, {}}; 206 return {ResultStatus::ErrorLoadingNSO, {}};
172 } 207 }
173 208
209 patch_ctx.SaveIndex(i);
174 code_size = *tentative_next_load_addr; 210 code_size = *tentative_next_load_addr;
175 } 211 }
176 212
@@ -184,6 +220,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
184 return 0; 220 return 0;
185 }(); 221 }();
186 222
223 // Add patch size to the total module size
224 code_size += patch_ctx.GetTotalPatchSize();
225
187 // Setup the process code layout 226 // Setup the process code layout
188 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { 227 if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
189 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 228 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
@@ -204,9 +243,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
204 243
205 const VAddr load_addr{next_load_addr}; 244 const VAddr load_addr{next_load_addr};
206 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; 245 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
207 const auto tentative_next_load_addr = 246 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
208 AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, 247 process, system, *module_file, load_addr, should_pass_arguments, true, pm,
209 should_pass_arguments, true, pm, GetPatcher(i)); 248 patch_ctx.GetPatchers(), patch_ctx.GetIndex(i));
210 if (!tentative_next_load_addr) { 249 if (!tentative_next_load_addr) {
211 return {ResultStatus::ErrorLoadingNSO, {}}; 250 return {ResultStatus::ErrorLoadingNSO, {}};
212 } 251 }
@@ -216,20 +255,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
216 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); 255 LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
217 } 256 }
218 257
219 // Find the RomFS by searching for a ".romfs" file in this directory
220 const auto& files = dir->GetFiles();
221 const auto romfs_iter =
222 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& f) {
223 return f->GetName().find(".romfs") != std::string::npos;
224 });
225
226 // Register the RomFS if a ".romfs" file was found
227 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
228 romfs = *romfs_iter;
229 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
230 *this, system.GetContentProvider(), system.GetFileSystemController()));
231 }
232
233 is_loaded = true; 258 is_loaded = true;
234 return {ResultStatus::Success, 259 return {ResultStatus::Success,
235 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; 260 LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 814407535..2a32b1276 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -74,8 +74,10 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
74 return load_result; 74 return load_result;
75 } 75 }
76 76
77 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( 77 system.GetFileSystemController().RegisterProcess(
78 *this, system.GetContentProvider(), system.GetFileSystemController())); 78 process.GetProcessId(), nca->GetTitleId(),
79 std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
80 system.GetFileSystemController()));
79 81
80 is_loaded = true; 82 is_loaded = true;
81 return load_result; 83 return load_result;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index e74697cda..f8225d697 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -275,10 +275,12 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S
275 return {ResultStatus::ErrorLoadingNRO, {}}; 275 return {ResultStatus::ErrorLoadingNRO, {}};
276 } 276 }
277 277
278 if (romfs != nullptr) { 278 u64 program_id{};
279 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( 279 ReadProgramId(program_id);
280 *this, system.GetContentProvider(), system.GetFileSystemController())); 280 system.GetFileSystemController().RegisterProcess(
281 } 281 process.GetProcessId(), program_id,
282 std::make_unique<FileSys::RomFSFactory>(*this, system.GetContentProvider(),
283 system.GetFileSystemController()));
282 284
283 is_loaded = true; 285 is_loaded = true;
284 return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, 286 return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index b053a0d14..583b7e927 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -77,7 +77,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
77 const FileSys::VfsFile& nso_file, VAddr load_base, 77 const FileSys::VfsFile& nso_file, VAddr load_base,
78 bool should_pass_arguments, bool load_into_process, 78 bool should_pass_arguments, bool load_into_process,
79 std::optional<FileSys::PatchManager> pm, 79 std::optional<FileSys::PatchManager> pm,
80 Core::NCE::Patcher* patch) { 80 std::vector<Core::NCE::Patcher>* patches,
81 s32 patch_index) {
81 if (nso_file.GetSize() < sizeof(NSOHeader)) { 82 if (nso_file.GetSize() < sizeof(NSOHeader)) {
82 return std::nullopt; 83 return std::nullopt;
83 } 84 }
@@ -94,8 +95,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
94 // Allocate some space at the beginning if we are patching in PreText mode. 95 // Allocate some space at the beginning if we are patching in PreText mode.
95 const size_t module_start = [&]() -> size_t { 96 const size_t module_start = [&]() -> size_t {
96#ifdef HAS_NCE 97#ifdef HAS_NCE
97 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { 98 if (patches && load_into_process) {
98 return patch->GetSectionSize(); 99 auto* patch = &patches->operator[](patch_index);
100 if (patch->GetPatchMode() == Core::NCE::PatchMode::PreText) {
101 return patch->GetSectionSize();
102 }
99 } 103 }
100#endif 104#endif
101 return 0; 105 return 0;
@@ -160,27 +164,24 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
160#ifdef HAS_NCE 164#ifdef HAS_NCE
161 // If we are computing the process code layout and using nce backend, patch. 165 // If we are computing the process code layout and using nce backend, patch.
162 const auto& code = codeset.CodeSegment(); 166 const auto& code = codeset.CodeSegment();
163 if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { 167 auto* patch = patches ? &patches->operator[](patch_index) : nullptr;
168 if (patch && !load_into_process) {
164 // Patch SVCs and MRS calls in the guest code 169 // Patch SVCs and MRS calls in the guest code
165 patch->PatchText(program_image, code); 170 while (!patch->PatchText(program_image, code)) {
166 171 patch = &patches->emplace_back();
167 // Add patch section size to the module size. 172 }
168 image_size += static_cast<u32>(patch->GetSectionSize());
169 } else if (patch) { 173 } else if (patch) {
170 // Relocate code patch and copy to the program_image. 174 // Relocate code patch and copy to the program_image.
171 patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); 175 if (patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers())) {
172 176 // Update patch section.
173 // Update patch section. 177 auto& patch_segment = codeset.PatchSegment();
174 auto& patch_segment = codeset.PatchSegment(); 178 patch_segment.addr =
175 patch_segment.addr = 179 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size;
176 patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; 180 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
177 patch_segment.size = static_cast<u32>(patch->GetSectionSize());
178
179 // Add patch section size to the module size. In PreText mode image_size
180 // already contains the patch segment as part of module_start.
181 if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) {
182 image_size += patch_segment.size;
183 } 181 }
182
183 // Refresh image_size to take account the patch section if it was added by RelocateAndCopy
184 image_size = static_cast<u32>(program_image.size());
184 } 185 }
185#endif 186#endif
186 187
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 29b86ed4c..6356697e3 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -93,7 +93,8 @@ public:
93 const FileSys::VfsFile& nso_file, VAddr load_base, 93 const FileSys::VfsFile& nso_file, VAddr load_base,
94 bool should_pass_arguments, bool load_into_process, 94 bool should_pass_arguments, bool load_into_process,
95 std::optional<FileSys::PatchManager> pm = {}, 95 std::optional<FileSys::PatchManager> pm = {},
96 Core::NCE::Patcher* patch = nullptr); 96 std::vector<Core::NCE::Patcher>* patches = nullptr,
97 s32 patch_index = -1);
97 98
98 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 99 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
99 100
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index f4ab75b77..28116ff3a 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -111,7 +111,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
111 111
112 FileSys::VirtualFile update_raw; 112 FileSys::VirtualFile update_raw;
113 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 113 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
114 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); 114 system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
115 std::move(update_raw));
115 } 116 }
116 117
117 is_loaded = true; 118 is_loaded = true;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 12d72c380..e9abb199a 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -78,7 +78,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S
78 78
79 FileSys::VirtualFile update_raw; 79 FileSys::VirtualFile update_raw;
80 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 80 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
81 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); 81 system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(),
82 std::move(update_raw));
82 } 83 }
83 84
84 is_loaded = true; 85 is_loaded = true;
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 8693801c7..bdcbccfde 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -65,6 +65,14 @@ void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value&
65 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32), 65 WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32),
66 &StorageDefinitions::U32, index_offset); 66 &StorageDefinitions::U32, index_offset);
67} 67}
68
69void WriteStorageByCasLoop(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
70 Id value, Id bit_offset, Id bit_count) {
71 const Id pointer{StoragePointer(ctx, binding, offset, ctx.storage_types.U32, sizeof(u32),
72 &StorageDefinitions::U32)};
73 ctx.OpFunctionCall(ctx.TypeVoid(), ctx.write_storage_cas_loop_func, pointer, value, bit_offset,
74 bit_count);
75}
68} // Anonymous namespace 76} // Anonymous namespace
69 77
70void EmitLoadGlobalU8(EmitContext&) { 78void EmitLoadGlobalU8(EmitContext&) {
@@ -219,26 +227,42 @@ Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Valu
219 227
220void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 228void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
221 Id value) { 229 Id value) {
222 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, 230 if (ctx.profile.support_int8) {
223 sizeof(u8), &StorageDefinitions::U8); 231 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8,
232 sizeof(u8), &StorageDefinitions::U8);
233 } else {
234 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u));
235 }
224} 236}
225 237
226void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 238void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
227 Id value) { 239 Id value) {
228 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, 240 if (ctx.profile.support_int8) {
229 sizeof(s8), &StorageDefinitions::S8); 241 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8,
242 sizeof(s8), &StorageDefinitions::S8);
243 } else {
244 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset8(offset), ctx.Const(8u));
245 }
230} 246}
231 247
232void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 248void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
233 Id value) { 249 Id value) {
234 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, 250 if (ctx.profile.support_int16) {
235 sizeof(u16), &StorageDefinitions::U16); 251 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16,
252 sizeof(u16), &StorageDefinitions::U16);
253 } else {
254 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u));
255 }
236} 256}
237 257
238void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 258void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
239 Id value) { 259 Id value) {
240 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, 260 if (ctx.profile.support_int16) {
241 sizeof(s16), &StorageDefinitions::S16); 261 WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16,
262 sizeof(s16), &StorageDefinitions::S16);
263 } else {
264 WriteStorageByCasLoop(ctx, binding, offset, value, ctx.BitOffset16(offset), ctx.Const(16u));
265 }
242} 266}
243 267
244void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, 268void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 0442adc83..a27f2f73a 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -480,6 +480,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
480 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index); 480 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index);
481 DefineImages(program.info, image_binding, bindings.image_scaling_index); 481 DefineImages(program.info, image_binding, bindings.image_scaling_index);
482 DefineAttributeMemAccess(program.info); 482 DefineAttributeMemAccess(program.info);
483 DefineWriteStorageCasLoopFunction(program.info);
483 DefineGlobalMemoryFunctions(program.info); 484 DefineGlobalMemoryFunctions(program.info);
484 DefineRescalingInput(program.info); 485 DefineRescalingInput(program.info);
485 DefineRenderArea(program.info); 486 DefineRenderArea(program.info);
@@ -877,6 +878,56 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
877 } 878 }
878} 879}
879 880
881void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) {
882 if (profile.support_int8 && profile.support_int16) {
883 return;
884 }
885 if (!info.uses_int8 && !info.uses_int16) {
886 return;
887 }
888
889 AddCapability(spv::Capability::VariablePointersStorageBuffer);
890
891 const Id ptr_type{TypePointer(spv::StorageClass::StorageBuffer, U32[1])};
892 const Id func_type{TypeFunction(void_id, ptr_type, U32[1], U32[1], U32[1])};
893 const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)};
894 const Id pointer{OpFunctionParameter(ptr_type)};
895 const Id value{OpFunctionParameter(U32[1])};
896 const Id bit_offset{OpFunctionParameter(U32[1])};
897 const Id bit_count{OpFunctionParameter(U32[1])};
898
899 AddLabel();
900 const Id scope_device{Const(1u)};
901 const Id ordering_relaxed{u32_zero_value};
902 const Id body_label{OpLabel()};
903 const Id continue_label{OpLabel()};
904 const Id endloop_label{OpLabel()};
905 const Id beginloop_label{OpLabel()};
906 OpBranch(beginloop_label);
907
908 AddLabel(beginloop_label);
909 OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
910 OpBranch(body_label);
911
912 AddLabel(body_label);
913 const Id expected_value{OpLoad(U32[1], pointer)};
914 const Id desired_value{OpBitFieldInsert(U32[1], expected_value, value, bit_offset, bit_count)};
915 const Id actual_value{OpAtomicCompareExchange(U32[1], pointer, scope_device, ordering_relaxed,
916 ordering_relaxed, desired_value, expected_value)};
917 const Id store_successful{OpIEqual(U1, expected_value, actual_value)};
918 OpBranchConditional(store_successful, endloop_label, continue_label);
919
920 AddLabel(endloop_label);
921 OpReturn();
922
923 AddLabel(continue_label);
924 OpBranch(beginloop_label);
925
926 OpFunctionEnd();
927
928 write_storage_cas_loop_func = func;
929}
930
880void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { 931void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
881 if (!info.uses_global_memory || !profile.support_int64) { 932 if (!info.uses_global_memory || !profile.support_int64) {
882 return; 933 return;
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 56019ad89..40adcb6b6 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -325,6 +325,8 @@ public:
325 Id f32x2_min_cas{}; 325 Id f32x2_min_cas{};
326 Id f32x2_max_cas{}; 326 Id f32x2_max_cas{};
327 327
328 Id write_storage_cas_loop_func{};
329
328 Id load_global_func_u32{}; 330 Id load_global_func_u32{};
329 Id load_global_func_u32x2{}; 331 Id load_global_func_u32x2{};
330 Id load_global_func_u32x4{}; 332 Id load_global_func_u32x4{};
@@ -372,6 +374,7 @@ private:
372 void DefineTextures(const Info& info, u32& binding, u32& scaling_index); 374 void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
373 void DefineImages(const Info& info, u32& binding, u32& scaling_index); 375 void DefineImages(const Info& info, u32& binding, u32& scaling_index);
374 void DefineAttributeMemAccess(const Info& info); 376 void DefineAttributeMemAccess(const Info& info);
377 void DefineWriteStorageCasLoopFunction(const Info& info);
375 void DefineGlobalMemoryFunctions(const Info& info); 378 void DefineGlobalMemoryFunctions(const Info& info);
376 void DefineRescalingInput(const Info& info); 379 void DefineRescalingInput(const Info& info);
377 void DefineRescalingInputPushConstant(); 380 void DefineRescalingInputPushConstant();
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index fa5f383d6..12a04b9a0 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -205,6 +205,7 @@ void ConfigureProfileManager::AddUser() {
205 205
206 const auto uuid = Common::UUID::MakeRandom(); 206 const auto uuid = Common::UUID::MakeRandom();
207 profile_manager.CreateNewUser(uuid, username.toStdString()); 207 profile_manager.CreateNewUser(uuid, username.toStdString());
208 profile_manager.WriteUserSaveFile();
208 209
209 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); 210 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
210} 211}
@@ -228,6 +229,7 @@ void ConfigureProfileManager::RenameUser() {
228 std::copy(username_std.begin(), username_std.end(), profile.username.begin()); 229 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
229 230
230 profile_manager.SetProfileBase(*uuid, profile); 231 profile_manager.SetProfileBase(*uuid, profile);
232 profile_manager.WriteUserSaveFile();
231 233
232 item_model->setItem( 234 item_model->setItem(
233 user, 0, 235 user, 0,
@@ -256,6 +258,8 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
256 return; 258 return;
257 } 259 }
258 260
261 profile_manager.WriteUserSaveFile();
262
259 item_model->removeRows(tree_view->currentIndex().row(), 1); 263 item_model->removeRows(tree_view->currentIndex().row(), 1);
260 tree_view->clearSelection(); 264 tree_view->clearSelection();
261 265
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fd5342537..33756febf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2292,14 +2292,14 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
2292 ASSERT(user_id); 2292 ASSERT(user_id);
2293 2293
2294 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( 2294 const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2295 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2295 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
2296 FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0); 2296 FileSys::SaveDataType::SaveData, program_id, user_id->AsU128(), 0);
2297 2297
2298 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); 2298 path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
2299 } else { 2299 } else {
2300 // Device save data 2300 // Device save data
2301 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( 2301 const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2302 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2302 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser,
2303 FileSys::SaveDataType::SaveData, program_id, {}, 0); 2303 FileSys::SaveDataType::SaveData, program_id, {}, 0);
2304 2304
2305 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); 2305 path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
@@ -2662,8 +2662,8 @@ void GMainWindow::RemoveCacheStorage(u64 program_id) {
2662 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); 2662 vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
2663 2663
2664 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( 2664 const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
2665 *system, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, 2665 {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage,
2666 FileSys::SaveDataType::CacheStorage, 0 /* program_id */, {}, 0); 2666 0 /* program_id */, {}, 0);
2667 2667
2668 const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); 2668 const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
2669 2669