summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.cpp4
-rw-r--r--src/audio_core/audio_renderer.h1
-rw-r--r--src/audio_core/stream.cpp5
-rw-r--r--src/audio_core/stream.h3
-rw-r--r--src/audio_core/time_stretch.cpp2
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/file_util.cpp2
-rw-r--r--src/common/file_util.h2
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/ring_buffer.h14
-rw-r--r--src/common/thread.h8
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/arm/arm_interface.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp1
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core_cpu.cpp8
-rw-r--r--src/core/core_cpu.h2
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp2
-rw-r--r--src/core/file_sys/control_metadata.h2
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp366
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h70
-rw-r--r--src/core/file_sys/nca_metadata.cpp2
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/partition_filesystem.cpp2
-rw-r--r--src/core/file_sys/partition_filesystem.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp51
-rw-r--r--src/core/file_sys/patch_manager.h3
-rw-r--r--src/core/file_sys/program_metadata.cpp4
-rw-r--r--src/core/file_sys/program_metadata.h3
-rw-r--r--src/core/file_sys/registered_cache.cpp9
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/romfs.cpp21
-rw-r--r--src/core/file_sys/romfs.h15
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/file_sys/romfs_factory.h1
-rw-r--r--src/core/file_sys/savedata_factory.cpp2
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/vfs.cpp45
-rw-r--r--src/core/file_sys/vfs.h17
-rw-r--r--src/core/file_sys/vfs_concat.cpp79
-rw-r--r--src/core/file_sys/vfs_concat.h20
-rw-r--r--src/core/file_sys/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs_layered.h50
-rw-r--r--src/core/file_sys/vfs_offset.cpp2
-rw-r--r--src/core/file_sys/vfs_offset.h1
-rw-r--r--src/core/file_sys/vfs_real.cpp17
-rw-r--r--src/core/file_sys/vfs_real.h1
-rw-r--r--src/core/file_sys/vfs_static.h79
-rw-r--r--src/core/file_sys/vfs_vector.cpp56
-rw-r--r--src/core/file_sys/vfs_vector.h26
-rw-r--r--src/core/file_sys/xts_archive.cpp2
-rw-r--r--src/core/file_sys/xts_archive.h1
-rw-r--r--src/core/hle/kernel/mutex.cpp1
-rw-r--r--src/core/hle/kernel/process.cpp89
-rw-r--r--src/core/hle/kernel/process.h55
-rw-r--r--src/core/hle/kernel/scheduler.cpp14
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/svc.cpp46
-rw-r--r--src/core/hle/kernel/thread.cpp64
-rw-r--r--src/core/hle/kernel/thread.h15
-rw-r--r--src/core/hle/service/audio/audren_u.cpp10
-rw-r--r--src/core/hle/service/fatal/fatal.cpp141
-rw-r--r--src/core/hle/service/fatal/fatal.h1
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp13
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp158
-rw-r--r--src/core/hle/service/hid/irs.h27
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nifm/nifm.cpp10
-rw-r--r--src/core/hle/service/nim/nim.cpp1
-rw-r--r--src/core/hle/service/sm/controller.cpp3
-rw-r--r--src/core/hle/service/ssl/ssl.cpp6
-rw-r--r--src/core/loader/nso.cpp15
-rw-r--r--src/video_core/engines/maxwell_3d.h12
-rw-r--r--src/video_core/engines/maxwell_compute.cpp19
-rw-r--r--src/video_core/engines/maxwell_compute.h36
-rw-r--r--src/video_core/engines/shader_bytecode.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h21
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp2
-rw-r--r--src/video_core/utils.h22
-rw-r--r--src/yuzu/debugger/wait_tree.cpp1
-rw-r--r--src/yuzu/game_list.cpp19
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/game_list_p.h2
-rw-r--r--src/yuzu/main.cpp145
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
102 files changed, 1978 insertions, 249 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 83b75e61f..521b19ff7 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -79,6 +79,10 @@ u32 AudioRenderer::GetMixBufferCount() const {
79 return worker_params.mix_buffer_count; 79 return worker_params.mix_buffer_count;
80} 80}
81 81
82u32 AudioRenderer::GetState() const {
83 return stream->GetState();
84}
85
82std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 86std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
83 // Copy UpdateDataHeader struct 87 // Copy UpdateDataHeader struct
84 UpdateDataHeader config{}; 88 UpdateDataHeader config{};
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 2c4f5ab75..be923ee65 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -170,6 +170,7 @@ public:
170 u32 GetSampleRate() const; 170 u32 GetSampleRate() const;
171 u32 GetSampleCount() const; 171 u32 GetSampleCount() const;
172 u32 GetMixBufferCount() const; 172 u32 GetMixBufferCount() const;
173 u32 GetState() const;
173 174
174private: 175private:
175 class VoiceState; 176 class VoiceState;
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 449db2416..ee4aa98af 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -49,9 +49,14 @@ void Stream::Play() {
49} 49}
50 50
51void Stream::Stop() { 51void Stream::Stop() {
52 state = State::Stopped;
52 ASSERT_MSG(false, "Unimplemented"); 53 ASSERT_MSG(false, "Unimplemented");
53} 54}
54 55
56u32 Stream::GetState() const {
57 return static_cast<u32>(state);
58}
59
55s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 60s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
56 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; 61 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
57 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); 62 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 27db1112f..43eca74e1 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -72,6 +72,9 @@ public:
72 /// Gets the number of channels 72 /// Gets the number of channels
73 u32 GetNumChannels() const; 73 u32 GetNumChannels() const;
74 74
75 /// Get the state
76 u32 GetState() const;
77
75private: 78private:
76 /// Current state of the stream 79 /// Current state of the stream
77 enum class State { 80 enum class State {
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index fc14151da..d72d67994 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -59,7 +59,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
59 m_stretch_ratio = std::max(m_stretch_ratio, 0.05); 59 m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
60 m_sound_touch.setTempo(m_stretch_ratio); 60 m_sound_touch.setTempo(m_stretch_ratio);
61 61
62 LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio, 62 LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
63 backlog_fullness); 63 backlog_fullness);
64 64
65 m_sound_touch.putSamples(in, static_cast<u32>(num_in)); 65 m_sound_touch.putSamples(in, static_cast<u32>(num_in));
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index df2ce80b1..4f88de768 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -33,6 +33,8 @@
33#define NAND_DIR "nand" 33#define NAND_DIR "nand"
34#define SYSDATA_DIR "sysdata" 34#define SYSDATA_DIR "sysdata"
35#define KEYS_DIR "keys" 35#define KEYS_DIR "keys"
36#define LOAD_DIR "load"
37#define DUMP_DIR "dump"
36#define LOG_DIR "log" 38#define LOG_DIR "log"
37 39
38// Filenames 40// Filenames
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 21a0b9738..548463787 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -705,6 +705,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
705#endif 705#endif
706 paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); 706 paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
707 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); 707 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
708 paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
709 paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
708 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); 710 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
709 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); 711 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
710 // TODO: Put the logs in a better location for each OS 712 // TODO: Put the logs in a better location for each OS
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 24c1e413c..3d8fe6264 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -29,6 +29,8 @@ enum class UserPath {
29 NANDDir, 29 NANDDir,
30 RootDir, 30 RootDir,
31 SDMCDir, 31 SDMCDir,
32 LoadDir,
33 DumpDir,
32 SysDataDir, 34 SysDataDir,
33 UserDir, 35 UserDir,
34}; 36};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index efd776db6..9f5918851 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -183,6 +183,7 @@ void FileBackend::Write(const Entry& entry) {
183 SUB(Service, FS) \ 183 SUB(Service, FS) \
184 SUB(Service, GRC) \ 184 SUB(Service, GRC) \
185 SUB(Service, HID) \ 185 SUB(Service, HID) \
186 SUB(Service, IRS) \
186 SUB(Service, LBL) \ 187 SUB(Service, LBL) \
187 SUB(Service, LDN) \ 188 SUB(Service, LDN) \
188 SUB(Service, LDR) \ 189 SUB(Service, LDR) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 4d577524f..abbd056ee 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -70,6 +70,7 @@ enum class Class : ClassType {
70 Service_FS, ///< The FS (Filesystem) service 70 Service_FS, ///< The FS (Filesystem) service
71 Service_GRC, ///< The game recording service 71 Service_GRC, ///< The game recording service
72 Service_HID, ///< The HID (Human interface device) service 72 Service_HID, ///< The HID (Human interface device) service
73 Service_IRS, ///< The IRS service
73 Service_LBL, ///< The LBL (LCD backlight) service 74 Service_LBL, ///< The LBL (LCD backlight) service
74 Service_LDN, ///< The LDN (Local domain network) service 75 Service_LDN, ///< The LDN (Local domain network) service
75 Service_LDR, ///< The loader service 76 Service_LDR, ///< The loader service
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 45926c9ec..abe3b4dc2 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -9,6 +9,7 @@
9#include <atomic> 9#include <atomic>
10#include <cstddef> 10#include <cstddef>
11#include <cstring> 11#include <cstring>
12#include <new>
12#include <type_traits> 13#include <type_traits>
13#include <vector> 14#include <vector>
14#include "common/common_types.h" 15#include "common/common_types.h"
@@ -29,7 +30,7 @@ class RingBuffer {
29 static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity); 30 static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
30 static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); 31 static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
31 // Ensure lock-free. 32 // Ensure lock-free.
32 static_assert(std::atomic<std::size_t>::is_always_lock_free); 33 static_assert(std::atomic_size_t::is_always_lock_free);
33 34
34public: 35public:
35 /// Pushes slots into the ring buffer 36 /// Pushes slots into the ring buffer
@@ -102,8 +103,15 @@ public:
102private: 103private:
103 // It is important to align the below variables for performance reasons: 104 // It is important to align the below variables for performance reasons:
104 // Having them on the same cache-line would result in false-sharing between them. 105 // Having them on the same cache-line would result in false-sharing between them.
105 alignas(128) std::atomic<std::size_t> m_read_index{0}; 106 // TODO: Remove this ifdef whenever clang and GCC support
106 alignas(128) std::atomic<std::size_t> m_write_index{0}; 107 // std::hardware_destructive_interference_size.
108#if defined(_MSC_VER) && _MSC_VER >= 1911
109 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
110 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
111#else
112 alignas(128) std::atomic_size_t m_read_index{0};
113 alignas(128) std::atomic_size_t m_write_index{0};
114#endif
107 115
108 std::array<T, granularity * capacity> m_data; 116 std::array<T, granularity * capacity> m_data;
109}; 117};
diff --git a/src/common/thread.h b/src/common/thread.h
index 12a1c095c..6cbdb96a3 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -87,14 +87,6 @@ private:
87 87
88void SleepCurrentThread(int ms); 88void SleepCurrentThread(int ms);
89void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms 89void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
90
91// Use this function during a spin-wait to make the current thread
92// relax while another thread is working. This may be more efficient
93// than using events because event functions use kernel calls.
94inline void YieldCPU() {
95 std::this_thread::yield();
96}
97
98void SetCurrentThreadName(const char* name); 90void SetCurrentThreadName(const char* name);
99 91
100} // namespace Common 92} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 26f727d96..23fd6e920 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -32,6 +32,8 @@ add_library(core STATIC
32 file_sys/control_metadata.h 32 file_sys/control_metadata.h
33 file_sys/directory.h 33 file_sys/directory.h
34 file_sys/errors.h 34 file_sys/errors.h
35 file_sys/fsmitm_romfsbuild.cpp
36 file_sys/fsmitm_romfsbuild.h
35 file_sys/mode.h 37 file_sys/mode.h
36 file_sys/nca_metadata.cpp 38 file_sys/nca_metadata.cpp
37 file_sys/nca_metadata.h 39 file_sys/nca_metadata.h
@@ -59,10 +61,13 @@ add_library(core STATIC
59 file_sys/vfs.h 61 file_sys/vfs.h
60 file_sys/vfs_concat.cpp 62 file_sys/vfs_concat.cpp
61 file_sys/vfs_concat.h 63 file_sys/vfs_concat.h
64 file_sys/vfs_layered.cpp
65 file_sys/vfs_layered.h
62 file_sys/vfs_offset.cpp 66 file_sys/vfs_offset.cpp
63 file_sys/vfs_offset.h 67 file_sys/vfs_offset.h
64 file_sys/vfs_real.cpp 68 file_sys/vfs_real.cpp
65 file_sys/vfs_real.h 69 file_sys/vfs_real.h
70 file_sys/vfs_static.h
66 file_sys/vfs_vector.cpp 71 file_sys/vfs_vector.cpp
67 file_sys/vfs_vector.h 72 file_sys/vfs_vector.h
68 file_sys/xts_archive.cpp 73 file_sys/xts_archive.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 867e34932..16d528994 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,7 +6,10 @@
6 6
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/vm_manager.h" 9
10namespace Kernel {
11enum class VMAPermission : u8;
12}
10 13
11namespace Core { 14namespace Core {
12 15
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 32cd5746e..7be5a38de 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -15,6 +15,7 @@
15#include "core/gdbstub/gdbstub.h" 15#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/svc.h" 17#include "core/hle/kernel/svc.h"
18#include "core/hle/kernel/vm_manager.h"
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20namespace Core { 21namespace Core {
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index e61382d3d..4ee92ee27 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,6 +12,10 @@
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14 14
15namespace Memory {
16struct PageTable;
17}
18
15namespace Core { 19namespace Core {
16 20
17class ARM_Dynarmic_Callbacks; 21class ARM_Dynarmic_Callbacks;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 50f0a42fb..7666354dc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
64 if (concat.empty()) 64 if (concat.empty())
65 return nullptr; 65 return nullptr;
66 66
67 return FileSys::ConcatenateFiles(concat, dir->GetName()); 67 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
68 } 68 }
69 69
70 return vfs->OpenFile(path, FileSys::Mode::Read); 70 return vfs->OpenFile(path, FileSys::Mode::Read);
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 21568ad50..265f8ed9c 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -55,16 +55,16 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
55 55
56 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index); 58 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
59#else 59#else
60 arm_interface = std::make_shared<ARM_Unicorn>(); 60 arm_interface = std::make_unique<ARM_Unicorn>();
61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 61 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
62#endif 62#endif
63 } else { 63 } else {
64 arm_interface = std::make_shared<ARM_Unicorn>(); 64 arm_interface = std::make_unique<ARM_Unicorn>();
65 } 65 }
66 66
67 scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); 67 scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
68} 68}
69 69
70Cpu::~Cpu() = default; 70Cpu::~Cpu() = default;
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 685532965..ee7e04abc 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -76,7 +76,7 @@ public:
76private: 76private:
77 void Reschedule(); 77 void Reschedule();
78 78
79 std::shared_ptr<ARM_Interface> arm_interface; 79 std::unique_ptr<ARM_Interface> arm_interface;
80 std::shared_ptr<CpuBarrier> cpu_barrier; 80 std::shared_ptr<CpuBarrier> cpu_barrier;
81 std::shared_ptr<Kernel::Scheduler> scheduler; 81 std::shared_ptr<Kernel::Scheduler> scheduler;
82 82
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 205492897..6102ef476 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -2,13 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h>
5#include "core/file_sys/bis_factory.h" 6#include "core/file_sys/bis_factory.h"
6#include "core/file_sys/registered_cache.h" 7#include "core/file_sys/registered_cache.h"
7 8
8namespace FileSys { 9namespace FileSys {
9 10
10BISFactory::BISFactory(VirtualDir nand_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
11 : nand_root(std::move(nand_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
12 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_shared<RegisteredCache>(
13 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
14 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_shared<RegisteredCache>(
@@ -24,4 +25,11 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
24 return usrnand_cache; 25 return usrnand_cache;
25} 26}
26 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
29 // LayeredFS doesn't work on updates and title id-less homebrew
30 if (title_id == 0 || (title_id & 0x800) > 0)
31 return nullptr;
32 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
33}
34
27} // namespace FileSys 35} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 9523dd864..c352e0925 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,14 +17,17 @@ class RegisteredCache;
17/// registered caches. 17/// registered caches.
18class BISFactory { 18class BISFactory {
19public: 19public:
20 explicit BISFactory(VirtualDir nand_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27
26private: 28private:
27 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root;
28 31
29 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::shared_ptr<RegisteredCache> sysnand_cache;
30 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::shared_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 45fc0b574..aa1b3c17d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -463,6 +463,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
463 status = Loader::ResultStatus::Success; 463 status = Loader::ResultStatus::Success;
464} 464}
465 465
466NCA::~NCA() = default;
467
466Loader::ResultStatus NCA::GetStatus() const { 468Loader::ResultStatus NCA::GetStatus() const {
467 return status; 469 return status;
468} 470}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 00eca52da..f9f66cae9 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -81,6 +81,8 @@ class NCA : public ReadOnlyVfsDirectory {
81public: 81public:
82 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 82 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
83 u64 bktr_base_ivfc_offset = 0); 83 u64 bktr_base_ivfc_offset = 0);
84 ~NCA() override;
85
84 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
85 87
86 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 88 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index f11b91399..5b1177a03 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -28,6 +28,8 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
28 file->ReadObject(raw.get()); 28 file->ReadObject(raw.get());
29} 29}
30 30
31NACP::~NACP() = default;
32
31const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 33const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
32 if (language != Language::Default) { 34 if (language != Language::Default) {
33 return raw->language_entries.at(static_cast<u8>(language)); 35 return raw->language_entries.at(static_cast<u8>(language));
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 319bae821..43d6f0719 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -73,6 +73,8 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
73class NACP { 73class NACP {
74public: 74public:
75 explicit NACP(VirtualFile file); 75 explicit NACP(VirtualFile file);
76 ~NACP();
77
76 const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; 78 const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
77 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
78 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
new file mode 100644
index 000000000..2a913ce82
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -0,0 +1,366 @@
1/*
2 * Copyright (c) 2018 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2018 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#include <cstring>
26#include "common/alignment.h"
27#include "common/assert.h"
28#include "core/file_sys/fsmitm_romfsbuild.h"
29#include "core/file_sys/vfs.h"
30#include "core/file_sys/vfs_vector.h"
31
32namespace FileSys {
33
34constexpr u64 FS_MAX_PATH = 0x301;
35
36constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
37constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
38
39// Types for building a RomFS.
40struct RomFSHeader {
41 u64 header_size;
42 u64 dir_hash_table_ofs;
43 u64 dir_hash_table_size;
44 u64 dir_table_ofs;
45 u64 dir_table_size;
46 u64 file_hash_table_ofs;
47 u64 file_hash_table_size;
48 u64 file_table_ofs;
49 u64 file_table_size;
50 u64 file_partition_ofs;
51};
52static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
53
54struct RomFSDirectoryEntry {
55 u32 parent;
56 u32 sibling;
57 u32 child;
58 u32 file;
59 u32 hash;
60 u32 name_size;
61};
62static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
63
64struct RomFSFileEntry {
65 u32 parent;
66 u32 sibling;
67 u64 offset;
68 u64 size;
69 u32 hash;
70 u32 name_size;
71};
72static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
73
74struct RomFSBuildFileContext;
75
76struct RomFSBuildDirectoryContext {
77 std::string path;
78 u32 cur_path_ofs = 0;
79 u32 path_len = 0;
80 u32 entry_offset = 0;
81 std::shared_ptr<RomFSBuildDirectoryContext> parent;
82 std::shared_ptr<RomFSBuildDirectoryContext> child;
83 std::shared_ptr<RomFSBuildDirectoryContext> sibling;
84 std::shared_ptr<RomFSBuildFileContext> file;
85};
86
87struct RomFSBuildFileContext {
88 std::string path;
89 u32 cur_path_ofs = 0;
90 u32 path_len = 0;
91 u32 entry_offset = 0;
92 u64 offset = 0;
93 u64 size = 0;
94 std::shared_ptr<RomFSBuildDirectoryContext> parent;
95 std::shared_ptr<RomFSBuildFileContext> sibling;
96 VirtualFile source;
97};
98
99static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
100 u32 hash = parent ^ 123456789;
101 for (u32 i = 0; i < path_len; i++) {
102 hash = (hash >> 5) | (hash << 27);
103 hash ^= path[start + i];
104 }
105
106 return hash;
107}
108
109static u64 romfs_get_hash_table_count(u64 num_entries) {
110 if (num_entries < 3) {
111 return 3;
112 }
113
114 if (num_entries < 19) {
115 return num_entries | 1;
116 }
117
118 u64 count = num_entries;
119 while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
120 count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
121 count++;
122 }
123 return count;
124}
125
126void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
127 std::shared_ptr<RomFSBuildDirectoryContext> parent) {
128 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
129
130 VirtualDir dir;
131
132 if (parent->path_len == 0)
133 dir = root_romfs;
134 else
135 dir = root_romfs->GetDirectoryRelative(parent->path);
136
137 const auto entries = dir->GetEntries();
138
139 for (const auto& kv : entries) {
140 if (kv.second == VfsEntryType::Directory) {
141 const auto child = std::make_shared<RomFSBuildDirectoryContext>();
142 // Set child's path.
143 child->cur_path_ofs = parent->path_len + 1;
144 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
145 child->path = parent->path + "/" + kv.first;
146
147 // Sanity check on path_len
148 ASSERT(child->path_len < FS_MAX_PATH);
149
150 if (AddDirectory(parent, child)) {
151 child_dirs.push_back(child);
152 }
153 } else {
154 const auto child = std::make_shared<RomFSBuildFileContext>();
155 // Set child's path.
156 child->cur_path_ofs = parent->path_len + 1;
157 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
158 child->path = parent->path + "/" + kv.first;
159
160 // Sanity check on path_len
161 ASSERT(child->path_len < FS_MAX_PATH);
162
163 child->source = root_romfs->GetFileRelative(child->path);
164
165 child->size = child->source->GetSize();
166
167 AddFile(parent, child);
168 }
169 }
170
171 for (auto& child : child_dirs) {
172 this->VisitDirectory(root_romfs, child);
173 }
174}
175
176bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
177 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
178 // Check whether it's already in the known directories.
179 const auto existing = directories.find(dir_ctx->path);
180 if (existing != directories.end())
181 return false;
182
183 // Add a new directory.
184 num_dirs++;
185 dir_table_size +=
186 sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
187 dir_ctx->parent = parent_dir_ctx;
188 directories.emplace(dir_ctx->path, dir_ctx);
189
190 return true;
191}
192
193bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
194 std::shared_ptr<RomFSBuildFileContext> file_ctx) {
195 // Check whether it's already in the known files.
196 const auto existing = files.find(file_ctx->path);
197 if (existing != files.end()) {
198 return false;
199 }
200
201 // Add a new file.
202 num_files++;
203 file_table_size +=
204 sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
205 file_ctx->parent = parent_dir_ctx;
206 files.emplace(file_ctx->path, file_ctx);
207
208 return true;
209}
210
211RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
212 root = std::make_shared<RomFSBuildDirectoryContext>();
213 root->path = "\0";
214 directories.emplace(root->path, root);
215 num_dirs = 1;
216 dir_table_size = 0x18;
217
218 VisitDirectory(base, root);
219}
220
221RomFSBuildContext::~RomFSBuildContext() = default;
222
223std::map<u64, VirtualFile> RomFSBuildContext::Build() {
224 const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
225 const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
226 dir_hash_table_size = 4 * dir_hash_table_entry_count;
227 file_hash_table_size = 4 * file_hash_table_entry_count;
228
229 // Assign metadata pointers
230 RomFSHeader header{};
231
232 std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
233 std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
234
235 std::vector<u8> dir_table(dir_table_size);
236 std::vector<u8> file_table(file_table_size);
237
238 std::shared_ptr<RomFSBuildFileContext> cur_file;
239
240 // Determine file offsets.
241 u32 entry_offset = 0;
242 std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
243 for (const auto& it : files) {
244 cur_file = it.second;
245 file_partition_size = Common::AlignUp(file_partition_size, 16);
246 cur_file->offset = file_partition_size;
247 file_partition_size += cur_file->size;
248 cur_file->entry_offset = entry_offset;
249 entry_offset += sizeof(RomFSFileEntry) +
250 Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
251 prev_file = cur_file;
252 }
253 // Assign deferred parent/sibling ownership.
254 for (auto it = files.rbegin(); it != files.rend(); ++it) {
255 cur_file = it->second;
256 cur_file->sibling = cur_file->parent->file;
257 cur_file->parent->file = cur_file;
258 }
259
260 std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
261
262 // Determine directory offsets.
263 entry_offset = 0;
264 for (const auto& it : directories) {
265 cur_dir = it.second;
266 cur_dir->entry_offset = entry_offset;
267 entry_offset += sizeof(RomFSDirectoryEntry) +
268 Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
269 }
270 // Assign deferred parent/sibling ownership.
271 for (auto it = directories.rbegin(); it->second != root; ++it) {
272 cur_dir = it->second;
273 cur_dir->sibling = cur_dir->parent->child;
274 cur_dir->parent->child = cur_dir;
275 }
276
277 std::map<u64, VirtualFile> out;
278
279 // Populate file tables.
280 for (const auto& it : files) {
281 cur_file = it.second;
282 RomFSFileEntry cur_entry{};
283
284 cur_entry.parent = cur_file->parent->entry_offset;
285 cur_entry.sibling =
286 cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
287 cur_entry.offset = cur_file->offset;
288 cur_entry.size = cur_file->size;
289
290 const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
291 const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
292 cur_file->cur_path_ofs, name_size);
293 cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
294 file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
295
296 cur_entry.name_size = name_size;
297
298 out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
299 std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
300 std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
301 Common::AlignUp(cur_entry.name_size, 4));
302 std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
303 cur_file->path.data() + cur_file->cur_path_ofs, name_size);
304 }
305
306 // Populate dir tables.
307 for (const auto& it : directories) {
308 cur_dir = it.second;
309 RomFSDirectoryEntry cur_entry{};
310
311 cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
312 cur_entry.sibling =
313 cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
314 cur_entry.child =
315 cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
316 cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
317
318 const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
319 const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
320 cur_dir->path, cur_dir->cur_path_ofs, name_size);
321 cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
322 dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
323
324 cur_entry.name_size = name_size;
325
326 std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
327 sizeof(RomFSDirectoryEntry));
328 std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
329 Common::AlignUp(cur_entry.name_size, 4));
330 std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
331 cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
332 }
333
334 // Set header fields.
335 header.header_size = sizeof(RomFSHeader);
336 header.file_hash_table_size = file_hash_table_size;
337 header.file_table_size = file_table_size;
338 header.dir_hash_table_size = dir_hash_table_size;
339 header.dir_table_size = dir_table_size;
340 header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
341 header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
342 header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
343 header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
344 header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
345
346 std::vector<u8> header_data(sizeof(RomFSHeader));
347 std::memcpy(header_data.data(), &header, header_data.size());
348 out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
349
350 std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
351 dir_table_size);
352 std::size_t index = 0;
353 std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
354 index += dir_hash_table.size() * sizeof(u32);
355 std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
356 index += dir_table.size();
357 std::memcpy(metadata.data() + index, file_hash_table.data(),
358 file_hash_table.size() * sizeof(u32));
359 index += file_hash_table.size() * sizeof(u32);
360 std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
361 out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
362
363 return out;
364}
365
366} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
new file mode 100644
index 000000000..b0c3c123b
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Atmosphère-NX
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17/*
18 * Adapted by DarkLordZach for use/interaction with yuzu
19 *
20 * Modifications Copyright 2018 yuzu emulator team
21 * Licensed under GPLv2 or any later version
22 * Refer to the license.txt file included.
23 */
24
25#pragma once
26
27#include <map>
28#include <memory>
29#include <string>
30#include <boost/detail/container_fwd.hpp>
31#include "common/common_types.h"
32#include "core/file_sys/vfs.h"
33
34namespace FileSys {
35
36struct RomFSBuildDirectoryContext;
37struct RomFSBuildFileContext;
38struct RomFSDirectoryEntry;
39struct RomFSFileEntry;
40
41class RomFSBuildContext {
42public:
43 explicit RomFSBuildContext(VirtualDir base);
44 ~RomFSBuildContext();
45
46 // This finalizes the context.
47 std::map<u64, VirtualFile> Build();
48
49private:
50 VirtualDir base;
51 std::shared_ptr<RomFSBuildDirectoryContext> root;
52 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
53 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
54 u64 num_dirs = 0;
55 u64 num_files = 0;
56 u64 dir_table_size = 0;
57 u64 file_table_size = 0;
58 u64 dir_hash_table_size = 0;
59 u64 file_hash_table_size = 0;
60 u64 file_partition_size = 0;
61
62 void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
63
64 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
65 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
66 bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
67 std::shared_ptr<RomFSBuildFileContext> file_ctx);
68};
69
70} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 479916b69..6f34b7836 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -51,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco
51 : header(std::move(header)), opt_header(std::move(opt_header)), 51 : header(std::move(header)), opt_header(std::move(opt_header)),
52 content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} 52 content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
53 53
54CNMT::~CNMT() = default;
55
54u64 CNMT::GetTitleID() const { 56u64 CNMT::GetTitleID() const {
55 return header.title_id; 57 return header.title_id;
56} 58}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index da5a8dbe8..a05d155f4 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -87,6 +87,7 @@ public:
87 explicit CNMT(VirtualFile file); 87 explicit CNMT(VirtualFile file);
88 CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, 88 CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
89 std::vector<MetaRecord> meta_records); 89 std::vector<MetaRecord> meta_records);
90 ~CNMT();
90 91
91 u64 GetTitleID() const; 92 u64 GetTitleID() const;
92 u32 GetTitleVersion() const; 93 u32 GetTitleVersion() const;
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index f5b3b0175..5791c76ff 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
72 status = Loader::ResultStatus::Success; 72 status = Loader::ResultStatus::Success;
73} 73}
74 74
75PartitionFilesystem::~PartitionFilesystem() = default;
76
75Loader::ResultStatus PartitionFilesystem::GetStatus() const { 77Loader::ResultStatus PartitionFilesystem::GetStatus() const {
76 return status; 78 return status;
77} 79}
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index e80d2456b..739c63a7f 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -25,6 +25,8 @@ namespace FileSys {
25class PartitionFilesystem : public ReadOnlyVfsDirectory { 25class PartitionFilesystem : public ReadOnlyVfsDirectory {
26public: 26public:
27 explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); 27 explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
28 ~PartitionFilesystem() override;
29
28 Loader::ResultStatus GetStatus() const; 30 Loader::ResultStatus GetStatus() const;
29 31
30 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 32 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index b37b4c68b..4b3b5e665 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -11,6 +11,7 @@
11#include "core/file_sys/patch_manager.h" 11#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 12#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/romfs.h" 13#include "core/file_sys/romfs.h"
14#include "core/file_sys/vfs_layered.h"
14#include "core/hle/service/filesystem/filesystem.h" 15#include "core/hle/service/filesystem/filesystem.h"
15#include "core/loader/loader.h" 16#include "core/loader/loader.h"
16 17
@@ -31,8 +32,9 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
31 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); 32 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
32} 33}
33 34
34constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{ 35constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
35 "Update", 36 "Update",
37 "LayeredFS",
36}; 38};
37 39
38std::string FormatPatchTypeName(PatchType type) { 40std::string FormatPatchTypeName(PatchType type) {
@@ -41,6 +43,8 @@ std::string FormatPatchTypeName(PatchType type) {
41 43
42PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 44PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
43 45
46PatchManager::~PatchManager() = default;
47
44VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { 48VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
45 LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); 49 LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
46 50
@@ -64,6 +68,44 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
64 return exefs; 68 return exefs;
65} 69}
66 70
71static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
72 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
73 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
74 return;
75 }
76
77 auto extracted = ExtractRomFS(romfs);
78 if (extracted == nullptr) {
79 return;
80 }
81
82 auto patch_dirs = load_dir->GetSubdirectories();
83 std::sort(patch_dirs.begin(), patch_dirs.end(),
84 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
85
86 std::vector<VirtualDir> layers;
87 layers.reserve(patch_dirs.size() + 1);
88 for (const auto& subdir : patch_dirs) {
89 auto romfs_dir = subdir->GetSubdirectory("romfs");
90 if (romfs_dir != nullptr)
91 layers.push_back(std::move(romfs_dir));
92 }
93 layers.push_back(std::move(extracted));
94
95 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
96 if (layered == nullptr) {
97 return;
98 }
99
100 auto packed = CreateRomFS(std::move(layered));
101 if (packed == nullptr) {
102 return;
103 }
104
105 LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
106 romfs = std::move(packed);
107}
108
67VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, 109VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
68 ContentRecordType type) const { 110 ContentRecordType type) const {
69 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, 111 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
@@ -87,6 +129,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
87 } 129 }
88 } 130 }
89 131
132 // LayeredFS
133 ApplyLayeredFS(romfs, title_id, type);
134
90 return romfs; 135 return romfs;
91} 136}
92 137
@@ -112,6 +157,10 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
112 } 157 }
113 } 158 }
114 159
160 const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
161 if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
162 out.insert_or_assign(PatchType::LayeredFS, "");
163
115 return out; 164 return out;
116} 165}
117 166
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b521977b2..464f17515 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -26,6 +26,7 @@ std::string FormatTitleVersion(u32 version,
26 26
27enum class PatchType { 27enum class PatchType {
28 Update, 28 Update,
29 LayeredFS,
29}; 30};
30 31
31std::string FormatPatchTypeName(PatchType type); 32std::string FormatPatchTypeName(PatchType type);
@@ -34,6 +35,7 @@ std::string FormatPatchTypeName(PatchType type);
34class PatchManager { 35class PatchManager {
35public: 36public:
36 explicit PatchManager(u64 title_id); 37 explicit PatchManager(u64 title_id);
38 ~PatchManager();
37 39
38 // Currently tracked ExeFS patches: 40 // Currently tracked ExeFS patches:
39 // - Game Updates 41 // - Game Updates
@@ -41,6 +43,7 @@ public:
41 43
42 // Currently tracked RomFS patches: 44 // Currently tracked RomFS patches:
43 // - Game Updates 45 // - Game Updates
46 // - LayeredFS
44 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 47 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
45 ContentRecordType type = ContentRecordType::Program) const; 48 ContentRecordType type = ContentRecordType::Program) const;
46 49
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 9d19aaa6d..02319ce0f 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -12,6 +12,10 @@
12 12
13namespace FileSys { 13namespace FileSys {
14 14
15ProgramMetadata::ProgramMetadata() = default;
16
17ProgramMetadata::~ProgramMetadata() = default;
18
15Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { 19Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
16 std::size_t total_size = static_cast<std::size_t>(file->GetSize()); 20 std::size_t total_size = static_cast<std::size_t>(file->GetSize());
17 if (total_size < sizeof(Header)) 21 if (total_size < sizeof(Header))
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 3c0a49f16..1143e36c4 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -36,6 +36,9 @@ enum class ProgramFilePermission : u64 {
36 */ 36 */
37class ProgramMetadata { 37class ProgramMetadata {
38public: 38public:
39 ProgramMetadata();
40 ~ProgramMetadata();
41
39 Loader::ResultStatus Load(VirtualFile file); 42 Loader::ResultStatus Load(VirtualFile file);
40 43
41 bool Is64BitProgram() const; 44 bool Is64BitProgram() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index dad7ae10b..e9b040689 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -18,6 +18,10 @@
18#include "core/loader/loader.h" 18#include "core/loader/loader.h"
19 19
20namespace FileSys { 20namespace FileSys {
21
22// The size of blocks to use when vfs raw copying into nand.
23constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
24
21std::string RegisteredCacheEntry::DebugInfo() const { 25std::string RegisteredCacheEntry::DebugInfo() const {
22 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); 26 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
23} 27}
@@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
121 if (concat.empty()) 125 if (concat.empty())
122 return nullptr; 126 return nullptr;
123 127
124 file = FileSys::ConcatenateFiles(concat); 128 file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
125 } 129 }
126 130
127 return file; 131 return file;
@@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
480 auto out = dir->CreateFileRelative(path); 484 auto out = dir->CreateFileRelative(path);
481 if (out == nullptr) 485 if (out == nullptr)
482 return InstallResult::ErrorCopyFailed; 486 return InstallResult::ErrorCopyFailed;
483 return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed; 487 return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
488 : InstallResult::ErrorCopyFailed;
484} 489}
485 490
486bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { 491bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index f487b0cf0..c0cd59fc5 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -27,7 +27,7 @@ struct ContentRecord;
27 27
28using NcaID = std::array<u8, 0x10>; 28using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>; 30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 31
32enum class InstallResult { 32enum class InstallResult {
33 Success, 33 Success,
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 9f6e41cdf..5910f7046 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -4,8 +4,10 @@
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/fsmitm_romfsbuild.h"
7#include "core/file_sys/romfs.h" 8#include "core/file_sys/romfs.h"
8#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/file_sys/vfs_concat.h"
9#include "core/file_sys/vfs_offset.h" 11#include "core/file_sys/vfs_offset.h"
10#include "core/file_sys/vfs_vector.h" 12#include "core/file_sys/vfs_vector.h"
11 13
@@ -98,7 +100,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
98 } 100 }
99} 101}
100 102
101VirtualDir ExtractRomFS(VirtualFile file) { 103VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
102 RomFSHeader header{}; 104 RomFSHeader header{};
103 if (file->ReadObject(&header) != sizeof(RomFSHeader)) 105 if (file->ReadObject(&header) != sizeof(RomFSHeader))
104 return nullptr; 106 return nullptr;
@@ -117,9 +119,22 @@ VirtualDir ExtractRomFS(VirtualFile file) {
117 119
118 VirtualDir out = std::move(root); 120 VirtualDir out = std::move(root);
119 121
120 while (out->GetSubdirectory("") != nullptr) 122 while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
121 out = out->GetSubdirectory(""); 123 if (out->GetSubdirectories().front()->GetName() == "data" &&
124 type == RomFSExtractionType::Truncated)
125 break;
126 out = out->GetSubdirectories().front();
127 }
122 128
123 return out; 129 return out;
124} 130}
131
132VirtualFile CreateRomFS(VirtualDir dir) {
133 if (dir == nullptr)
134 return nullptr;
135
136 RomFSBuildContext ctx{dir};
137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
138}
139
125} // namespace FileSys 140} // namespace FileSys
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index e54a7d7a9..ecd1eb725 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
@@ -12,6 +13,8 @@
12 13
13namespace FileSys { 14namespace FileSys {
14 15
16struct RomFSHeader;
17
15struct IVFCLevel { 18struct IVFCLevel {
16 u64_le offset; 19 u64_le offset;
17 u64_le size; 20 u64_le size;
@@ -29,8 +32,18 @@ struct IVFCHeader {
29}; 32};
30static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); 33static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
31 34
35enum class RomFSExtractionType {
36 Full, // Includes data directory
37 Truncated, // Traverses into data directory
38};
39
32// Converts a RomFS binary blob to VFS Filesystem 40// Converts a RomFS binary blob to VFS Filesystem
33// Returns nullptr on failure 41// Returns nullptr on failure
34VirtualDir ExtractRomFS(VirtualFile file); 42VirtualDir ExtractRomFS(VirtualFile file,
43 RomFSExtractionType type = RomFSExtractionType::Truncated);
44
45// Converts a VFS filesystem into a RomFS binary
46// Returns nullptr on failure
47VirtualFile CreateRomFS(VirtualDir dir);
35 48
36} // namespace FileSys 49} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index d9d90939e..3d1a3685e 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -28,6 +28,8 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
28 ivfc_offset = app_loader.ReadRomFSIVFCOffset(); 28 ivfc_offset = app_loader.ReadRomFSIVFCOffset();
29} 29}
30 30
31RomFSFactory::~RomFSFactory() = default;
32
31ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 33ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
32 if (!updatable) 34 if (!updatable)
33 return MakeResult<VirtualFile>(file); 35 return MakeResult<VirtualFile>(file);
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 26b8f46cc..2cace8180 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -30,6 +30,7 @@ enum class StorageId : u8 {
30class RomFSFactory { 30class RomFSFactory {
31public: 31public:
32 explicit RomFSFactory(Loader::AppLoader& app_loader); 32 explicit RomFSFactory(Loader::AppLoader& app_loader);
33 ~RomFSFactory();
33 34
34 ResultVal<VirtualFile> OpenCurrentProcess(); 35 ResultVal<VirtualFile> OpenCurrentProcess();
35 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); 36 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index cddb014fc..9b2c51bbd 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -20,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const {
20 20
21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} 21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
22 22
23SaveDataFactory::~SaveDataFactory() = default;
24
23ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { 25ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
24 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 26 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
25 if (meta.zero_1 != 0) { 27 if (meta.zero_1 != 0) {
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index ba978695b..d69ef6741 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -48,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr
48class SaveDataFactory { 48class SaveDataFactory {
49public: 49public:
50 explicit SaveDataFactory(VirtualDir dir); 50 explicit SaveDataFactory(VirtualDir dir);
51 ~SaveDataFactory();
51 52
52 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
53 54
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 1120a4920..e85a2b76e 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -24,7 +24,7 @@ enum class ContentRecordType : u8;
24class NSP : public ReadOnlyVfsDirectory { 24class NSP : public ReadOnlyVfsDirectory {
25public: 25public:
26 explicit NSP(VirtualFile file); 26 explicit NSP(VirtualFile file);
27 ~NSP(); 27 ~NSP() override;
28 28
29 Loader::ResultStatus GetStatus() const; 29 Loader::ResultStatus GetStatus() const;
30 Loader::ResultStatus GetProgramStatus(u64 title_id) const; 30 Loader::ResultStatus GetProgramStatus(u64 title_id) const;
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index d7b52abfd..bfe50da73 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -399,6 +399,15 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
399 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); 399 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
400} 400}
401 401
402std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
403 std::map<std::string, VfsEntryType, std::less<>> out;
404 for (const auto& dir : GetSubdirectories())
405 out.emplace(dir->GetName(), VfsEntryType::Directory);
406 for (const auto& file : GetFiles())
407 out.emplace(file->GetName(), VfsEntryType::File);
408 return out;
409}
410
402std::string VfsDirectory::GetFullPath() const { 411std::string VfsDirectory::GetFullPath() const {
403 if (IsRoot()) 412 if (IsRoot())
404 return GetName(); 413 return GetName();
@@ -454,13 +463,41 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
454 return true; 463 return true;
455} 464}
456 465
457bool VfsRawCopy(VirtualFile src, VirtualFile dest) { 466bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
458 if (src == nullptr || dest == nullptr) 467 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
459 return false; 468 return false;
460 if (!dest->Resize(src->GetSize())) 469 if (!dest->Resize(src->GetSize()))
461 return false; 470 return false;
462 std::vector<u8> data = src->ReadAllBytes(); 471
463 return dest->WriteBytes(data, 0) == data.size(); 472 std::vector<u8> temp(std::min(block_size, src->GetSize()));
473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
474 const auto read = std::min(block_size, src->GetSize() - i);
475 const auto block = src->Read(temp.data(), read, i);
476
477 if (dest->Write(temp.data(), read, i) != read)
478 return false;
479 }
480
481 return true;
482}
483
484bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
485 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
486 return false;
487
488 for (const auto& file : src->GetFiles()) {
489 const auto out = dest->CreateFile(file->GetName());
490 if (!VfsRawCopy(file, out, block_size))
491 return false;
492 }
493
494 for (const auto& dir : src->GetSubdirectories()) {
495 const auto out = dest->CreateSubdirectory(dir->GetName());
496 if (!VfsRawCopyD(dir, out, block_size))
497 return false;
498 }
499
500 return true;
464} 501}
465 502
466VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { 503VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 74489b452..270291631 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <string_view> 10#include <string_view>
@@ -265,6 +266,10 @@ public:
265 // dest. 266 // dest.
266 virtual bool Copy(std::string_view src, std::string_view dest); 267 virtual bool Copy(std::string_view src, std::string_view dest);
267 268
269 // Gets all of the entries directly in the directory (files and dirs), returning a map between
270 // item name -> type.
271 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
272
268 // Interprets the file with name file instead as a directory of type directory. 273 // Interprets the file with name file instead as a directory of type directory.
269 // The directory must have a constructor that takes a single argument of type 274 // The directory must have a constructor that takes a single argument of type
270 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a 275 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
@@ -310,13 +315,19 @@ public:
310 bool Rename(std::string_view name) override; 315 bool Rename(std::string_view name) override;
311}; 316};
312 317
313// Compare the two files, byte-for-byte, in increments specificed by block_size 318// Compare the two files, byte-for-byte, in increments specified by block_size
314bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200); 319bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
320 std::size_t block_size = 0x1000);
315 321
316// A method that copies the raw data between two different implementations of VirtualFile. If you 322// A method that copies the raw data between two different implementations of VirtualFile. If you
317// are using the same implementation, it is probably better to use the Copy method in the parent 323// are using the same implementation, it is probably better to use the Copy method in the parent
318// directory of src/dest. 324// directory of src/dest.
319bool VfsRawCopy(VirtualFile src, VirtualFile dest); 325bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
326
327// A method that performs a similar function to VfsRawCopy above, but instead copies entire
328// directories. It suffers the same performance penalties as above and an implementation-specific
329// Copy should always be preferred.
330bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
320 331
321// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not 332// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
322// it attempts to create it and returns the new dir or nullptr on failure. 333// it attempts to create it and returns the new dir or nullptr on failure.
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 25a980cbb..16d801c0c 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -5,17 +5,22 @@
5#include <algorithm> 5#include <algorithm>
6#include <utility> 6#include <utility>
7 7
8#include "common/assert.h"
8#include "core/file_sys/vfs_concat.h" 9#include "core/file_sys/vfs_concat.h"
10#include "core/file_sys/vfs_static.h"
9 11
10namespace FileSys { 12namespace FileSys {
11 13
12VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) { 14static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
13 if (files.empty()) 15 const auto last_valid = --map.end();
14 return nullptr; 16 for (auto iter = map.begin(); iter != last_valid;) {
15 if (files.size() == 1) 17 const auto old = iter++;
16 return files[0]; 18 if (old->first + old->second->GetSize() != iter->first) {
19 return false;
20 }
21 }
17 22
18 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name))); 23 return map.begin()->first == 0;
19} 24}
20 25
21ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) 26ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
@@ -27,6 +32,48 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
27 } 32 }
28} 33}
29 34
35ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
36 : files(std::move(files_)), name(std::move(name)) {
37 ASSERT(VerifyConcatenationMapContinuity(files));
38}
39
40ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
41
42VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
43 std::string name) {
44 if (files.empty())
45 return nullptr;
46 if (files.size() == 1)
47 return files[0];
48
49 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
50}
51
52VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
53 std::map<u64, VirtualFile> files,
54 std::string name) {
55 if (files.empty())
56 return nullptr;
57 if (files.size() == 1)
58 return files.begin()->second;
59
60 const auto last_valid = --files.end();
61 for (auto iter = files.begin(); iter != last_valid;) {
62 const auto old = iter++;
63 if (old->first + old->second->GetSize() != iter->first) {
64 files.emplace(old->first + old->second->GetSize(),
65 std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
66 old->second->GetSize()));
67 }
68 }
69
70 // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
71 if (files.begin()->first != 0)
72 files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
73
74 return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
75}
76
30std::string ConcatenatedVfsFile::GetName() const { 77std::string ConcatenatedVfsFile::GetName() const {
31 if (files.empty()) 78 if (files.empty())
32 return ""; 79 return "";
@@ -60,7 +107,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
60} 107}
61 108
62std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { 109std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
63 auto entry = files.end(); 110 auto entry = --files.end();
64 for (auto iter = files.begin(); iter != files.end(); ++iter) { 111 for (auto iter = files.begin(); iter != files.end(); ++iter) {
65 if (iter->first > offset) { 112 if (iter->first > offset) {
66 entry = --iter; 113 entry = --iter;
@@ -68,20 +115,17 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
68 } 115 }
69 } 116 }
70 117
71 // Check if the entry should be the last one. The loop above will make it end(). 118 if (entry->first + entry->second->GetSize() <= offset)
72 if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
73 --entry;
74
75 if (entry == files.end())
76 return 0; 119 return 0;
77 120
78 const auto remaining = entry->second->GetSize() + offset - entry->first; 121 const auto read_in =
79 if (length > remaining) { 122 std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
80 return entry->second->Read(data, remaining, offset - entry->first) + 123 if (length > read_in) {
81 Read(data + remaining, length - remaining, offset + remaining); 124 return entry->second->Read(data, read_in, offset - entry->first) +
125 Read(data + read_in, length - read_in, offset + read_in);
82 } 126 }
83 127
84 return entry->second->Read(data, length, offset - entry->first); 128 return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
85} 129}
86 130
87std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { 131std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
@@ -91,4 +135,5 @@ std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::
91bool ConcatenatedVfsFile::Rename(std::string_view name) { 135bool ConcatenatedVfsFile::Rename(std::string_view name) {
92 return false; 136 return false;
93} 137}
138
94} // namespace FileSys 139} // namespace FileSys
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 31775db7e..c90f9d5d1 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -4,24 +4,30 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include <memory> 8#include <memory>
8#include <string_view> 9#include <string_view>
9#include <boost/container/flat_map.hpp>
10#include "core/file_sys/vfs.h" 10#include "core/file_sys/vfs.h"
11 11
12namespace FileSys { 12namespace FileSys {
13 13
14// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
15VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
16
17// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently 14// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
18// read-only. 15// read-only.
19class ConcatenatedVfsFile : public VfsFile { 16class ConcatenatedVfsFile : public VfsFile {
20 friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
21
22 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); 17 ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
18 ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
23 19
24public: 20public:
21 ~ConcatenatedVfsFile() override;
22
23 /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
24 static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
25
26 /// Convenience function that turns a map of offsets to files into a concatenated file, filling
27 /// gaps with a given filler byte.
28 static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
29 std::string name);
30
25 std::string GetName() const override; 31 std::string GetName() const override;
26 std::size_t GetSize() const override; 32 std::size_t GetSize() const override;
27 bool Resize(std::size_t new_size) override; 33 bool Resize(std::size_t new_size) override;
@@ -34,7 +40,7 @@ public:
34 40
35private: 41private:
36 // Maps starting offset to file -- more efficient. 42 // Maps starting offset to file -- more efficient.
37 boost::container::flat_map<u64, VirtualFile> files; 43 std::map<u64, VirtualFile> files;
38 std::string name; 44 std::string name;
39}; 45};
40 46
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
new file mode 100644
index 000000000..bfee01725
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -0,0 +1,132 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <utility>
7#include "core/file_sys/vfs_layered.h"
8
9namespace FileSys {
10
11LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
12 : dirs(std::move(dirs)), name(std::move(name)) {}
13
14LayeredVfsDirectory::~LayeredVfsDirectory() = default;
15
16VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
17 std::string name) {
18 if (dirs.empty())
19 return nullptr;
20 if (dirs.size() == 1)
21 return dirs[0];
22
23 return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
24}
25
26std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
27 for (const auto& layer : dirs) {
28 const auto file = layer->GetFileRelative(path);
29 if (file != nullptr)
30 return file;
31 }
32
33 return nullptr;
34}
35
36std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
37 std::string_view path) const {
38 std::vector<VirtualDir> out;
39 for (const auto& layer : dirs) {
40 auto dir = layer->GetDirectoryRelative(path);
41 if (dir != nullptr)
42 out.push_back(std::move(dir));
43 }
44
45 return MakeLayeredDirectory(std::move(out));
46}
47
48std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
49 return GetFileRelative(name);
50}
51
52std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
53 return GetDirectoryRelative(name);
54}
55
56std::string LayeredVfsDirectory::GetFullPath() const {
57 return dirs[0]->GetFullPath();
58}
59
60std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
61 std::vector<VirtualFile> out;
62 for (const auto& layer : dirs) {
63 for (const auto& file : layer->GetFiles()) {
64 if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
65 return comp->GetName() == file->GetName();
66 }) == out.end()) {
67 out.push_back(file);
68 }
69 }
70 }
71
72 return out;
73}
74
75std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const {
76 std::vector<std::string> names;
77 for (const auto& layer : dirs) {
78 for (const auto& sd : layer->GetSubdirectories()) {
79 if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
80 names.push_back(sd->GetName());
81 }
82 }
83
84 std::vector<VirtualDir> out;
85 out.reserve(names.size());
86 for (const auto& subdir : names)
87 out.push_back(GetSubdirectory(subdir));
88
89 return out;
90}
91
92bool LayeredVfsDirectory::IsWritable() const {
93 return false;
94}
95
96bool LayeredVfsDirectory::IsReadable() const {
97 return true;
98}
99
100std::string LayeredVfsDirectory::GetName() const {
101 return name.empty() ? dirs[0]->GetName() : name;
102}
103
104std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
105 return dirs[0]->GetParentDirectory();
106}
107
108std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
109 return nullptr;
110}
111
112std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
113 return nullptr;
114}
115
116bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
117 return false;
118}
119
120bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
121 return false;
122}
123
124bool LayeredVfsDirectory::Rename(std::string_view name_) {
125 name = name_;
126 return true;
127}
128
129bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
130 return false;
131}
132} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
new file mode 100644
index 000000000..d85310f57
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/file_sys/vfs.h"
9
10namespace FileSys {
11
12// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
13// one and falling back to the one after. The highest priority directory (overwrites all others)
14// should be element 0 in the dirs vector.
15class LayeredVfsDirectory : public VfsDirectory {
16 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
17
18public:
19 ~LayeredVfsDirectory() override;
20
21 /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
22 static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
23
24 std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
25 std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
26 std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
27 std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
28 std::string GetFullPath() const override;
29
30 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
31 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
32 bool IsWritable() const override;
33 bool IsReadable() const override;
34 std::string GetName() const override;
35 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
36 std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
37 std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
38 bool DeleteSubdirectory(std::string_view name) override;
39 bool DeleteFile(std::string_view name) override;
40 bool Rename(std::string_view name) override;
41
42protected:
43 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
44
45private:
46 std::vector<VirtualDir> dirs;
47 std::string name;
48};
49
50} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index f5ed291ea..a4c6719a0 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -14,6 +14,8 @@ OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_,
14 : file(file_), offset(offset_), size(size_), name(std::move(name_)), 14 : file(file_), offset(offset_), size(size_), name(std::move(name_)),
15 parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} 15 parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
16 16
17OffsetVfsFile::~OffsetVfsFile() = default;
18
17std::string OffsetVfsFile::GetName() const { 19std::string OffsetVfsFile::GetName() const {
18 return name.empty() ? file->GetName() : name; 20 return name.empty() ? file->GetName() : name;
19} 21}
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 34cb180b3..8062702a7 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -19,6 +19,7 @@ class OffsetVfsFile : public VfsFile {
19public: 19public:
20 OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0, 20 OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0,
21 std::string new_name = "", VirtualDir new_parent = nullptr); 21 std::string new_name = "", VirtualDir new_parent = nullptr);
22 ~OffsetVfsFile() override;
22 23
23 std::string GetName() const override; 24 std::string GetName() const override;
24 std::size_t GetSize() const override; 25 std::size_t GetSize() const override;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 5e242e20f..9defad04c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -413,6 +413,23 @@ std::string RealVfsDirectory::GetFullPath() const {
413 return out; 413 return out;
414} 414}
415 415
416std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
417 if (perms == Mode::Append)
418 return {};
419
420 std::map<std::string, VfsEntryType, std::less<>> out;
421 FileUtil::ForeachDirectoryEntry(
422 nullptr, path,
423 [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
424 const std::string full_path = directory + DIR_SEP + filename;
425 out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
426 : VfsEntryType::File);
427 return true;
428 });
429
430 return out;
431}
432
416bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 433bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
417 return false; 434 return false;
418} 435}
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 681c43e82..5b61db90d 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -98,6 +98,7 @@ public:
98 bool DeleteFile(std::string_view name) override; 98 bool DeleteFile(std::string_view name) override;
99 bool Rename(std::string_view name) override; 99 bool Rename(std::string_view name) override;
100 std::string GetFullPath() const override; 100 std::string GetFullPath() const override;
101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
101 102
102protected: 103protected:
103 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
new file mode 100644
index 000000000..44fab51d1
--- /dev/null
+++ b/src/core/file_sys/vfs_static.h
@@ -0,0 +1,79 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <memory>
9#include <string_view>
10
11#include "core/file_sys/vfs.h"
12
13namespace FileSys {
14
15class StaticVfsFile : public VfsFile {
16public:
17 explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
18 VirtualDir parent = nullptr)
19 : value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
20
21 std::string GetName() const override {
22 return name;
23 }
24
25 std::size_t GetSize() const override {
26 return size;
27 }
28
29 bool Resize(std::size_t new_size) override {
30 size = new_size;
31 return true;
32 }
33
34 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
35 return parent;
36 }
37
38 bool IsWritable() const override {
39 return false;
40 }
41
42 bool IsReadable() const override {
43 return true;
44 }
45
46 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
47 const auto read = std::min(length, size - offset);
48 std::fill(data, data + read, value);
49 return read;
50 }
51
52 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
53 return 0;
54 }
55
56 boost::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size)
58 return value;
59 return boost::none;
60 }
61
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
63 const auto read = std::min(length, size - offset);
64 return std::vector<u8>(read, value);
65 }
66
67 bool Rename(std::string_view new_name) override {
68 name = new_name;
69 return true;
70 }
71
72private:
73 u8 value;
74 std::size_t size;
75 std::string name;
76 VirtualDir parent;
77};
78
79} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 98e7c4598..389c7e003 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -3,16 +3,72 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring>
6#include <utility> 7#include <utility>
7#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs_vector.h"
8 9
9namespace FileSys { 10namespace FileSys {
11VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
12 : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
13
14VectorVfsFile::~VectorVfsFile() = default;
15
16std::string VectorVfsFile::GetName() const {
17 return name;
18}
19
20size_t VectorVfsFile::GetSize() const {
21 return data.size();
22}
23
24bool VectorVfsFile::Resize(size_t new_size) {
25 data.resize(new_size);
26 return true;
27}
28
29std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
30 return parent;
31}
32
33bool VectorVfsFile::IsWritable() const {
34 return true;
35}
36
37bool VectorVfsFile::IsReadable() const {
38 return true;
39}
40
41std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
42 const auto read = std::min(length, data.size() - offset);
43 std::memcpy(data_, data.data() + offset, read);
44 return read;
45}
46
47std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
48 if (offset + length > data.size())
49 data.resize(offset + length);
50 const auto write = std::min(length, data.size() - offset);
51 std::memcpy(data.data(), data_, write);
52 return write;
53}
54
55bool VectorVfsFile::Rename(std::string_view name_) {
56 name = name_;
57 return true;
58}
59
60void VectorVfsFile::Assign(std::vector<u8> new_data) {
61 data = std::move(new_data);
62}
63
10VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, 64VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
11 std::vector<VirtualDir> dirs_, std::string name_, 65 std::vector<VirtualDir> dirs_, std::string name_,
12 VirtualDir parent_) 66 VirtualDir parent_)
13 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), 67 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
14 name(std::move(name_)) {} 68 name(std::move(name_)) {}
15 69
70VectorVfsDirectory::~VectorVfsDirectory() = default;
71
16std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { 72std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
17 return files; 73 return files;
18} 74}
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 179f62e4b..48a414c98 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -8,6 +8,31 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
12class VectorVfsFile : public VfsFile {
13public:
14 explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
15 VirtualDir parent = nullptr);
16 ~VectorVfsFile() override;
17
18 std::string GetName() const override;
19 std::size_t GetSize() const override;
20 bool Resize(std::size_t new_size) override;
21 std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
22 bool IsWritable() const override;
23 bool IsReadable() const override;
24 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
25 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
26 bool Rename(std::string_view name) override;
27
28 virtual void Assign(std::vector<u8> new_data);
29
30private:
31 std::vector<u8> data;
32 VirtualDir parent;
33 std::string name;
34};
35
11// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. 36// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
12// Vector data is supplied upon construction. 37// Vector data is supplied upon construction.
13class VectorVfsDirectory : public VfsDirectory { 38class VectorVfsDirectory : public VfsDirectory {
@@ -15,6 +40,7 @@ public:
15 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, 40 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
16 std::vector<VirtualDir> dirs = {}, std::string name = "", 41 std::vector<VirtualDir> dirs = {}, std::string name = "",
17 VirtualDir parent = nullptr); 42 VirtualDir parent = nullptr);
43 ~VectorVfsDirectory() override;
18 44
19 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 45 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
20 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 46 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index e937d1403..b2b164368 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -69,6 +69,8 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
69 Common::HexArrayToString(nca_id, false))); 69 Common::HexArrayToString(nca_id, false)));
70} 70}
71 71
72NAX::~NAX() = default;
73
72Loader::ResultStatus NAX::Parse(std::string_view path) { 74Loader::ResultStatus NAX::Parse(std::string_view path) {
73 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) 75 if (file->ReadObject(header.get()) != sizeof(NAXHeader))
74 return Loader::ResultStatus::ErrorBadNAXHeader; 76 return Loader::ResultStatus::ErrorBadNAXHeader;
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 6e2fc4d2e..8fedd8585 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -33,6 +33,7 @@ class NAX : public ReadOnlyVfsDirectory {
33public: 33public:
34 explicit NAX(VirtualFile file); 34 explicit NAX(VirtualFile file);
35 explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); 35 explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
36 ~NAX() override;
36 37
37 Loader::ResultStatus GetStatus() const; 38 Loader::ResultStatus GetStatus() const;
38 39
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 51f4544be..81675eac5 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/object.h" 16#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
18#include "core/hle/result.h" 18#include "core/hle/result.h"
19#include "core/memory.h"
19 20
20namespace Kernel { 21namespace Kernel {
21 22
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 7a272d031..121f741fd 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -7,10 +7,12 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/hle/kernel/errors.h" 11#include "core/hle/kernel/errors.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/resource_limit.h" 14#include "core/hle/kernel/resource_limit.h"
15#include "core/hle/kernel/scheduler.h"
14#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
15#include "core/hle/kernel/vm_manager.h" 17#include "core/hle/kernel/vm_manager.h"
16#include "core/memory.h" 18#include "core/memory.h"
@@ -125,7 +127,92 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
125 vm_manager.LogLayout(); 127 vm_manager.LogLayout();
126 status = ProcessStatus::Running; 128 status = ProcessStatus::Running;
127 129
128 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this); 130 Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
131}
132
133void Process::PrepareForTermination() {
134 status = ProcessStatus::Exited;
135
136 const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
137 for (auto& thread : thread_list) {
138 if (thread->owner_process != this)
139 continue;
140
141 if (thread == GetCurrentThread())
142 continue;
143
144 // TODO(Subv): When are the other running/ready threads terminated?
145 ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
146 thread->status == ThreadStatus::WaitSynchAll,
147 "Exiting processes with non-waiting threads is currently unimplemented");
148
149 thread->Stop();
150 }
151 };
152
153 auto& system = Core::System::GetInstance();
154 stop_threads(system.Scheduler(0)->GetThreadList());
155 stop_threads(system.Scheduler(1)->GetThreadList());
156 stop_threads(system.Scheduler(2)->GetThreadList());
157 stop_threads(system.Scheduler(3)->GetThreadList());
158}
159
160/**
161 * Finds a free location for the TLS section of a thread.
162 * @param tls_slots The TLS page array of the thread's owner process.
163 * Returns a tuple of (page, slot, alloc_needed) where:
164 * page: The index of the first allocated TLS page that has free slots.
165 * slot: The index of the first free slot in the indicated page.
166 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
167 */
168static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
169 const std::vector<std::bitset<8>>& tls_slots) {
170 // Iterate over all the allocated pages, and try to find one where not all slots are used.
171 for (std::size_t page = 0; page < tls_slots.size(); ++page) {
172 const auto& page_tls_slots = tls_slots[page];
173 if (!page_tls_slots.all()) {
174 // We found a page with at least one free slot, find which slot it is
175 for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
176 if (!page_tls_slots.test(slot)) {
177 return std::make_tuple(page, slot, false);
178 }
179 }
180 }
181 }
182
183 return std::make_tuple(0, 0, true);
184}
185
186VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
187 auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
188
189 if (needs_allocation) {
190 tls_slots.emplace_back(0); // The page is completely available at the start
191 available_page = tls_slots.size() - 1;
192 available_slot = 0; // Use the first slot in the new page
193
194 // Allocate some memory from the end of the linear heap for this region.
195 auto& tls_memory = thread.GetTLSMemory();
196 tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
197
198 vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
199
200 vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
201 tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal);
202 }
203
204 tls_slots[available_page].set(available_slot);
205
206 return Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
207 available_slot * Memory::TLS_ENTRY_SIZE;
208}
209
210void Process::FreeTLSSlot(VAddr tls_address) {
211 const VAddr tls_base = tls_address - Memory::TLS_AREA_VADDR;
212 const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
213 const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
214
215 tls_slots[tls_page].reset(tls_slot);
129} 216}
130 217
131void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 218void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 81538f70c..04d74e572 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -131,6 +131,16 @@ public:
131 return HANDLE_TYPE; 131 return HANDLE_TYPE;
132 } 132 }
133 133
134 /// Gets the current status of the process
135 ProcessStatus GetStatus() const {
136 return status;
137 }
138
139 /// Gets the unique ID that identifies this particular process.
140 u32 GetProcessID() const {
141 return process_id;
142 }
143
134 /// Title ID corresponding to the process 144 /// Title ID corresponding to the process
135 u64 program_id; 145 u64 program_id;
136 146
@@ -154,11 +164,6 @@ public:
154 u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; 164 u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
155 u32 allowed_thread_priority_mask = 0xFFFFFFFF; 165 u32 allowed_thread_priority_mask = 0xFFFFFFFF;
156 u32 is_virtual_address_memory_enabled = 0; 166 u32 is_virtual_address_memory_enabled = 0;
157 /// Current status of the process
158 ProcessStatus status;
159
160 /// The ID of this process
161 u32 process_id = 0;
162 167
163 /** 168 /**
164 * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them 169 * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -171,13 +176,42 @@ public:
171 */ 176 */
172 void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); 177 void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
173 178
179 /**
180 * Prepares a process for termination by stopping all of its threads
181 * and clearing any other resources.
182 */
183 void PrepareForTermination();
184
174 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); 185 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
175 186
176 /////////////////////////////////////////////////////////////////////////////////////////////// 187 ///////////////////////////////////////////////////////////////////////////////////////////////
177 // Memory Management 188 // Memory Management
178 189
190 // Marks the next available region as used and returns the address of the slot.
191 VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
192
193 // Frees a used TLS slot identified by the given address
194 void FreeTLSSlot(VAddr tls_address);
195
196 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
197 ResultCode HeapFree(VAddr target, u32 size);
198
199 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
200
201 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
202
179 VMManager vm_manager; 203 VMManager vm_manager;
180 204
205private:
206 explicit Process(KernelCore& kernel);
207 ~Process() override;
208
209 /// Current status of the process
210 ProcessStatus status;
211
212 /// The ID of this process
213 u32 process_id = 0;
214
181 // Memory used to back the allocations in the regular heap. A single vector is used to cover 215 // Memory used to back the allocations in the regular heap. A single vector is used to cover
182 // the entire virtual address space extents that bound the allocations, including any holes. 216 // the entire virtual address space extents that bound the allocations, including any holes.
183 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous 217 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
@@ -197,17 +231,6 @@ public:
197 std::vector<std::bitset<8>> tls_slots; 231 std::vector<std::bitset<8>> tls_slots;
198 232
199 std::string name; 233 std::string name;
200
201 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
202 ResultCode HeapFree(VAddr target, u32 size);
203
204 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
205
206 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
207
208private:
209 explicit Process(KernelCore& kernel);
210 ~Process() override;
211}; 234};
212 235
213} // namespace Kernel 236} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 69c812f16..9faf903cf 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -17,7 +17,7 @@ namespace Kernel {
17 17
18std::mutex Scheduler::scheduler_mutex; 18std::mutex Scheduler::scheduler_mutex;
19 19
20Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {} 20Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
21 21
22Scheduler::~Scheduler() { 22Scheduler::~Scheduler() {
23 for (auto& thread : thread_list) { 23 for (auto& thread : thread_list) {
@@ -59,9 +59,9 @@ void Scheduler::SwitchContext(Thread* new_thread) {
59 // Save context for previous thread 59 // Save context for previous thread
60 if (previous_thread) { 60 if (previous_thread) {
61 previous_thread->last_running_ticks = CoreTiming::GetTicks(); 61 previous_thread->last_running_ticks = CoreTiming::GetTicks();
62 cpu_core->SaveContext(previous_thread->context); 62 cpu_core.SaveContext(previous_thread->context);
63 // Save the TPIDR_EL0 system register in case it was modified. 63 // Save the TPIDR_EL0 system register in case it was modified.
64 previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0(); 64 previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
65 65
66 if (previous_thread->status == ThreadStatus::Running) { 66 if (previous_thread->status == ThreadStatus::Running) {
67 // This is only the case when a reschedule is triggered without the current thread 67 // This is only the case when a reschedule is triggered without the current thread
@@ -91,10 +91,10 @@ void Scheduler::SwitchContext(Thread* new_thread) {
91 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); 91 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
92 } 92 }
93 93
94 cpu_core->LoadContext(new_thread->context); 94 cpu_core.LoadContext(new_thread->context);
95 cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); 95 cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
96 cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 96 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
97 cpu_core->ClearExclusiveState(); 97 cpu_core.ClearExclusiveState();
98 } else { 98 } else {
99 current_thread = nullptr; 99 current_thread = nullptr;
100 // Note: We do not reset the current process and current page table when idling because 100 // Note: We do not reset the current process and current page table when idling because
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 744990c9b..2c94641ec 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -19,7 +19,7 @@ namespace Kernel {
19 19
20class Scheduler final { 20class Scheduler final {
21public: 21public:
22 explicit Scheduler(Core::ARM_Interface* cpu_core); 22 explicit Scheduler(Core::ARM_Interface& cpu_core);
23 ~Scheduler(); 23 ~Scheduler();
24 24
25 /// Returns whether there are any threads that are ready to run. 25 /// Returns whether there are any threads that are ready to run.
@@ -72,7 +72,7 @@ private:
72 72
73 SharedPtr<Thread> current_thread = nullptr; 73 SharedPtr<Thread> current_thread = nullptr;
74 74
75 Core::ARM_Interface* cpu_core; 75 Core::ARM_Interface& cpu_core;
76 76
77 static std::mutex scheduler_mutex; 77 static std::mutex scheduler_mutex;
78}; 78};
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 371fc439e..c9d212a4c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -169,7 +169,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
169 return ERR_INVALID_HANDLE; 169 return ERR_INVALID_HANDLE;
170 } 170 }
171 171
172 *process_id = process->process_id; 172 *process_id = process->GetProcessID();
173 return RESULT_SUCCESS; 173 return RESULT_SUCCESS;
174} 174}
175 175
@@ -530,35 +530,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
530 530
531/// Exits the current process 531/// Exits the current process
532static void ExitProcess() { 532static void ExitProcess() {
533 LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); 533 auto& current_process = Core::CurrentProcess();
534 534
535 ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, 535 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
536 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
536 "Process has already exited"); 537 "Process has already exited");
537 538
538 Core::CurrentProcess()->status = ProcessStatus::Exited; 539 current_process->PrepareForTermination();
539
540 auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
541 for (auto& thread : thread_list) {
542 if (thread->owner_process != Core::CurrentProcess())
543 continue;
544
545 if (thread == GetCurrentThread())
546 continue;
547
548 // TODO(Subv): When are the other running/ready threads terminated?
549 ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
550 thread->status == ThreadStatus::WaitSynchAll,
551 "Exiting processes with non-waiting threads is currently unimplemented");
552
553 thread->Stop();
554 }
555 };
556
557 auto& system = Core::System::GetInstance();
558 stop_threads(system.Scheduler(0)->GetThreadList());
559 stop_threads(system.Scheduler(1)->GetThreadList());
560 stop_threads(system.Scheduler(2)->GetThreadList());
561 stop_threads(system.Scheduler(3)->GetThreadList());
562 540
563 // Kill the current thread 541 // Kill the current thread
564 GetCurrentThread()->Stop(); 542 GetCurrentThread()->Stop();
@@ -1039,7 +1017,7 @@ static const FunctionDef SVC_Table[] = {
1039 {0x2B, nullptr, "FlushDataCache"}, 1017 {0x2B, nullptr, "FlushDataCache"},
1040 {0x2C, nullptr, "MapPhysicalMemory"}, 1018 {0x2C, nullptr, "MapPhysicalMemory"},
1041 {0x2D, nullptr, "UnmapPhysicalMemory"}, 1019 {0x2D, nullptr, "UnmapPhysicalMemory"},
1042 {0x2E, nullptr, "GetNextThreadInfo"}, 1020 {0x2E, nullptr, "GetFutureThreadInfo"},
1043 {0x2F, nullptr, "GetLastThreadInfo"}, 1021 {0x2F, nullptr, "GetLastThreadInfo"},
1044 {0x30, nullptr, "GetResourceLimitLimitValue"}, 1022 {0x30, nullptr, "GetResourceLimitLimitValue"},
1045 {0x31, nullptr, "GetResourceLimitCurrentValue"}, 1023 {0x31, nullptr, "GetResourceLimitCurrentValue"},
@@ -1065,11 +1043,11 @@ static const FunctionDef SVC_Table[] = {
1065 {0x45, nullptr, "CreateEvent"}, 1043 {0x45, nullptr, "CreateEvent"},
1066 {0x46, nullptr, "Unknown"}, 1044 {0x46, nullptr, "Unknown"},
1067 {0x47, nullptr, "Unknown"}, 1045 {0x47, nullptr, "Unknown"},
1068 {0x48, nullptr, "AllocateUnsafeMemory"}, 1046 {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
1069 {0x49, nullptr, "FreeUnsafeMemory"}, 1047 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
1070 {0x4A, nullptr, "SetUnsafeAllocationLimit"}, 1048 {0x4A, nullptr, "SetUnsafeLimit"},
1071 {0x4B, nullptr, "CreateJitMemory"}, 1049 {0x4B, nullptr, "CreateCodeMemory"},
1072 {0x4C, nullptr, "MapJitMemory"}, 1050 {0x4C, nullptr, "ControlCodeMemory"},
1073 {0x4D, nullptr, "SleepSystem"}, 1051 {0x4D, nullptr, "SleepSystem"},
1074 {0x4E, nullptr, "ReadWriteRegister"}, 1052 {0x4E, nullptr, "ReadWriteRegister"},
1075 {0x4F, nullptr, "SetProcessActivity"}, 1053 {0x4F, nullptr, "SetProcessActivity"},
@@ -1104,7 +1082,7 @@ static const FunctionDef SVC_Table[] = {
1104 {0x6C, nullptr, "SetHardwareBreakPoint"}, 1082 {0x6C, nullptr, "SetHardwareBreakPoint"},
1105 {0x6D, nullptr, "GetDebugThreadParam"}, 1083 {0x6D, nullptr, "GetDebugThreadParam"},
1106 {0x6E, nullptr, "Unknown"}, 1084 {0x6E, nullptr, "Unknown"},
1107 {0x6F, nullptr, "GetMemoryInfo"}, 1085 {0x6F, nullptr, "GetSystemInfo"},
1108 {0x70, nullptr, "CreatePort"}, 1086 {0x70, nullptr, "CreatePort"},
1109 {0x71, nullptr, "ManageNamedPort"}, 1087 {0x71, nullptr, "ManageNamedPort"},
1110 {0x72, nullptr, "ConnectToPort"}, 1088 {0x72, nullptr, "ConnectToPort"},
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index d4183d6e3..315f65338 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -65,10 +65,7 @@ void Thread::Stop() {
65 wait_objects.clear(); 65 wait_objects.clear();
66 66
67 // Mark the TLS slot in the thread's page as free. 67 // Mark the TLS slot in the thread's page as free.
68 const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; 68 owner_process->FreeTLSSlot(tls_address);
69 const u64 tls_slot =
70 ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
71 Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
72} 69}
73 70
74void WaitCurrentThread_Sleep() { 71void WaitCurrentThread_Sleep() {
@@ -178,32 +175,6 @@ void Thread::ResumeFromWait() {
178} 175}
179 176
180/** 177/**
181 * Finds a free location for the TLS section of a thread.
182 * @param tls_slots The TLS page array of the thread's owner process.
183 * Returns a tuple of (page, slot, alloc_needed) where:
184 * page: The index of the first allocated TLS page that has free slots.
185 * slot: The index of the first free slot in the indicated page.
186 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
187 */
188static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
189 const std::vector<std::bitset<8>>& tls_slots) {
190 // Iterate over all the allocated pages, and try to find one where not all slots are used.
191 for (std::size_t page = 0; page < tls_slots.size(); ++page) {
192 const auto& page_tls_slots = tls_slots[page];
193 if (!page_tls_slots.all()) {
194 // We found a page with at least one free slot, find which slot it is
195 for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
196 if (!page_tls_slots.test(slot)) {
197 return std::make_tuple(page, slot, false);
198 }
199 }
200 }
201 }
202
203 return std::make_tuple(0, 0, true);
204}
205
206/**
207 * Resets a thread context, making it ready to be scheduled and run by the CPU 178 * Resets a thread context, making it ready to be scheduled and run by the CPU
208 * @param context Thread context to reset 179 * @param context Thread context to reset
209 * @param stack_top Address of the top of the stack 180 * @param stack_top Address of the top of the stack
@@ -264,32 +235,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
264 thread->owner_process = owner_process; 235 thread->owner_process = owner_process;
265 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); 236 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
266 thread->scheduler->AddThread(thread, priority); 237 thread->scheduler->AddThread(thread, priority);
267 238 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
268 // Find the next available TLS index, and mark it as used
269 auto& tls_slots = owner_process->tls_slots;
270
271 auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
272 if (needs_allocation) {
273 tls_slots.emplace_back(0); // The page is completely available at the start
274 available_page = tls_slots.size() - 1;
275 available_slot = 0; // Use the first slot in the new page
276
277 // Allocate some memory from the end of the linear heap for this region.
278 const std::size_t offset = thread->tls_memory->size();
279 thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
280
281 auto& vm_manager = owner_process->vm_manager;
282 vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
283
284 vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
285 thread->tls_memory, 0, Memory::PAGE_SIZE,
286 MemoryState::ThreadLocal);
287 }
288
289 // Mark the slot as used
290 tls_slots[available_page].set(available_slot);
291 thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
292 available_slot * Memory::TLS_ENTRY_SIZE;
293 239
294 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used 240 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
295 // to initialize the context 241 // to initialize the context
@@ -311,13 +257,13 @@ void Thread::BoostPriority(u32 priority) {
311} 257}
312 258
313SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 259SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
314 SharedPtr<Process> owner_process) { 260 Process& owner_process) {
315 // Setup page table so we can write to memory 261 // Setup page table so we can write to memory
316 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); 262 SetCurrentPageTable(&owner_process.vm_manager.page_table);
317 263
318 // Initialize new "main" thread 264 // Initialize new "main" thread
319 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, 265 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
320 Memory::STACK_AREA_VADDR_END, std::move(owner_process)); 266 Memory::STACK_AREA_VADDR_END, &owner_process);
321 267
322 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 268 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
323 269
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index df4748942..4250144c3 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -62,6 +62,9 @@ enum class ThreadWakeupReason {
62 62
63class Thread final : public WaitObject { 63class Thread final : public WaitObject {
64public: 64public:
65 using TLSMemory = std::vector<u8>;
66 using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
67
65 /** 68 /**
66 * Creates and returns a new thread. The new thread is immediately scheduled 69 * Creates and returns a new thread. The new thread is immediately scheduled
67 * @param kernel The kernel instance this thread will be created under. 70 * @param kernel The kernel instance this thread will be created under.
@@ -134,6 +137,14 @@ public:
134 return thread_id; 137 return thread_id;
135 } 138 }
136 139
140 TLSMemoryPtr& GetTLSMemory() {
141 return tls_memory;
142 }
143
144 const TLSMemoryPtr& GetTLSMemory() const {
145 return tls_memory;
146 }
147
137 /** 148 /**
138 * Resumes a thread from waiting 149 * Resumes a thread from waiting
139 */ 150 */
@@ -269,7 +280,7 @@ private:
269 explicit Thread(KernelCore& kernel); 280 explicit Thread(KernelCore& kernel);
270 ~Thread() override; 281 ~Thread() override;
271 282
272 std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>(); 283 TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
273}; 284};
274 285
275/** 286/**
@@ -281,7 +292,7 @@ private:
281 * @return A shared pointer to the main thread 292 * @return A shared pointer to the main thread
282 */ 293 */
283SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 294SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
284 SharedPtr<Process> owner_process); 295 Process& owner_process);
285 296
286/** 297/**
287 * Gets the current thread 298 * Gets the current thread
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 06ac6372d..fa15712cf 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -10,6 +10,7 @@
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "core/core.h"
13#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/event.h"
15#include "core/hle/kernel/hle_ipc.h" 16#include "core/hle/kernel/hle_ipc.h"
@@ -25,7 +26,7 @@ public:
25 {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, 26 {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
26 {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, 27 {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
27 {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, 28 {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
28 {3, nullptr, "GetAudioRendererState"}, 29 {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
29 {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, 30 {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
30 {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, 31 {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
31 {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, 32 {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
@@ -62,6 +63,13 @@ private:
62 LOG_DEBUG(Service_Audio, "called"); 63 LOG_DEBUG(Service_Audio, "called");
63 } 64 }
64 65
66 void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS);
69 rb.Push<u32>(renderer->GetState());
70 LOG_DEBUG(Service_Audio, "called");
71 }
72
65 void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { 73 void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
66 IPC::ResponseBuilder rb{ctx, 3}; 74 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 75 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index b436ce4e6..2212b2cdd 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -2,8 +2,17 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <cstring>
7#include <ctime>
8#include <fmt/time.h>
9#include "common/file_util.h"
5#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scm_rev.h"
12#include "common/swap.h"
13#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/process.h"
7#include "core/hle/service/fatal/fatal.h" 16#include "core/hle/service/fatal/fatal.h"
8#include "core/hle/service/fatal/fatal_p.h" 17#include "core/hle/service/fatal/fatal_p.h"
9#include "core/hle/service/fatal/fatal_u.h" 18#include "core/hle/service/fatal/fatal_u.h"
@@ -15,16 +24,142 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
15 24
16Module::Interface::~Interface() = default; 25Module::Interface::~Interface() = default;
17 26
27struct FatalInfo {
28 std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
29 // not(find a game which has non zero valeus)
30 u64_le unk0{};
31 u64_le unk1{};
32 u64_le unk2{};
33 u64_le unk3{};
34 u64_le unk4{};
35 u64_le unk5{};
36 u64_le unk6{};
37
38 std::array<u64_le, 32> backtrace{};
39 u64_le unk7{};
40 u64_le unk8{};
41 u32_le backtrace_size{};
42 u32_le unk9{};
43 u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
44};
45static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
46
47enum class FatalType : u32 {
48 ErrorReportAndScreen = 0,
49 ErrorReport = 1,
50 ErrorScreen = 2,
51};
52
53static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
54 const auto title_id = Core::CurrentProcess()->program_id;
55 std::string crash_report =
56 fmt::format("Yuzu {}-{} crash report\n"
57 "Title ID: {:016x}\n"
58 "Result: 0x{:X} ({:04}-{:04d})\n"
59 "\n",
60 Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
61 2000 + static_cast<u32>(error_code.module.Value()),
62 static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
63 if (info.backtrace_size != 0x0) {
64 crash_report += "Registers:\n";
65 // TODO(ogniK): This is just a guess, find a game which actually has non zero values
66 for (size_t i = 0; i < info.registers.size(); i++) {
67 crash_report +=
68 fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
69 }
70 crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
71 crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
72 crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
73 crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
74 crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
75 crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
76 crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
77 crash_report += "\nBacktrace:\n";
78 for (size_t i = 0; i < info.backtrace_size; i++) {
79 crash_report +=
80 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
81 }
82 crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
83 crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
84 crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
85 crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
86 }
87
88 LOG_ERROR(Service_Fatal, "{}", crash_report);
89
90 const std::string crashreport_dir =
91 FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
92
93 if (!FileUtil::CreateFullPath(crashreport_dir)) {
94 LOG_ERROR(
95 Service_Fatal,
96 "Unable to create crash report directory. Possible log directory permissions issue.");
97 return;
98 }
99
100 const std::time_t t = std::time(nullptr);
101 const std::string crashreport_filename =
102 fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
103
104 auto file = FileUtil::IOFile(crashreport_filename, "wb");
105 if (file.IsOpen()) {
106 file.WriteString(crash_report);
107 LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
108 } else {
109 LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
110 }
111}
112
113static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
114 LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
115 switch (fatal_type) {
116 case FatalType::ErrorReportAndScreen:
117 GenerateErrorReport(error_code, info);
118 [[fallthrough]];
119 case FatalType::ErrorScreen:
120 // Since we have no fatal:u error screen. We should just kill execution instead
121 ASSERT(false);
122 break;
123 // Should not throw a fatal screen but should generate an error report
124 case FatalType::ErrorReport:
125 GenerateErrorReport(error_code, info);
126 break;
127 };
128}
129
130void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
131 LOG_ERROR(Service_Fatal, "called");
132 IPC::RequestParser rp{ctx};
133 auto error_code = rp.Pop<ResultCode>();
134
135 ThrowFatalError(error_code, FatalType::ErrorScreen, {});
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(RESULT_SUCCESS);
138}
139
18void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { 140void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
141 LOG_ERROR(Service_Fatal, "called");
19 IPC::RequestParser rp(ctx); 142 IPC::RequestParser rp(ctx);
20 u32 error_code = rp.Pop<u32>(); 143 auto error_code = rp.Pop<ResultCode>();
21 LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); 144 auto fatal_type = rp.PopEnum<FatalType>();
145
146 ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
22 IPC::ResponseBuilder rb{ctx, 2}; 147 IPC::ResponseBuilder rb{ctx, 2};
23 rb.Push(RESULT_SUCCESS); 148 rb.Push(RESULT_SUCCESS);
24} 149}
25 150
26void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { 151void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
27 LOG_WARNING(Service_Fatal, "(STUBBED) called"); 152 LOG_ERROR(Service_Fatal, "called");
153 IPC::RequestParser rp(ctx);
154 auto error_code = rp.Pop<ResultCode>();
155 auto fatal_type = rp.PopEnum<FatalType>();
156 auto fatal_info = ctx.ReadBuffer();
157 FatalInfo info{};
158
159 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
160 std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
161
162 ThrowFatalError(error_code, fatal_type, info);
28 IPC::ResponseBuilder rb{ctx, 2}; 163 IPC::ResponseBuilder rb{ctx, 2};
29 rb.Push(RESULT_SUCCESS); 164 rb.Push(RESULT_SUCCESS);
30} 165}
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index 4d9a5be52..09371ff7f 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -15,6 +15,7 @@ public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 15 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override; 16 ~Interface() override;
17 17
18 void ThrowFatal(Kernel::HLERequestContext& ctx);
18 void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); 19 void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
19 void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); 20 void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
20 21
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index befc307cf..1572a2051 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -8,7 +8,7 @@ namespace Service::Fatal {
8 8
9Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { 9Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
10 static const FunctionInfo functions[] = { 10 static const FunctionInfo functions[] = {
11 {0, nullptr, "ThrowFatal"}, 11 {0, &Fatal_U::ThrowFatal, "ThrowFatal"},
12 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, 12 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
13 {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"}, 13 {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
14 }; 14 };
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index d349ee686..aed2abb71 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -343,6 +343,15 @@ std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
343 return sdmc_factory->GetSDMCContents(); 343 return sdmc_factory->GetSDMCContents();
344} 344}
345 345
346FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
347 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
348
349 if (bis_factory == nullptr)
350 return nullptr;
351
352 return bis_factory->GetModificationLoadRoot(title_id);
353}
354
346void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 355void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
347 if (overwrite) { 356 if (overwrite) {
348 bis_factory = nullptr; 357 bis_factory = nullptr;
@@ -354,9 +363,11 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
354 FileSys::Mode::ReadWrite); 363 FileSys::Mode::ReadWrite);
355 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 364 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
356 FileSys::Mode::ReadWrite); 365 FileSys::Mode::ReadWrite);
366 auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
367 FileSys::Mode::ReadWrite);
357 368
358 if (bis_factory == nullptr) 369 if (bis_factory == nullptr)
359 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory); 370 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
360 if (save_data_factory == nullptr) 371 if (save_data_factory == nullptr)
361 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 372 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
362 if (sdmc_factory == nullptr) 373 if (sdmc_factory == nullptr)
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index aab65a2b8..7039a2247 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -52,6 +52,8 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
54 54
55FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
56
55// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 57// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
56// above is called. 58// above is called.
57void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 59void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index e587ad0d8..872e3c344 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -2,6 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/swap.h"
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/shared_memory.h"
5#include "core/hle/service/hid/irs.h" 10#include "core/hle/service/hid/irs.h"
6 11
7namespace Service::HID { 12namespace Service::HID {
@@ -9,28 +14,145 @@ namespace Service::HID {
9IRS::IRS() : ServiceFramework{"irs"} { 14IRS::IRS() : ServiceFramework{"irs"} {
10 // clang-format off 15 // clang-format off
11 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
12 {302, nullptr, "ActivateIrsensor"}, 17 {302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
13 {303, nullptr, "DeactivateIrsensor"}, 18 {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
14 {304, nullptr, "GetIrsensorSharedMemoryHandle"}, 19 {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
15 {305, nullptr, "StopImageProcessor"}, 20 {305, &IRS::StopImageProcessor, "StopImageProcessor"},
16 {306, nullptr, "RunMomentProcessor"}, 21 {306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
17 {307, nullptr, "RunClusteringProcessor"}, 22 {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
18 {308, nullptr, "RunImageTransferProcessor"}, 23 {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
19 {309, nullptr, "GetImageTransferProcessorState"}, 24 {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
20 {310, nullptr, "RunTeraPluginProcessor"}, 25 {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
21 {311, nullptr, "GetNpadIrCameraHandle"}, 26 {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
22 {312, nullptr, "RunPointingProcessor"}, 27 {312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
23 {313, nullptr, "SuspendImageProcessor"}, 28 {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
24 {314, nullptr, "CheckFirmwareVersion"}, 29 {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
25 {315, nullptr, "SetFunctionLevel"}, 30 {315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
26 {316, nullptr, "RunImageTransferExProcessor"}, 31 {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
27 {317, nullptr, "RunIrLedProcessor"}, 32 {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
28 {318, nullptr, "StopImageProcessorAsync"}, 33 {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
29 {319, nullptr, "ActivateIrsensorWithFunctionLevel"}, 34 {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
30 }; 35 };
31 // clang-format on 36 // clang-format on
32 37
33 RegisterHandlers(functions); 38 RegisterHandlers(functions);
39
40 auto& kernel = Core::System::GetInstance().Kernel();
41 shared_mem = Kernel::SharedMemory::Create(
42 kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
43 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
44}
45
46void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
47 IPC::ResponseBuilder rb{ctx, 2};
48 rb.Push(RESULT_SUCCESS);
49 LOG_WARNING(Service_IRS, "(STUBBED) called");
50}
51
52void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
53 IPC::ResponseBuilder rb{ctx, 2};
54 rb.Push(RESULT_SUCCESS);
55 LOG_WARNING(Service_IRS, "(STUBBED) called");
56}
57
58void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
59 IPC::ResponseBuilder rb{ctx, 2, 1};
60 rb.Push(RESULT_SUCCESS);
61 rb.PushCopyObjects(shared_mem);
62 LOG_DEBUG(Service_IRS, "called");
63}
64
65void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(RESULT_SUCCESS);
68 LOG_WARNING(Service_IRS, "(STUBBED) called");
69}
70
71void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(RESULT_SUCCESS);
74 LOG_WARNING(Service_IRS, "(STUBBED) called");
75}
76
77void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_IRS, "(STUBBED) called");
81}
82
83void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS);
86 LOG_WARNING(Service_IRS, "(STUBBED) called");
87}
88
89void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
90 IPC::ResponseBuilder rb{ctx, 5};
91 rb.Push(RESULT_SUCCESS);
92 rb.PushRaw<u64>(CoreTiming::GetTicks());
93 rb.PushRaw<u32>(0);
94 LOG_WARNING(Service_IRS, "(STUBBED) called");
95}
96
97void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
98 IPC::ResponseBuilder rb{ctx, 2};
99 rb.Push(RESULT_SUCCESS);
100 LOG_WARNING(Service_IRS, "(STUBBED) called");
101}
102
103void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
104 IPC::ResponseBuilder rb{ctx, 3};
105 rb.Push(RESULT_SUCCESS);
106 rb.PushRaw<u32>(device_handle);
107 LOG_WARNING(Service_IRS, "(STUBBED) called");
108}
109
110void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(RESULT_SUCCESS);
113 LOG_WARNING(Service_IRS, "(STUBBED) called");
114}
115
116void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(RESULT_SUCCESS);
119 LOG_WARNING(Service_IRS, "(STUBBED) called");
120}
121
122void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
123 IPC::ResponseBuilder rb{ctx, 2};
124 rb.Push(RESULT_SUCCESS);
125 LOG_WARNING(Service_IRS, "(STUBBED) called");
126}
127
128void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
129 IPC::ResponseBuilder rb{ctx, 2};
130 rb.Push(RESULT_SUCCESS);
131 LOG_WARNING(Service_IRS, "(STUBBED) called");
132}
133
134void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
135 IPC::ResponseBuilder rb{ctx, 2};
136 rb.Push(RESULT_SUCCESS);
137 LOG_WARNING(Service_IRS, "(STUBBED) called");
138}
139
140void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2};
142 rb.Push(RESULT_SUCCESS);
143 LOG_WARNING(Service_IRS, "(STUBBED) called");
144}
145
146void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(RESULT_SUCCESS);
149 LOG_WARNING(Service_IRS, "(STUBBED) called");
150}
151
152void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
153 IPC::ResponseBuilder rb{ctx, 2};
154 rb.Push(RESULT_SUCCESS);
155 LOG_WARNING(Service_IRS, "(STUBBED) called");
34} 156}
35 157
36IRS::~IRS() = default; 158IRS::~IRS() = default;
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 6fb16b45d..12de6bfb3 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,14 +4,41 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/object.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
10namespace Kernel {
11class SharedMemory;
12}
13
9namespace Service::HID { 14namespace Service::HID {
10 15
11class IRS final : public ServiceFramework<IRS> { 16class IRS final : public ServiceFramework<IRS> {
12public: 17public:
13 explicit IRS(); 18 explicit IRS();
14 ~IRS() override; 19 ~IRS() override;
20
21private:
22 void ActivateIrsensor(Kernel::HLERequestContext& ctx);
23 void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
24 void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
25 void StopImageProcessor(Kernel::HLERequestContext& ctx);
26 void RunMomentProcessor(Kernel::HLERequestContext& ctx);
27 void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
28 void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
29 void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
30 void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
31 void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
32 void RunPointingProcessor(Kernel::HLERequestContext& ctx);
33 void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
34 void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
35 void SetFunctionLevel(Kernel::HLERequestContext& ctx);
36 void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
37 void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
38 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
39 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
40 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
41 const u32 device_handle{0xABCD};
15}; 42};
16 43
17class IRS_SYS final : public ServiceFramework<IRS_SYS> { 44class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index f8d2127d9..8c07a05c2 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/event.h" 8#include "core/hle/kernel/event.h"
8#include "core/hle/service/hid/hid.h" 9#include "core/hle/service/hid/hid.h"
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index b6075f256..10611ed6a 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -31,7 +31,7 @@ public:
31 {1, &IRequest::GetResult, "GetResult"}, 31 {1, &IRequest::GetResult, "GetResult"},
32 {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, 32 {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
33 {3, &IRequest::Cancel, "Cancel"}, 33 {3, &IRequest::Cancel, "Cancel"},
34 {4, nullptr, "Submit"}, 34 {4, &IRequest::Submit, "Submit"},
35 {5, nullptr, "SetRequirement"}, 35 {5, nullptr, "SetRequirement"},
36 {6, nullptr, "SetRequirementPreset"}, 36 {6, nullptr, "SetRequirementPreset"},
37 {8, nullptr, "SetPriority"}, 37 {8, nullptr, "SetPriority"},
@@ -61,11 +61,17 @@ public:
61 } 61 }
62 62
63private: 63private:
64 void Submit(Kernel::HLERequestContext& ctx) {
65 LOG_WARNING(Service_NIFM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(RESULT_SUCCESS);
68 }
69
64 void GetRequestState(Kernel::HLERequestContext& ctx) { 70 void GetRequestState(Kernel::HLERequestContext& ctx) {
65 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 71 LOG_WARNING(Service_NIFM, "(STUBBED) called");
66 IPC::ResponseBuilder rb{ctx, 3}; 72 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 73 rb.Push(RESULT_SUCCESS);
68 rb.Push<u32>(3); 74 rb.Push<u32>(0);
69 } 75 }
70 76
71 void GetResult(Kernel::HLERequestContext& ctx) { 77 void GetResult(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index c1737defa..261ad539c 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -4,6 +4,7 @@
4 4
5#include <chrono> 5#include <chrono>
6#include <ctime> 6#include <ctime>
7#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
9#include "core/hle/service/nim/nim.h" 10#include "core/hle/service/nim/nim.h"
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index cdf328a26..98f6e4111 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -2,8 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/client_session.h"
9#include "core/hle/kernel/server_session.h"
7#include "core/hle/kernel/session.h" 10#include "core/hle/kernel/session.h"
8#include "core/hle/service/sm/controller.h" 11#include "core/hle/service/sm/controller.h"
9 12
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index fe0a318ee..bc4f7a437 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -103,6 +103,7 @@ public:
103 } 103 }
104 104
105private: 105private:
106 u32 ssl_version{};
106 void CreateContext(Kernel::HLERequestContext& ctx) { 107 void CreateContext(Kernel::HLERequestContext& ctx) {
107 LOG_WARNING(Service_SSL, "(STUBBED) called"); 108 LOG_WARNING(Service_SSL, "(STUBBED) called");
108 109
@@ -112,10 +113,9 @@ private:
112 } 113 }
113 114
114 void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { 115 void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
115 LOG_WARNING(Service_SSL, "(STUBBED) called"); 116 LOG_DEBUG(Service_SSL, "called");
116 IPC::RequestParser rp{ctx}; 117 IPC::RequestParser rp{ctx};
117 u32 unk1 = rp.Pop<u32>(); // Probably minor/major? 118 ssl_version = rp.Pop<u32>();
118 u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
119 119
120 IPC::ResponseBuilder rb{ctx, 2}; 120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(RESULT_SUCCESS); 121 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 3c6306818..78a4438c4 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -32,11 +32,18 @@ static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect
32 32
33struct NsoHeader { 33struct NsoHeader {
34 u32_le magic; 34 u32_le magic;
35 INSERT_PADDING_BYTES(0xc); 35 u32_le version;
36 INSERT_PADDING_WORDS(1);
37 u8 flags;
36 std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) 38 std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
37 u32_le bss_size; 39 u32_le bss_size;
38 INSERT_PADDING_BYTES(0x1c); 40 INSERT_PADDING_BYTES(0x1c);
39 std::array<u32_le, 3> segments_compressed_size; 41 std::array<u32_le, 3> segments_compressed_size;
42
43 bool IsSegmentCompressed(size_t segment_num) const {
44 ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
45 return ((flags >> segment_num) & 1);
46 }
40}; 47};
41static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); 48static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
42static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); 49static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
@@ -105,9 +112,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
105 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); 112 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
106 std::vector<u8> program_image; 113 std::vector<u8> program_image;
107 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 114 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
108 const std::vector<u8> compressed_data = 115 std::vector<u8> data =
109 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 116 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
110 std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); 117 if (nso_header.IsSegmentCompressed(i)) {
118 data = DecompressSegment(data, nso_header.segments[i]);
119 }
111 program_image.resize(nso_header.segments[i].location); 120 program_image.resize(nso_header.segments[i].location);
112 program_image.insert(program_image.end(), data.begin(), data.end()); 121 program_image.insert(program_image.end(), data.begin(), data.end());
113 codeset->segments[i].addr = nso_header.segments[i].location; 122 codeset->segments[i].addr = nso_header.segments[i].location;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index b81b0723d..16cdfc7e2 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -461,7 +461,11 @@ public:
461 u32 entry; 461 u32 entry;
462 } macros; 462 } macros;
463 463
464 INSERT_PADDING_WORDS(0x1B8); 464 INSERT_PADDING_WORDS(0x189);
465
466 u32 tfb_enabled;
467
468 INSERT_PADDING_WORDS(0x2E);
465 469
466 RenderTargetConfig rt[NumRenderTargets]; 470 RenderTargetConfig rt[NumRenderTargets];
467 471
@@ -594,7 +598,9 @@ public:
594 598
595 u32 depth_write_enabled; 599 u32 depth_write_enabled;
596 600
597 INSERT_PADDING_WORDS(0x7); 601 u32 alpha_test_enabled;
602
603 INSERT_PADDING_WORDS(0x6);
598 604
599 u32 d3d_cull_mode; 605 u32 d3d_cull_mode;
600 606
@@ -977,6 +983,7 @@ private:
977 "Field " #field_name " has invalid position") 983 "Field " #field_name " has invalid position")
978 984
979ASSERT_REG_POSITION(macros, 0x45); 985ASSERT_REG_POSITION(macros, 0x45);
986ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
980ASSERT_REG_POSITION(rt, 0x200); 987ASSERT_REG_POSITION(rt, 0x200);
981ASSERT_REG_POSITION(viewport_transform[0], 0x280); 988ASSERT_REG_POSITION(viewport_transform[0], 0x280);
982ASSERT_REG_POSITION(viewport, 0x300); 989ASSERT_REG_POSITION(viewport, 0x300);
@@ -996,6 +1003,7 @@ ASSERT_REG_POSITION(zeta_height, 0x48b);
996ASSERT_REG_POSITION(depth_test_enable, 0x4B3); 1003ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
997ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); 1004ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
998ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); 1005ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
1006ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
999ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 1007ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
1000ASSERT_REG_POSITION(depth_test_func, 0x4C3); 1008ASSERT_REG_POSITION(depth_test_func, 0x4C3);
1001ASSERT_REG_POSITION(blend, 0x4CF); 1009ASSERT_REG_POSITION(blend, 0x4CF);
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index e4e5f9e5e..59e28b22d 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -2,12 +2,29 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/core.h"
5#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
6 8
7namespace Tegra { 9namespace Tegra {
8namespace Engines { 10namespace Engines {
9 11
10void MaxwellCompute::WriteReg(u32 method, u32 value) {} 12void MaxwellCompute::WriteReg(u32 method, u32 value) {
13 ASSERT_MSG(method < Regs::NUM_REGS,
14 "Invalid MaxwellCompute register, increase the size of the Regs structure");
15
16 regs.reg_array[method] = value;
17
18 switch (method) {
19 case MAXWELL_COMPUTE_REG_INDEX(compute): {
20 LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
21 UNREACHABLE();
22 break;
23 }
24 default:
25 break;
26 }
27}
11 28
12} // namespace Engines 29} // namespace Engines
13} // namespace Tegra 30} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h
index 2b3e4ced6..6ea934fb9 100644
--- a/src/video_core/engines/maxwell_compute.h
+++ b/src/video_core/engines/maxwell_compute.h
@@ -4,17 +4,53 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
7#include "common/common_types.h" 11#include "common/common_types.h"
8 12
9namespace Tegra::Engines { 13namespace Tegra::Engines {
10 14
15#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
16 (offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
17
11class MaxwellCompute final { 18class MaxwellCompute final {
12public: 19public:
13 MaxwellCompute() = default; 20 MaxwellCompute() = default;
14 ~MaxwellCompute() = default; 21 ~MaxwellCompute() = default;
15 22
23 struct Regs {
24 static constexpr std::size_t NUM_REGS = 0xCF8;
25
26 union {
27 struct {
28 INSERT_PADDING_WORDS(0x281);
29
30 union {
31 u32 compute_end;
32 BitField<0, 1, u32> unknown;
33 } compute;
34
35 INSERT_PADDING_WORDS(0xA76);
36 };
37 std::array<u32, NUM_REGS> reg_array;
38 };
39 } regs{};
40
41 static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
42 "MaxwellCompute Regs has wrong size");
43
16 /// Write the value to the register identified by method. 44 /// Write the value to the register identified by method.
17 void WriteReg(u32 method, u32 value); 45 void WriteReg(u32 method, u32 value);
18}; 46};
19 47
48#define ASSERT_REG_POSITION(field_name, position) \
49 static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
50 "Field " #field_name " has invalid position")
51
52ASSERT_REG_POSITION(compute, 0x281);
53
54#undef ASSERT_REG_POSITION
55
20} // namespace Tegra::Engines 56} // namespace Tegra::Engines
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 7e1de0fa1..b1f137b9c 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -5,9 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <bitset> 7#include <bitset>
8#include <cstring>
9#include <map>
10#include <string> 8#include <string>
9#include <tuple>
11#include <vector> 10#include <vector>
12 11
13#include <boost/optional.hpp> 12#include <boost/optional.hpp>
@@ -315,17 +314,29 @@ enum class TextureMiscMode : u64 {
315 PTP, 314 PTP,
316}; 315};
317 316
318enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 }; 317enum class IpaInterpMode : u64 {
319enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 }; 318 Linear = 0,
319 Perspective = 1,
320 Flat = 2,
321 Sc = 3,
322};
323
324enum class IpaSampleMode : u64 {
325 Default = 0,
326 Centroid = 1,
327 Offset = 2,
328};
320 329
321struct IpaMode { 330struct IpaMode {
322 IpaInterpMode interpolation_mode; 331 IpaInterpMode interpolation_mode;
323 IpaSampleMode sampling_mode; 332 IpaSampleMode sampling_mode;
324 inline bool operator==(const IpaMode& a) { 333
325 return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode); 334 bool operator==(const IpaMode& a) const {
335 return std::tie(interpolation_mode, sampling_mode) ==
336 std::tie(a.interpolation_mode, a.sampling_mode);
326 } 337 }
327 inline bool operator!=(const IpaMode& a) { 338 bool operator!=(const IpaMode& a) const {
328 return !((*this) == a); 339 return !operator==(a);
329 } 340 }
330}; 341};
331 342
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e37acbfac..44850d193 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -383,7 +383,7 @@ void RasterizerOpenGL::Clear() {
383 bool use_stencil{}; 383 bool use_stencil{};
384 384
385 OpenGLState clear_state; 385 OpenGLState clear_state;
386 clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer; 386 clear_state.draw.draw_framebuffer = framebuffer.handle;
387 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; 387 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
388 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; 388 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
389 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; 389 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
@@ -450,6 +450,8 @@ void RasterizerOpenGL::DrawArrays() {
450 SyncBlendState(); 450 SyncBlendState();
451 SyncLogicOpState(); 451 SyncLogicOpState();
452 SyncCullMode(); 452 SyncCullMode();
453 SyncAlphaTest();
454 SyncTransformFeedback();
453 455
454 // TODO(bunnei): Sync framebuffer_scale uniform here 456 // TODO(bunnei): Sync framebuffer_scale uniform here
455 // TODO(bunnei): Sync scissorbox uniform(s) here 457 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -883,4 +885,24 @@ void RasterizerOpenGL::SyncLogicOpState() {
883 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 885 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
884} 886}
885 887
888void RasterizerOpenGL::SyncAlphaTest() {
889 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
890
891 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
892 // implemented with a test+discard in fragment shaders.
893 if (regs.alpha_test_enabled != 0) {
894 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
895 UNREACHABLE();
896 }
897}
898
899void RasterizerOpenGL::SyncTransformFeedback() {
900 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
901
902 if (regs.tfb_enabled != 0) {
903 LOG_CRITICAL(Render_OpenGL, "Transform feedbacks are not implemented");
904 UNREACHABLE();
905 }
906}
907
886} // namespace OpenGL 908} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bf9560bdc..c3f1e14bf 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -158,6 +158,12 @@ private:
158 /// Syncs the LogicOp state to match the guest state 158 /// Syncs the LogicOp state to match the guest state
159 void SyncLogicOpState(); 159 void SyncLogicOpState();
160 160
161 /// Syncs the alpha test state to match the guest state
162 void SyncAlphaTest();
163
164 /// Syncs the transform feedback state to match the guest state
165 void SyncTransformFeedback();
166
161 bool has_ARB_direct_state_access = false; 167 bool has_ARB_direct_state_access = false;
162 bool has_ARB_multi_bind = false; 168 bool has_ARB_multi_bind = false;
163 bool has_ARB_separate_shader_objects = false; 169 bool has_ARB_separate_shader_objects = false;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 86682d7cb..24a540258 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -141,8 +141,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
141 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 141 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
142 true}, // BC7U 142 true}, // BC7U
143 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, 143 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8,
144 ComponentType::UNorm, true}, // BC6H_UF16 144 ComponentType::Float, true}, // BC6H_UF16
145 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 145 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
146 true}, // BC6H_SF16 146 true}, // BC6H_SF16
147 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 147 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
148 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U 148 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
@@ -501,6 +501,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
501 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); 501 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
502 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 502 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
503 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 503 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
504
505 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
506 SurfaceParams::SurfaceTargetName(params.target));
504} 507}
505 508
506static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 509static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index d7a4bc37f..80c5f324b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -137,6 +137,27 @@ struct SurfaceParams {
137 } 137 }
138 } 138 }
139 139
140 static std::string SurfaceTargetName(SurfaceTarget target) {
141 switch (target) {
142 case SurfaceTarget::Texture1D:
143 return "Texture1D";
144 case SurfaceTarget::Texture2D:
145 return "Texture2D";
146 case SurfaceTarget::Texture3D:
147 return "Texture3D";
148 case SurfaceTarget::Texture1DArray:
149 return "Texture1DArray";
150 case SurfaceTarget::Texture2DArray:
151 return "Texture2DArray";
152 case SurfaceTarget::TextureCubemap:
153 return "TextureCubemap";
154 default:
155 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
156 UNREACHABLE();
157 return fmt::format("TextureUnknown({})", static_cast<u32>(target));
158 }
159 }
160
140 /** 161 /**
141 * Gets the compression factor for the specified PixelFormat. This applies to just the 162 * Gets the compression factor for the specified PixelFormat. This applies to just the
142 * "compressed width" and "compressed height", not the overall compression factor of a 163 * "compressed width" and "compressed height", not the overall compression factor of a
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 894fe6eae..7cd8f91e4 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -8,6 +8,7 @@
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_opengl/gl_shader_cache.h" 9#include "video_core/renderer_opengl/gl_shader_cache.h"
10#include "video_core/renderer_opengl/gl_shader_manager.h" 10#include "video_core/renderer_opengl/gl_shader_manager.h"
11#include "video_core/utils.h"
11 12
12namespace OpenGL { 13namespace OpenGL {
13 14
@@ -83,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
83 shader.Create(program_result.first.c_str(), gl_type); 84 shader.Create(program_result.first.c_str(), gl_type);
84 program.Create(true, shader.handle); 85 program.Create(true, shader.handle);
85 SetShaderUniformBlockBindings(program.handle); 86 SetShaderUniformBlockBindings(program.handle);
87 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
86} 88}
87 89
88GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { 90GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index af99132ba..e5173e20a 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -4,6 +4,7 @@
4 4
5#include <iterator> 5#include <iterator>
6#include <glad/glad.h> 6#include <glad/glad.h>
7#include "common/assert.h"
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "video_core/renderer_opengl/gl_state.h" 9#include "video_core/renderer_opengl/gl_state.h"
9 10
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e3e24b9e7..9a93029d8 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -7,12 +7,8 @@
7#include <array> 7#include <array>
8#include <glad/glad.h> 8#include <glad/glad.h>
9 9
10#include "video_core/engines/maxwell_3d.h"
11
12namespace OpenGL { 10namespace OpenGL {
13 11
14using Regs = Tegra::Engines::Maxwell3D::Regs;
15
16namespace TextureUnits { 12namespace TextureUnits {
17 13
18struct TextureUnit { 14struct TextureUnit {
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index 664f3ca20..e409228cc 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -74,7 +74,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
74 } 74 }
75 } 75 }
76 76
77 if (invalidate | !persistent) { 77 if (invalidate || !persistent) {
78 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | 78 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
79 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | 79 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
80 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); 80 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index e0a14d48f..681919ae3 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -161,4 +161,26 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe
161 } 161 }
162} 162}
163 163
164static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
165 std::string extra_info = "") {
166 if (!GLAD_GL_KHR_debug) {
167 return; // We don't need to throw an error as this is just for debugging
168 }
169 const std::string nice_addr = fmt::format("0x{:016x}", addr);
170 std::string object_label;
171
172 switch (identifier) {
173 case GL_TEXTURE:
174 object_label = extra_info + "@" + nice_addr;
175 break;
176 case GL_PROGRAM:
177 object_label = "ShaderProgram@" + nice_addr;
178 break;
179 default:
180 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
181 break;
182 }
183 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
184}
185
164} // namespace VideoCore 186} // namespace VideoCore
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f2a7e23f0..a3b1fd357 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
16#include "core/hle/kernel/timer.h" 16#include "core/hle/kernel/timer.h"
17#include "core/hle/kernel/wait_object.h" 17#include "core/hle/kernel/wait_object.h"
18#include "core/memory.h"
18 19
19WaitTreeItem::WaitTreeItem() = default; 20WaitTreeItem::WaitTreeItem() = default;
20WaitTreeItem::~WaitTreeItem() = default; 21WaitTreeItem::~WaitTreeItem() = default;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 3b3b551bb..991ae10cd 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -89,15 +89,7 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e
89} 89}
90 90
91void GameList::SearchField::setFilterResult(int visible, int total) { 91void GameList::SearchField::setFilterResult(int visible, int total) {
92 QString result_of_text = tr("of"); 92 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
93 QString result_text;
94 if (total == 1) {
95 result_text = tr("result");
96 } else {
97 result_text = tr("results");
98 }
99 label_filter_result->setText(
100 QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text));
101} 93}
102 94
103void GameList::SearchField::clear() { 95void GameList::SearchField::clear() {
@@ -326,9 +318,14 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
326 int row = item_model->itemFromIndex(item)->row(); 318 int row = item_model->itemFromIndex(item)->row();
327 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); 319 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
328 u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); 320 u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong();
321 std::string path = child_file->data(GameListItemPath::FullPathRole).toString().toStdString();
329 322
330 QMenu context_menu; 323 QMenu context_menu;
331 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 324 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
325 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
326 context_menu.addSeparator();
327 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
328 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
332 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 329 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
333 330
334 open_save_location->setEnabled(program_id != 0); 331 open_save_location->setEnabled(program_id != 0);
@@ -337,6 +334,10 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
337 334
338 connect(open_save_location, &QAction::triggered, 335 connect(open_save_location, &QAction::triggered,
339 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); 336 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
337 connect(open_lfs_location, &QAction::triggered,
338 [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); });
339 connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); });
340 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
340 connect(navigate_to_gamedb_entry, &QAction::triggered, 341 connect(navigate_to_gamedb_entry, &QAction::triggered,
341 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); 342 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
342 343
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 2713e7b54..3bf51870e 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -28,7 +28,10 @@ namespace FileSys {
28class VfsFilesystem; 28class VfsFilesystem;
29} 29}
30 30
31enum class GameListOpenTarget { SaveData }; 31enum class GameListOpenTarget {
32 SaveData,
33 ModData,
34};
32 35
33class GameList : public QWidget { 36class GameList : public QWidget {
34 Q_OBJECT 37 Q_OBJECT
@@ -89,6 +92,8 @@ signals:
89 void GameChosen(QString game_path); 92 void GameChosen(QString game_path);
90 void ShouldCancelWorker(); 93 void ShouldCancelWorker();
91 void OpenFolderRequested(u64 program_id, GameListOpenTarget target); 94 void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
95 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
96 void CopyTIDRequested(u64 program_id);
92 void NavigateToGamedbEntryRequested(u64 program_id, 97 void NavigateToGamedbEntryRequested(u64 program_id,
93 const CompatibilityList& compatibility_list); 98 const CompatibilityList& compatibility_list);
94 99
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index b6272d536..cee109730 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -68,7 +68,7 @@ public:
68 if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { 68 if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
69 picture = GetDefaultIcon(size); 69 picture = GetDefaultIcon(size);
70 } 70 }
71 picture = picture.scaled(size, size); 71 picture = picture.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
72 72
73 setData(picture, Qt::DecorationRole); 73 setData(picture, Qt::DecorationRole);
74 } 74 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 45bb1d1d1..d74489935 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -7,6 +7,22 @@
7#include <memory> 7#include <memory>
8#include <thread> 8#include <thread>
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h"
13
14// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
15// defines.
16static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
17 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
18 return vfs->CreateDirectory(path, mode);
19}
20
21static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
22 const std::string& path) {
23 return dir->CreateFile(path);
24}
25
10#include <fmt/ostream.h> 26#include <fmt/ostream.h>
11#include <glad/glad.h> 27#include <glad/glad.h>
12 28
@@ -30,16 +46,18 @@
30#include "common/telemetry.h" 46#include "common/telemetry.h"
31#include "core/core.h" 47#include "core/core.h"
32#include "core/crypto/key_manager.h" 48#include "core/crypto/key_manager.h"
49#include "core/file_sys/bis_factory.h"
33#include "core/file_sys/card_image.h" 50#include "core/file_sys/card_image.h"
34#include "core/file_sys/content_archive.h" 51#include "core/file_sys/content_archive.h"
35#include "core/file_sys/control_metadata.h" 52#include "core/file_sys/control_metadata.h"
36#include "core/file_sys/patch_manager.h" 53#include "core/file_sys/patch_manager.h"
37#include "core/file_sys/registered_cache.h" 54#include "core/file_sys/registered_cache.h"
55#include "core/file_sys/romfs.h"
38#include "core/file_sys/savedata_factory.h" 56#include "core/file_sys/savedata_factory.h"
39#include "core/file_sys/submission_package.h" 57#include "core/file_sys/submission_package.h"
40#include "core/file_sys/vfs_real.h"
41#include "core/hle/kernel/process.h" 58#include "core/hle/kernel/process.h"
42#include "core/hle/service/filesystem/filesystem.h" 59#include "core/hle/service/filesystem/filesystem.h"
60#include "core/hle/service/filesystem/fsp_ldr.h"
43#include "core/loader/loader.h" 61#include "core/loader/loader.h"
44#include "core/perf_stats.h" 62#include "core/perf_stats.h"
45#include "core/settings.h" 63#include "core/settings.h"
@@ -362,6 +380,8 @@ void GMainWindow::RestoreUIState() {
362void GMainWindow::ConnectWidgetEvents() { 380void GMainWindow::ConnectWidgetEvents() {
363 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); 381 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
364 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 382 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
383 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
384 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
365 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 385 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
366 &GMainWindow::OnGameListNavigateToGamedbEntry); 386 &GMainWindow::OnGameListNavigateToGamedbEntry);
367 387
@@ -713,6 +733,12 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
713 program_id, user_id, 0); 733 program_id, user_id, 0);
714 break; 734 break;
715 } 735 }
736 case GameListOpenTarget::ModData: {
737 open_target = "Mod Data";
738 const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir);
739 path = fmt::format("{}{:016X}", load_dir, program_id);
740 break;
741 }
716 default: 742 default:
717 UNIMPLEMENTED(); 743 UNIMPLEMENTED();
718 } 744 }
@@ -730,6 +756,120 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
730 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); 756 QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
731} 757}
732 758
759static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
760 std::size_t out = 0;
761
762 for (const auto& subdir : dir->GetSubdirectories()) {
763 out += 1 + CalculateRomFSEntrySize(subdir, full);
764 }
765
766 return out + (full ? dir->GetFiles().size() : 0);
767}
768
769static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src,
770 const FileSys::VirtualDir& dest, std::size_t block_size, bool full) {
771 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
772 return false;
773 if (dialog.wasCanceled())
774 return false;
775
776 if (full) {
777 for (const auto& file : src->GetFiles()) {
778 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
779 if (!FileSys::VfsRawCopy(file, out, block_size))
780 return false;
781 dialog.setValue(dialog.value() + 1);
782 if (dialog.wasCanceled())
783 return false;
784 }
785 }
786
787 for (const auto& dir : src->GetSubdirectories()) {
788 const auto out = dest->CreateSubdirectory(dir->GetName());
789 if (!RomFSRawCopy(dialog, dir, out, block_size, full))
790 return false;
791 dialog.setValue(dialog.value() + 1);
792 if (dialog.wasCanceled())
793 return false;
794 }
795
796 return true;
797}
798
799void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
800 const auto path = fmt::format("{}{:016X}/romfs",
801 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
802
803 const auto failed = [this, &path] {
804 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
805 tr("There was an error copying the RomFS files or the user "
806 "cancelled the operation."));
807 vfs->DeleteDirectory(path);
808 };
809
810 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
811 if (loader == nullptr) {
812 failed();
813 return;
814 }
815
816 FileSys::VirtualFile file;
817 if (loader->ReadRomFS(file) != Loader::ResultStatus::Success) {
818 failed();
819 return;
820 }
821
822 const auto romfs =
823 loader->IsRomFSUpdatable()
824 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset())
825 : file;
826
827 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
828 if (extracted == nullptr) {
829 failed();
830 return;
831 }
832
833 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite);
834
835 if (out == nullptr) {
836 failed();
837 return;
838 }
839
840 bool ok;
841 const auto res = QInputDialog::getItem(
842 this, tr("Select RomFS Dump Mode"),
843 tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
844 "files into the new directory while <br>skeleton will only create the directory "
845 "structure."),
846 {"Full", "Skeleton"}, 0, false, &ok);
847 if (!ok)
848 failed();
849
850 const auto full = res == "Full";
851 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
852
853 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
854 progress.setWindowModality(Qt::WindowModal);
855 progress.setMinimumDuration(100);
856
857 if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) {
858 progress.close();
859 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
860 tr("The operation completed successfully."));
861 QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
862 } else {
863 progress.close();
864 failed();
865 }
866}
867
868void GMainWindow::OnGameListCopyTID(u64 program_id) {
869 QClipboard* clipboard = QGuiApplication::clipboard();
870 clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id)));
871}
872
733void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, 873void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
734 const CompatibilityList& compatibility_list) { 874 const CompatibilityList& compatibility_list) {
735 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 875 const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -790,7 +930,8 @@ void GMainWindow::OnMenuInstallToNAND() {
790 return; 930 return;
791 } 931 }
792 932
793 const auto qt_raw_copy = [this](FileSys::VirtualFile src, FileSys::VirtualFile dest) { 933 const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
934 const FileSys::VirtualFile& dest, std::size_t block_size) {
794 if (src == nullptr || dest == nullptr) 935 if (src == nullptr || dest == nullptr)
795 return false; 936 return false;
796 if (!dest->Resize(src->GetSize())) 937 if (!dest->Resize(src->GetSize()))
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 552e3e61c..8ee9242b1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -138,6 +138,8 @@ private slots:
138 /// Called whenever a user selects a game in the game list widget. 138 /// Called whenever a user selects a game in the game list widget.
139 void OnGameListLoadFile(QString game_path); 139 void OnGameListLoadFile(QString game_path);
140 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); 140 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
141 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
142 void OnGameListCopyTID(u64 program_id);
141 void OnGameListNavigateToGamedbEntry(u64 program_id, 143 void OnGameListNavigateToGamedbEntry(u64 program_id,
142 const CompatibilityList& compatibility_list); 144 const CompatibilityList& compatibility_list);
143 void OnMenuLoadFile(); 145 void OnMenuLoadFile();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 7ec1f5110..a478b0a56 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -120,7 +120,7 @@ void Config::ReadValues() {
120 sdl2_config->Get("Data Storage", "nand_directory", 120 sdl2_config->Get("Data Storage", "nand_directory",
121 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); 121 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
122 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 122 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
123 sdl2_config->Get("Data Storage", "nand_directory", 123 sdl2_config->Get("Data Storage", "sdmc_directory",
124 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 124 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
125 125
126 // System 126 // System
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b1c364fbb..b2559b717 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -20,8 +20,10 @@
20#include "common/string_util.h" 20#include "common/string_util.h"
21#include "common/telemetry.h" 21#include "common/telemetry.h"
22#include "core/core.h" 22#include "core/core.h"
23#include "core/crypto/key_manager.h"
23#include "core/file_sys/vfs_real.h" 24#include "core/file_sys/vfs_real.h"
24#include "core/gdbstub/gdbstub.h" 25#include "core/gdbstub/gdbstub.h"
26#include "core/hle/service/filesystem/filesystem.h"
25#include "core/loader/loader.h" 27#include "core/loader/loader.h"
26#include "core/settings.h" 28#include "core/settings.h"
27#include "core/telemetry_session.h" 29#include "core/telemetry_session.h"
@@ -29,7 +31,6 @@
29#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 31#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
30 32
31#include <getopt.h> 33#include <getopt.h>
32#include "core/crypto/key_manager.h"
33#ifndef _MSC_VER 34#ifndef _MSC_VER
34#include <unistd.h> 35#include <unistd.h>
35#endif 36#endif
@@ -169,6 +170,7 @@ int main(int argc, char** argv) {
169 170
170 Core::System& system{Core::System::GetInstance()}; 171 Core::System& system{Core::System::GetInstance()};
171 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 172 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
173 Service::FileSystem::CreateFactories(system.GetFilesystem());
172 174
173 SCOPE_EXIT({ system.Shutdown(); }); 175 SCOPE_EXIT({ system.Shutdown(); });
174 176