summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis/linux-mingw/docker.sh1
-rw-r--r--appveyor.yml19
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/core/file_sys/bis_factory.cpp9
-rw-r--r--src/core/file_sys/bis_factory.h4
-rw-r--r--src/core/file_sys/card_image.cpp2
-rw-r--r--src/core/file_sys/card_image.h3
-rw-r--r--src/core/file_sys/content_archive.cpp5
-rw-r--r--src/core/file_sys/content_archive.h3
-rw-r--r--src/core/file_sys/errors.h25
-rw-r--r--src/core/file_sys/patch_manager.cpp13
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/file_sys/submission_package.cpp2
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/hle/kernel/errors.h74
-rw-r--r--src/core/hle/kernel/svc.cpp66
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp48
-rw-r--r--src/core/hle/kernel/thread.h2
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/am/am.cpp27
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp38
-rw-r--r--src/core/hle/service/btm/btm.cpp108
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp44
-rw-r--r--src/core/hle/service/filesystem/filesystem.h3
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp58
-rw-r--r--src/core/hle/service/hid/hid.cpp16
-rw-r--r--src/core/hle/service/spl/module.cpp3
-rw-r--r--src/core/hle/service/time/interface.cpp3
-rw-r--r--src/core/hle/service/time/time.cpp15
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/vi/vi.cpp22
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_state.h2
-rw-r--r--src/video_core/textures/decoders.cpp32
-rw-r--r--src/yuzu/configuration/config.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui21
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp2
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui223
-rw-r--r--src/yuzu/configuration/configure_general.cpp3
-rw-r--r--src/yuzu/game_list.cpp33
-rw-r--r--src/yuzu/game_list_worker.cpp24
-rw-r--r--src/yuzu/main.cpp10
-rw-r--r--src/yuzu/ui_settings.h1
-rw-r--r--src/yuzu_cmd/config.cpp3
-rw-r--r--src/yuzu_cmd/default_ini.h2
56 files changed, 712 insertions, 349 deletions
diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh
index d15c3f6e8..6cf43a006 100755
--- a/.travis/linux-mingw/docker.sh
+++ b/.travis/linux-mingw/docker.sh
@@ -57,3 +57,4 @@ done
57 57
58pip3 install pefile 58pip3 install pefile
59python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/" 59python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
60python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"
diff --git a/appveyor.yml b/appveyor.yml
index 6d0e6522a..d6a69fbc2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -125,26 +125,27 @@ after_build:
125 Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST 125 Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
126 Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST 126 Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
127 127
128 # copy the qt windows plugin dll to platforms
129 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
130
131 # copy the qt windows vista style dll to platforms
132 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
133
134 # copy the qt jpeg imageformat dll to platforms
135 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
136
128 # copy all the dll dependencies to the release folder 137 # copy all the dll dependencies to the release folder
129 . "./.appveyor/UtilityFunctions.ps1" 138 . "./.appveyor/UtilityFunctions.ps1"
130 $DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH" 139 $DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
131 $MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe" 140 $MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
132 $MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe" 141 $MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
142 $MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
133 Write-Host "Detected the following dependencies:" 143 Write-Host "Detected the following dependencies:"
134 Write-Host $MingwDLLs 144 Write-Host $MingwDLLs
135 foreach ($file in $MingwDLLs) { 145 foreach ($file in $MingwDLLs) {
136 Copy-Item -path "$file" -force -destination "$RELEASE_DIST" 146 Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
137 } 147 }
138 148
139 # copy the qt windows plugin dll to platforms
140 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
141
142 # copy the qt windows vista style dll to platforms
143 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
144
145 # copy the qt jpeg imageformat dll to platforms
146 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
147
148 7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* 149 7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
149 7z a $MINGW_SEVENZIP $RELEASE_DIST 150 7z a $MINGW_SEVENZIP $RELEASE_DIST
150 } 151 }
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index bf803da8d..21e07925d 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -117,21 +117,21 @@ private:
117 // We don't delete it because we want BitField to be trivially copyable. 117 // We don't delete it because we want BitField to be trivially copyable.
118 constexpr BitField& operator=(const BitField&) = default; 118 constexpr BitField& operator=(const BitField&) = default;
119 119
120 // StorageType is T for non-enum types and the underlying type of T if 120 // UnderlyingType is T for non-enum types and the underlying type of T if
121 // T is an enumeration. Note that T is wrapped within an enable_if in the 121 // T is an enumeration. Note that T is wrapped within an enable_if in the
122 // former case to workaround compile errors which arise when using 122 // former case to workaround compile errors which arise when using
123 // std::underlying_type<T>::type directly. 123 // std::underlying_type<T>::type directly.
124 using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, 124 using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
125 std::enable_if<true, T>>::type; 125 std::enable_if<true, T>>::type;
126 126
127 // Unsigned version of StorageType 127 // We store the value as the unsigned type to avoid undefined behaviour on value shifting
128 using StorageTypeU = std::make_unsigned_t<StorageType>; 128 using StorageType = std::make_unsigned_t<UnderlyingType>;
129 129
130public: 130public:
131 /// Constants to allow limited introspection of fields if needed 131 /// Constants to allow limited introspection of fields if needed
132 static constexpr std::size_t position = Position; 132 static constexpr std::size_t position = Position;
133 static constexpr std::size_t bits = Bits; 133 static constexpr std::size_t bits = Bits;
134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; 134 static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
135 135
136 /** 136 /**
137 * Formats a value by masking and shifting it according to the field parameters. A value 137 * Formats a value by masking and shifting it according to the field parameters. A value
@@ -148,11 +148,12 @@ public:
148 * union in a constexpr context. 148 * union in a constexpr context.
149 */ 149 */
150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
151 if (std::numeric_limits<T>::is_signed) { 151 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
152 std::size_t shift = 8 * sizeof(T) - bits; 152 std::size_t shift = 8 * sizeof(T) - bits;
153 return (T)((storage << (shift - position)) >> shift); 153 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
154 shift);
154 } else { 155 } else {
155 return (T)((storage & mask) >> position); 156 return static_cast<T>((storage & mask) >> position);
156 } 157 }
157 } 158 }
158 159
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 76a2b7e86..e29f70b3a 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -8,8 +8,9 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 dump_root(std::move(dump_root_)),
13 sysnand_cache(std::make_unique<RegisteredCache>( 14 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 15 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_unique<RegisteredCache>( 16 usrnand_cache(std::make_unique<RegisteredCache>(
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
32 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); 33 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
33} 34}
34 35
36VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
37 if (title_id == 0)
38 return nullptr;
39 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
40}
41
35} // namespace FileSys 42} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 364d309bd..453c11ad2 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,17 +17,19 @@ class RegisteredCache;
17/// registered caches. 17/// registered caches.
18class BISFactory { 18class BISFactory {
19public: 19public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 RegisteredCache* GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 RegisteredCache* GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 VirtualDir GetModificationDumpRoot(u64 title_id) const;
27 28
28private: 29private:
29 VirtualDir nand_root; 30 VirtualDir nand_root;
30 VirtualDir load_root; 31 VirtualDir load_root;
32 VirtualDir dump_root;
31 33
32 std::unique_ptr<RegisteredCache> sysnand_cache; 34 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::unique_ptr<RegisteredCache> usrnand_cache; 35 std::unique_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 1ece55731..2c145bd09 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) { 176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
177 if (file->GetExtension() != "nca") 177 if (file->GetExtension() != "nca")
178 continue; 178 continue;
179 auto nca = std::make_shared<NCA>(file); 179 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
180 // TODO(DarkLordZach): Add proper Rev1+ Support 180 // TODO(DarkLordZach): Add proper Rev1+ Support
181 if (nca->IsUpdate()) 181 if (nca->IsUpdate())
182 continue; 182 continue;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 8f62571cf..25f5914b6 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/crypto/key_manager.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13 14
14namespace Loader { 15namespace Loader {
@@ -107,5 +108,7 @@ private:
107 std::shared_ptr<NSP> secure_partition; 108 std::shared_ptr<NSP> secure_partition;
108 std::shared_ptr<NCA> program; 109 std::shared_ptr<NCA> program;
109 std::vector<std::shared_ptr<NCA>> ncas; 110 std::vector<std::shared_ptr<NCA>> ncas;
111
112 Core::Crypto::KeyManager keys;
110}; 113};
111} // namespace FileSys 114} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index b46fe893c..19b6f8600 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
102} 102}
103 103
104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) 104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
105 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { 105 Core::Crypto::KeyManager keys_)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
106 if (file == nullptr) { 107 if (file == nullptr) {
107 status = Loader::ResultStatus::ErrorNullFile; 108 status = Loader::ResultStatus::ErrorNullFile;
108 return; 109 return;
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 4bba55607..99294cbb4 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
79class NCA : public ReadOnlyVfsDirectory { 79class NCA : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
82 u64 bktr_base_ivfc_offset = 0); 82 u64 bktr_base_ivfc_offset = 0,
83 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
83 ~NCA() override; 84 ~NCA() override;
84 85
85 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index fea0593c7..e4a4ee4ab 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -8,25 +8,10 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11namespace ErrCodes { 11constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
12enum { 12constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
13 NotFound = 1, 13constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
14 TitleNotFound = 1002, 14constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
15 SdCardNotFound = 2001, 15constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
16 RomFSNotFound = 2520,
17};
18}
19
20constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
21
22// TODO(bunnei): Replace these with correct errors for Switch OS
23constexpr ResultCode ERROR_INVALID_PATH(-1);
24constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
25constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
26constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
27constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
28constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
29constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
30constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
31 16
32} // namespace FileSys 17} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0c1156989..8d062eb3e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -19,6 +19,7 @@
19#include "core/file_sys/vfs_vector.h" 19#include "core/file_sys/vfs_vector.h"
20#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22#include "core/settings.h"
22 23
23namespace FileSys { 24namespace FileSys {
24 25
@@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
119 const auto build_id_raw = Common::HexArrayToString(header.build_id); 120 const auto build_id_raw = Common::HexArrayToString(header.build_id);
120 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 121 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
121 122
123 if (Settings::values.dump_nso) {
124 LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
125 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
126 if (dump_dir != nullptr) {
127 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
128 const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
129
130 file->Resize(nso.size());
131 file->WriteBytes(nso);
132 }
133 }
134
122 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 135 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
123 136
124 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 137 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 96302a241..a3f8f2f73 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
106 106
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 108 std::string_view path) const {
109 if (dir->GetFileRelative(path) != nullptr) 109 const auto file = dir->GetFileRelative(path);
110 return dir->GetFileRelative(path); 110 if (file != nullptr)
111 if (dir->GetDirectoryRelative(path) != nullptr) { 111 return file;
112
113 const auto nca_dir = dir->GetDirectoryRelative(path);
114 if (nca_dir != nullptr) {
112 const auto nca_dir = dir->GetDirectoryRelative(path); 115 const auto nca_dir = dir->GetDirectoryRelative(path);
113 VirtualFile file = nullptr; 116 VirtualFile file = nullptr;
114 117
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
225 228
226 if (file == nullptr) 229 if (file == nullptr)
227 continue; 230 continue;
228 const auto nca = std::make_shared<NCA>(parser(file, id)); 231 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
229 if (nca->GetStatus() != Loader::ResultStatus::Success || 232 if (nca->GetStatus() != Loader::ResultStatus::Success ||
230 nca->GetType() != NCAContentType::Meta) { 233 nca->GetType() != NCAContentType::Meta) {
231 continue; 234 continue;
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
315 const auto raw = GetEntryRaw(title_id, type); 318 const auto raw = GetEntryRaw(title_id, type);
316 if (raw == nullptr) 319 if (raw == nullptr)
317 return nullptr; 320 return nullptr;
318 return std::make_unique<NCA>(raw); 321 return std::make_unique<NCA>(raw, nullptr, 0, keys);
319} 322}
320 323
321std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 324std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 6cfb16017..6b89db8de 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13#include <boost/container/flat_map.hpp> 13#include <boost/container/flat_map.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/crypto/key_manager.h"
15#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
16 17
17namespace FileSys { 18namespace FileSys {
@@ -133,6 +134,8 @@ private:
133 134
134 VirtualDir dir; 135 VirtualDir dir;
135 RegisteredCacheParsingFunction parser; 136 RegisteredCacheParsingFunction parser;
137 Core::Crypto::KeyManager keys;
138
136 // maps tid -> NcaID of meta 139 // maps tid -> NcaID of meta
137 boost::container::flat_map<u64, NcaID> meta_id; 140 boost::container::flat_map<u64, NcaID> meta_id;
138 // maps tid -> meta 141 // maps tid -> meta
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 2aaba4179..e1a4210db 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
252 continue; 252 continue;
253 } 253 }
254 254
255 auto next_nca = std::make_shared<NCA>(next_file); 255 auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
256 if (next_nca->GetType() == NCAContentType::Program) 256 if (next_nca->GetType() == NCAContentType::Program)
257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
258 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 258 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 338080b7e..9a28ed5bb 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -70,6 +70,8 @@ private:
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files; 71 std::vector<VirtualFile> ticket_files;
72 72
73 Core::Crypto::KeyManager keys;
74
73 VirtualFile romfs; 75 VirtualFile romfs;
74 VirtualDir exefs; 76 VirtualDir exefs;
75}; 77};
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index ee698c8a7..8b58d701d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,58 +8,28 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11namespace ErrCodes { 11// Confirmed Switch kernel error codes
12enum {
13 // Confirmed Switch OS error codes
14 MaxConnectionsReached = 7,
15 InvalidSize = 101,
16 InvalidAddress = 102,
17 HandleTableFull = 105,
18 InvalidMemoryState = 106,
19 InvalidMemoryPermissions = 108,
20 InvalidMemoryRange = 110,
21 InvalidThreadPriority = 112,
22 InvalidProcessorId = 113,
23 InvalidHandle = 114,
24 InvalidPointer = 115,
25 InvalidCombination = 116,
26 Timeout = 117,
27 SynchronizationCanceled = 118,
28 TooLarge = 119,
29 InvalidEnumValue = 120,
30 NoSuchEntry = 121,
31 AlreadyRegistered = 122,
32 SessionClosed = 123,
33 InvalidState = 125,
34 ResourceLimitExceeded = 132,
35};
36}
37 12
38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
39// double check that the code matches before re-using the constant. 14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
40 15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); 17constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 18constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 19constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
45 ErrCodes::MaxConnectionsReached); 20constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 21constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 22constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
48 ErrCodes::InvalidCombination); 23constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 24constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 25constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 26constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
52 ErrCodes::InvalidMemoryPermissions); 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
53constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
54constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
55constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
56constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
57constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
60 ErrCodes::InvalidThreadPriority);
61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
64 34
65} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index b0b6508d9..75dbfc31d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -123,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
123 return RESULT_SUCCESS; 123 return RESULT_SUCCESS;
124} 124}
125 125
126static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
127 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
128
129 if (!Common::Is4KBAligned(addr)) {
130 return ERR_INVALID_ADDRESS;
131 }
132
133 if (size == 0 || !Common::Is4KBAligned(size)) {
134 return ERR_INVALID_SIZE;
135 }
136
137 if (!IsValidAddressRange(addr, size)) {
138 return ERR_INVALID_ADDRESS_STATE;
139 }
140
141 const auto permission = static_cast<MemoryPermission>(prot);
142 if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
143 permission != MemoryPermission::ReadWrite) {
144 return ERR_INVALID_MEMORY_PERMISSIONS;
145 }
146
147 auto* const current_process = Core::CurrentProcess();
148 auto& vm_manager = current_process->VMManager();
149
150 if (!IsInsideAddressSpace(vm_manager, addr, size)) {
151 return ERR_INVALID_ADDRESS_STATE;
152 }
153
154 const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
155 if (iter == vm_manager.vma_map.end()) {
156 return ERR_INVALID_ADDRESS_STATE;
157 }
158
159 LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
160 // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
161 // make sense to allow changing permissions on kernel memory itself, etc).
162
163 const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
164
165 return vm_manager.ReprotectRange(addr, size, converted_permissions);
166}
167
126static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { 168static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
127 LOG_WARNING(Kernel_SVC, 169 LOG_WARNING(Kernel_SVC,
128 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, 170 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
@@ -172,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
172 // Read 1 char beyond the max allowed port name to detect names that are too long. 214 // Read 1 char beyond the max allowed port name to detect names that are too long.
173 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); 215 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
174 if (port_name.size() > PortNameMaxLength) { 216 if (port_name.size() > PortNameMaxLength) {
175 return ERR_PORT_NAME_TOO_LONG; 217 return ERR_OUT_OF_RANGE;
176 } 218 }
177 219
178 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 220 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -268,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
268 310
269 static constexpr u64 MaxHandles = 0x40; 311 static constexpr u64 MaxHandles = 0x40;
270 312
271 if (handle_count > MaxHandles) 313 if (handle_count > MaxHandles) {
272 return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); 314 return ERR_OUT_OF_RANGE;
315 }
273 316
274 auto* const thread = GetCurrentThread(); 317 auto* const thread = GetCurrentThread();
275 318
@@ -334,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
334 } 377 }
335 378
336 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 379 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
337 thread->SetWaitSynchronizationResult( 380 thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
338 ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
339 thread->ResumeFromWait(); 381 thread->ResumeFromWait();
340 return RESULT_SUCCESS; 382 return RESULT_SUCCESS;
341} 383}
@@ -564,7 +606,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
564 } 606 }
565 607
566 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) { 608 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
567 return ERR_INVALID_COMBINATION_KERNEL; 609 return ERR_INVALID_COMBINATION;
568 } 610 }
569 611
570 *result = current_process->GetRandomEntropy(info_sub_id); 612 *result = current_process->GetRandomEntropy(info_sub_id);
@@ -601,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
601 case GetInfoType::ThreadTickCount: { 643 case GetInfoType::ThreadTickCount: {
602 constexpr u64 num_cpus = 4; 644 constexpr u64 num_cpus = 4;
603 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
604 return ERR_INVALID_COMBINATION_KERNEL; 646 return ERR_INVALID_COMBINATION;
605 } 647 }
606 648
607 const auto thread = 649 const auto thread =
@@ -1139,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
1139 1181
1140/// Reset an event 1182/// Reset an event
1141static ResultCode ResetSignal(Handle handle) { 1183static ResultCode ResetSignal(Handle handle) {
1142 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1184 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1143 1185
1144 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1186 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1145 auto event = handle_table.Get<Event>(handle); 1187 auto event = handle_table.Get<Event>(handle);
@@ -1194,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1194 } 1236 }
1195 1237
1196 if (mask == 0) { 1238 if (mask == 0) {
1197 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1239 return ERR_INVALID_COMBINATION;
1198 } 1240 }
1199 1241
1200 /// This value is used to only change the affinity mask without changing the current ideal core. 1242 /// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1203,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1203 if (core == OnlyChangeMask) { 1245 if (core == OnlyChangeMask) {
1204 core = thread->GetIdealCore(); 1246 core = thread->GetIdealCore();
1205 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { 1247 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
1206 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 1248 return ERR_INVALID_PROCESSOR_ID;
1207 } 1249 }
1208 1250
1209 // Error out if the input core isn't enabled in the input mask. 1251 // Error out if the input core isn't enabled in the input mask.
1210 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1252 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
1211 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1253 return ERR_INVALID_COMBINATION;
1212 } 1254 }
1213 1255
1214 thread->ChangeCore(core, mask); 1256 thread->ChangeCore(core, mask);
@@ -1297,7 +1339,7 @@ struct FunctionDef {
1297static const FunctionDef SVC_Table[] = { 1339static const FunctionDef SVC_Table[] = {
1298 {0x00, nullptr, "Unknown"}, 1340 {0x00, nullptr, "Unknown"},
1299 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, 1341 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
1300 {0x02, nullptr, "SetMemoryPermission"}, 1342 {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
1301 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, 1343 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
1302 {0x04, SvcWrap<MapMemory>, "MapMemory"}, 1344 {0x04, SvcWrap<MapMemory>, "MapMemory"},
1303 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, 1345 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b09753c80..233a99fb0 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -121,6 +121,11 @@ void SvcWrap() {
121 FuncReturn(func(Param(0), Param(1), Param(2)).raw); 121 FuncReturn(func(Param(0), Param(1), Param(2)).raw);
122} 122}
123 123
124template <ResultCode func(u64, u64, u32)>
125void SvcWrap() {
126 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
127}
128
124template <ResultCode func(u32, u64, u64, u32)> 129template <ResultCode func(u32, u64, u64, u32)>
125void SvcWrap() { 130void SvcWrap() {
126 FuncReturn( 131 FuncReturn(
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index dd5cd9ced..4ffb76818 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
142 142
143 status = ThreadStatus::Ready; 143 status = ThreadStatus::Ready;
144 144
145 std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); 145 ChangeScheduler();
146 if (!new_processor_id) {
147 new_processor_id = processor_id;
148 }
149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core;
152 }
153
154 ASSERT(*new_processor_id < 4);
155
156 // Add thread to new core's scheduler
157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158
159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler
161 scheduler->RemoveThread(this);
162 next_scheduler->AddThread(this, current_priority);
163 }
164
165 processor_id = *new_processor_id;
166
167 // If the thread was ready, unschedule from the previous core and schedule on the new core
168 scheduler->UnscheduleThread(this, current_priority);
169 next_scheduler->ScheduleThread(this, current_priority);
170
171 // Change thread's scheduler
172 scheduler = next_scheduler;
173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 146}
176 147
177/** 148/**
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
364void Thread::ChangeCore(u32 core, u64 mask) { 335void Thread::ChangeCore(u32 core, u64 mask) {
365 ideal_core = core; 336 ideal_core = core;
366 affinity_mask = mask; 337 affinity_mask = mask;
338 ChangeScheduler();
339}
367 340
341void Thread::ChangeScheduler() {
368 if (status != ThreadStatus::Ready) { 342 if (status != ThreadStatus::Ready) {
369 return; 343 return;
370 } 344 }
371 345
346 auto& system = Core::System::GetInstance();
372 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; 347 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
373 348
374 if (!new_processor_id) { 349 if (!new_processor_id) {
375 new_processor_id = processor_id; 350 new_processor_id = processor_id;
376 } 351 }
377 if (ideal_core != -1 && 352 if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 353 new_processor_id = ideal_core;
380 } 354 }
381 355
382 ASSERT(*new_processor_id < 4); 356 ASSERT(*new_processor_id < 4);
383 357
384 // Add thread to new core's scheduler 358 // Add thread to new core's scheduler
385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); 359 auto& next_scheduler = system.Scheduler(*new_processor_id);
386 360
387 if (*new_processor_id != processor_id) { 361 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 362 // Remove thread from previous core's scheduler
389 scheduler->RemoveThread(this); 363 scheduler->RemoveThread(this);
390 next_scheduler->AddThread(this, current_priority); 364 next_scheduler.AddThread(this, current_priority);
391 } 365 }
392 366
393 processor_id = *new_processor_id; 367 processor_id = *new_processor_id;
394 368
395 // If the thread was ready, unschedule from the previous core and schedule on the new core 369 // If the thread was ready, unschedule from the previous core and schedule on the new core
396 scheduler->UnscheduleThread(this, current_priority); 370 scheduler->UnscheduleThread(this, current_priority);
397 next_scheduler->ScheduleThread(this, current_priority); 371 next_scheduler.ScheduleThread(this, current_priority);
398 372
399 // Change thread's scheduler 373 // Change thread's scheduler
400 scheduler = next_scheduler; 374 scheduler = &next_scheduler;
401 375
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 376 system.CpuCore(processor_id).PrepareReschedule();
403} 377}
404 378
405bool Thread::AllWaitObjectsReady() { 379bool Thread::AllWaitObjectsReady() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 4a6e11239..d384d50db 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -374,6 +374,8 @@ private:
374 explicit Thread(KernelCore& kernel); 374 explicit Thread(KernelCore& kernel);
375 ~Thread() override; 375 ~Thread() override;
376 376
377 void ChangeScheduler();
378
377 Core::ARM_Interface::ThreadContext context{}; 379 Core::ARM_Interface::ThreadContext context{};
378 380
379 u32 thread_id = 0; 381 u32 thread_id = 0;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index c6b18cfba..bfb77cc31 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -19,8 +19,6 @@
19enum class ErrorDescription : u32 { 19enum class ErrorDescription : u32 {
20 Success = 0, 20 Success = 0,
21 RemoteProcessDead = 301, 21 RemoteProcessDead = 301,
22 InvalidOffset = 6061,
23 InvalidLength = 6062,
24}; 22};
25 23
26/** 24/**
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 0477ce66e..3758ecae1 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -203,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
203ISelfController::~ISelfController() = default; 203ISelfController::~ISelfController() = default;
204 204
205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
206 // Takes 3 input u8s with each field located immediately after the previous u8, these are 206 // Takes 3 input u8s with each field located immediately after the previous
207 // bool flags. No output. 207 // u8, these are bool flags. No output.
208 208
209 IPC::RequestParser rp{ctx}; 209 IPC::RequestParser rp{ctx};
210 210
@@ -258,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
258} 258}
259 259
260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
261 // Takes 3 input u8s with each field located immediately after the previous u8, these are 261 // Takes 3 input u8s with each field located immediately after the previous
262 // bool flags. No output. 262 // u8, these are bool flags. No output.
263 IPC::RequestParser rp{ctx}; 263 IPC::RequestParser rp{ctx};
264 264
265 bool enabled = rp.Pop<bool>(); 265 bool enabled = rp.Pop<bool>();
@@ -302,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
302} 302}
303 303
304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
305 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 305 // TODO(Subv): Find out how AM determines the display to use, for now just
306 // in the Default display. 306 // create the layer in the Default display.
307 u64 display_id = nvflinger->OpenDisplay("Default"); 307 u64 display_id = nvflinger->OpenDisplay("Default");
308 u64 layer_id = nvflinger->CreateLayer(display_id); 308 u64 layer_id = nvflinger->CreateLayer(display_id);
309 309
@@ -733,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
733 {70, nullptr, "RequestToShutdown"}, 733 {70, nullptr, "RequestToShutdown"},
734 {71, nullptr, "RequestToReboot"}, 734 {71, nullptr, "RequestToReboot"},
735 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 735 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
736 {90, nullptr, "EnableApplicationCrashReport"}, 736 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 737 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
738 {101, nullptr, "SetApplicationCopyrightImage"}, 738 {101, nullptr, "SetApplicationCopyrightImage"},
739 {102, nullptr, "SetApplicationCopyrightVisibility"}, 739 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -752,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
752 752
753IApplicationFunctions::~IApplicationFunctions() = default; 753IApplicationFunctions::~IApplicationFunctions() = default;
754 754
755void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
756 IPC::ResponseBuilder rb{ctx, 2};
757 rb.Push(RESULT_SUCCESS);
758 LOG_WARNING(Service_AM, "(STUBBED) called");
759}
760
755void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 761void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
756 Kernel::HLERequestContext& ctx) { 762 Kernel::HLERequestContext& ctx) {
757 IPC::ResponseBuilder rb{ctx, 2}; 763 IPC::ResponseBuilder rb{ctx, 2};
@@ -821,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
821 827
822void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 828void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
823 // Takes an input u32 Result, no output. 829 // Takes an input u32 Result, no output.
824 // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. 830 // For example, in some cases official apps use this with error 0x2A2 then
831 // uses svcBreak.
825 832
826 IPC::RequestParser rp{ctx}; 833 IPC::RequestParser rp{ctx};
827 u32 result = rp.Pop<u32>(); 834 u32 result = rp.Pop<u32>();
@@ -884,8 +891,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
884void InstallInterfaces(SM::ServiceManager& service_manager, 891void InstallInterfaces(SM::ServiceManager& service_manager,
885 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 892 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
886 auto message_queue = std::make_shared<AppletMessageQueue>(); 893 auto message_queue = std::make_shared<AppletMessageQueue>();
887 message_queue->PushMessage( 894 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
888 AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot 895 // game boot
889 896
890 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); 897 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
891 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); 898 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 2f1c20bce..5a3fcba8f 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -185,6 +185,7 @@ private:
185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 185 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 186 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); 187 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
188 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
188}; 189};
189 190
190class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 191class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 35a8bef6c..d3ea57ea7 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -28,13 +28,13 @@ public:
28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
30 {3, &IAudioRenderer::GetState, "GetState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, 31 {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
32 {5, &IAudioRenderer::Start, "Start"}, 32 {5, &IAudioRenderer::Start, "Start"},
33 {6, &IAudioRenderer::Stop, "Stop"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
35 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, 35 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
36 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, 36 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
37 {10, nullptr, "RequestUpdateAuto"}, 37 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
38 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
39 }; 39 };
40 // clang-format on 40 // clang-format on
@@ -79,7 +79,7 @@ private:
79 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
80 } 80 }
81 81
82 void RequestUpdate(Kernel::HLERequestContext& ctx) { 82 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
84 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 783c39503..763e619a4 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -77,8 +77,8 @@ private:
77 IPC::ResponseBuilder rb{ctx, 6}; 77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS); 78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed); 79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count); 80 rb.Push<u32>(sample_count);
81 rb.Push<u64>(performance);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 } 83 }
84 84
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index d0a15cc4c..f3bde6d0d 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -2,12 +2,49 @@
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/hle/ipc_helpers.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/service/btdrv/btdrv.h" 9#include "core/hle/service/btdrv/btdrv.h"
6#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h" 11#include "core/hle/service/sm/sm.h"
8 12
9namespace Service::BtDrv { 13namespace Service::BtDrv {
10 14
15class Bt final : public ServiceFramework<Bt> {
16public:
17 explicit Bt() : ServiceFramework{"bt"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Unknown0"},
21 {1, nullptr, "Unknown1"},
22 {2, nullptr, "Unknown2"},
23 {3, nullptr, "Unknown3"},
24 {4, nullptr, "Unknown4"},
25 {5, nullptr, "Unknown5"},
26 {6, nullptr, "Unknown6"},
27 {7, nullptr, "Unknown7"},
28 {8, nullptr, "Unknown8"},
29 {9, &Bt::RegisterEvent, "RegisterEvent"},
30 };
31 // clang-format on
32 RegisterHandlers(functions);
33 }
34
35private:
36 void RegisterEvent(Kernel::HLERequestContext& ctx) {
37 auto& kernel = Core::System::GetInstance().Kernel();
38 register_event =
39 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
40 IPC::ResponseBuilder rb{ctx, 2, 1};
41 rb.Push(RESULT_SUCCESS);
42 rb.PushCopyObjects(register_event);
43 LOG_WARNING(Service_BTM, "(STUBBED) called");
44 }
45 Kernel::SharedPtr<Kernel::Event> register_event;
46};
47
11class BtDrv final : public ServiceFramework<BtDrv> { 48class BtDrv final : public ServiceFramework<BtDrv> {
12public: 49public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} { 50 explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +104,7 @@ public:
67 104
68void InstallInterfaces(SM::ServiceManager& sm) { 105void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm); 106 std::make_shared<BtDrv>()->InstallAsService(sm);
107 std::make_shared<Bt>()->InstallAsService(sm);
70} 108}
71 109
72} // namespace Service::BtDrv 110} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b949bfabd..a02f6b53a 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -6,13 +6,118 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/btm/btm.h" 11#include "core/hle/service/btm/btm.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 13
14namespace Service::BTM { 14namespace Service::BTM {
15 15
16class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
17public:
18 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
22 {1, nullptr, "Unknown1"},
23 {2, nullptr, "Unknown2"},
24 {3, nullptr, "Unknown3"},
25 {4, nullptr, "Unknown4"},
26 {5, nullptr, "Unknown5"},
27 {6, nullptr, "Unknown6"},
28 {7, nullptr, "Unknown7"},
29 {8, nullptr, "Unknown8"},
30 {9, nullptr, "Unknown9"},
31 {10, nullptr, "Unknown10"},
32 {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
33 {18, nullptr, "Unknown18"},
34 {19, nullptr, "Unknown19"},
35 {20, nullptr, "Unknown20"},
36 {21, nullptr, "Unknown21"},
37 {22, nullptr, "Unknown22"},
38 {23, nullptr, "Unknown23"},
39 {24, nullptr, "Unknown24"},
40 {25, nullptr, "Unknown25"},
41 {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
42 {27, nullptr, "Unknown27"},
43 {28, nullptr, "Unknown28"},
44 {29, nullptr, "Unknown29"},
45 {30, nullptr, "Unknown30"},
46 {31, nullptr, "Unknown31"},
47 {32, nullptr, "Unknown32"},
48 {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
49 {34, nullptr, "Unknown34"},
50 {35, nullptr, "Unknown35"},
51 {36, nullptr, "Unknown36"},
52 {37, nullptr, "Unknown37"},
53 };
54 // clang-format on
55 RegisterHandlers(functions);
56 }
57
58private:
59 void GetScanEvent(Kernel::HLERequestContext& ctx) {
60 auto& kernel = Core::System::GetInstance().Kernel();
61 scan_event =
62 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
63 IPC::ResponseBuilder rb{ctx, 2, 1};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushCopyObjects(scan_event);
66 LOG_WARNING(Service_BTM, "(STUBBED) called");
67 }
68 void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
69 auto& kernel = Core::System::GetInstance().Kernel();
70 connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
71 "IBtmUserCore:ConnectionEvent");
72 IPC::ResponseBuilder rb{ctx, 2, 1};
73 rb.Push(RESULT_SUCCESS);
74 rb.PushCopyObjects(connection_event);
75 LOG_WARNING(Service_BTM, "(STUBBED) called");
76 }
77 void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
78 auto& kernel = Core::System::GetInstance().Kernel();
79 service_discovery =
80 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
81 IPC::ResponseBuilder rb{ctx, 2, 1};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushCopyObjects(service_discovery);
84 LOG_WARNING(Service_BTM, "(STUBBED) called");
85 }
86 void GetConfigEvent(Kernel::HLERequestContext& ctx) {
87 auto& kernel = Core::System::GetInstance().Kernel();
88 config_event =
89 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
90 IPC::ResponseBuilder rb{ctx, 2, 1};
91 rb.Push(RESULT_SUCCESS);
92 rb.PushCopyObjects(config_event);
93 LOG_WARNING(Service_BTM, "(STUBBED) called");
94 }
95 Kernel::SharedPtr<Kernel::Event> scan_event;
96 Kernel::SharedPtr<Kernel::Event> connection_event;
97 Kernel::SharedPtr<Kernel::Event> service_discovery;
98 Kernel::SharedPtr<Kernel::Event> config_event;
99};
100
101class BTM_USR final : public ServiceFramework<BTM_USR> {
102public:
103 explicit BTM_USR() : ServiceFramework{"btm:u"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
107 };
108 // clang-format on
109 RegisterHandlers(functions);
110 }
111
112private:
113 void GetCoreImpl(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(RESULT_SUCCESS);
116 rb.PushIpcInterface<IBtmUserCore>();
117 LOG_DEBUG(Service_BTM, "called");
118 }
119};
120
16class BTM final : public ServiceFramework<BTM> { 121class BTM final : public ServiceFramework<BTM> {
17public: 122public:
18 explicit BTM() : ServiceFramework{"btm"} { 123 explicit BTM() : ServiceFramework{"btm"} {
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
116 std::make_shared<BTM>()->InstallAsService(sm); 221 std::make_shared<BTM>()->InstallAsService(sm);
117 std::make_shared<BTM_DBG>()->InstallAsService(sm); 222 std::make_shared<BTM_DBG>()->InstallAsService(sm);
118 std::make_shared<BTM_SYS>()->InstallAsService(sm); 223 std::make_shared<BTM_SYS>()->InstallAsService(sm);
224 std::make_shared<BTM_USR>()->InstallAsService(sm);
119} 225}
120 226
121} // namespace Service::BTM 227} // namespace Service::BTM
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index ea8fd965a..5d6294016 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -303,7 +303,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
303 static_cast<u8>(space), save_struct.DebugInfo()); 303 static_cast<u8>(space), save_struct.DebugInfo());
304 304
305 if (save_data_factory == nullptr) { 305 if (save_data_factory == nullptr) {
306 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); 306 return FileSys::ERROR_ENTITY_NOT_FOUND;
307 } 307 }
308 308
309 return save_data_factory->Open(space, save_struct); 309 return save_data_factory->Open(space, save_struct);
@@ -313,7 +313,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
313 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); 313 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
314 314
315 if (save_data_factory == nullptr) { 315 if (save_data_factory == nullptr) {
316 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); 316 return FileSys::ERROR_ENTITY_NOT_FOUND;
317 } 317 }
318 318
319 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); 319 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
@@ -323,15 +323,22 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
323 LOG_TRACE(Service_FS, "Opening SDMC"); 323 LOG_TRACE(Service_FS, "Opening SDMC");
324 324
325 if (sdmc_factory == nullptr) { 325 if (sdmc_factory == nullptr) {
326 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); 326 return FileSys::ERROR_SD_CARD_NOT_FOUND;
327 } 327 }
328 328
329 return sdmc_factory->Open(); 329 return sdmc_factory->Open();
330} 330}
331 331
332std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 332std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
333 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ 333
334 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); 334std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
335 if (registered_cache_union == nullptr) {
336 registered_cache_union =
337 std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
338 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
339 }
340
341 return registered_cache_union;
335} 342}
336 343
337FileSys::RegisteredCache* GetSystemNANDContents() { 344FileSys::RegisteredCache* GetSystemNANDContents() {
@@ -370,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
370 return bis_factory->GetModificationLoadRoot(title_id); 377 return bis_factory->GetModificationLoadRoot(title_id);
371} 378}
372 379
380FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
381 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
382
383 if (bis_factory == nullptr)
384 return nullptr;
385
386 return bis_factory->GetModificationDumpRoot(title_id);
387}
388
373void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 389void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
374 if (overwrite) { 390 if (overwrite) {
375 bis_factory = nullptr; 391 bis_factory = nullptr;
@@ -383,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
383 FileSys::Mode::ReadWrite); 399 FileSys::Mode::ReadWrite);
384 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 400 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
385 FileSys::Mode::ReadWrite); 401 FileSys::Mode::ReadWrite);
402 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
403 FileSys::Mode::ReadWrite);
386 404
387 if (bis_factory == nullptr) 405 if (bis_factory == nullptr) {
388 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 406 bis_factory =
389 if (save_data_factory == nullptr) 407 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
408 }
409
410 if (save_data_factory == nullptr) {
390 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 411 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
391 if (sdmc_factory == nullptr) 412 }
413
414 if (sdmc_factory == nullptr) {
392 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 415 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
416 }
393} 417}
394 418
395void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { 419void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 2cbb70c87..ff9182e84 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -48,13 +48,14 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); 48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
49ResultVal<FileSys::VirtualDir> OpenSDMC(); 49ResultVal<FileSys::VirtualDir> OpenSDMC();
50 50
51std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 51std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
52 52
53FileSys::RegisteredCache* GetSystemNANDContents(); 53FileSys::RegisteredCache* GetSystemNANDContents();
54FileSys::RegisteredCache* GetUserNANDContents(); 54FileSys::RegisteredCache* GetUserNANDContents();
55FileSys::RegisteredCache* GetSDMCContents(); 55FileSys::RegisteredCache* GetSDMCContents();
56 56
57FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 57FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
58FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
58 59
59// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 60// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
60// above is called. 61// above is called.
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b9a1d5105..038dc80b1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -63,12 +63,12 @@ private:
63 // Error checking 63 // Error checking
64 if (length < 0) { 64 if (length < 0) {
65 IPC::ResponseBuilder rb{ctx, 2}; 65 IPC::ResponseBuilder rb{ctx, 2};
66 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 66 rb.Push(FileSys::ERROR_INVALID_SIZE);
67 return; 67 return;
68 } 68 }
69 if (offset < 0) { 69 if (offset < 0) {
70 IPC::ResponseBuilder rb{ctx, 2}; 70 IPC::ResponseBuilder rb{ctx, 2};
71 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 71 rb.Push(FileSys::ERROR_INVALID_OFFSET);
72 return; 72 return;
73 } 73 }
74 74
@@ -108,12 +108,12 @@ private:
108 // Error checking 108 // Error checking
109 if (length < 0) { 109 if (length < 0) {
110 IPC::ResponseBuilder rb{ctx, 2}; 110 IPC::ResponseBuilder rb{ctx, 2};
111 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 111 rb.Push(FileSys::ERROR_INVALID_SIZE);
112 return; 112 return;
113 } 113 }
114 if (offset < 0) { 114 if (offset < 0) {
115 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 116 rb.Push(FileSys::ERROR_INVALID_OFFSET);
117 return; 117 return;
118 } 118 }
119 119
@@ -139,12 +139,12 @@ private:
139 // Error checking 139 // Error checking
140 if (length < 0) { 140 if (length < 0) {
141 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
142 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 142 rb.Push(FileSys::ERROR_INVALID_SIZE);
143 return; 143 return;
144 } 144 }
145 if (offset < 0) { 145 if (offset < 0) {
146 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
147 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 147 rb.Push(FileSys::ERROR_INVALID_OFFSET);
148 return; 148 return;
149 } 149 }
150 150
@@ -744,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
744 744
745 if (dir.Failed()) { 745 if (dir.Failed()) {
746 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 746 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
747 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 747 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
748 return; 748 return;
749 } 749 }
750 750
@@ -836,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
836 static_cast<u8>(storage_id), title_id); 836 static_cast<u8>(storage_id), title_id);
837 837
838 IPC::ResponseBuilder rb{ctx, 2}; 838 IPC::ResponseBuilder rb{ctx, 2};
839 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 839 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
840} 840}
841 841
842} // namespace Service::FileSystem 842} // namespace Service::FileSystem
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 56c415e4e..205e4fd14 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -40,6 +40,29 @@ enum class JoystickId : std::size_t {
40 Joystick_Right, 40 Joystick_Right,
41}; 41};
42 42
43static std::size_t NPadIdToIndex(u32 npad_id) {
44 switch (npad_id) {
45 case 0:
46 case 1:
47 case 2:
48 case 3:
49 case 4:
50 case 5:
51 case 6:
52 case 7:
53 return npad_id;
54 case 8:
55 case NPAD_HANDHELD:
56 return 8;
57 case 9:
58 case NPAD_UNKNOWN:
59 return 9;
60 default:
61 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
62 return 0;
63 }
64}
65
43Controller_NPad::Controller_NPad() = default; 66Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default; 67Controller_NPad::~Controller_NPad() = default;
45 68
@@ -288,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
288 switch (controller_type) { 311 switch (controller_type) {
289 case NPadControllerType::Handheld: 312 case NPadControllerType::Handheld:
290 handheld_entry.connection_status.raw = 0; 313 handheld_entry.connection_status.raw = 0;
291 handheld_entry.connection_status.IsConnected.Assign(1); 314 handheld_entry.connection_status.IsWired.Assign(1);
292 if (!Settings::values.use_docked_mode) { 315 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
293 handheld_entry.connection_status.IsWired.Assign(1); 316 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
294 } 317 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
318 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
295 handheld_entry.pad_states.raw = pad_state.raw; 319 handheld_entry.pad_states.raw = pad_state.raw;
296 handheld_entry.l_stick = lstick_entry; 320 handheld_entry.l_stick = lstick_entry;
297 handheld_entry.r_stick = rstick_entry; 321 handheld_entry.r_stick = rstick_entry;
@@ -371,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
371 supported_npad_id_types.clear(); 395 supported_npad_id_types.clear();
372 supported_npad_id_types.resize(length / sizeof(u32)); 396 supported_npad_id_types.resize(length / sizeof(u32));
373 std::memcpy(supported_npad_id_types.data(), data, length); 397 std::memcpy(supported_npad_id_types.data(), data, length);
398 bool had_controller_update = false;
374 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 399 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
375 auto& controller = connected_controllers[i]; 400 auto& controller = connected_controllers[i];
376 if (!controller.is_connected) { 401 if (!controller.is_connected) {
377 continue; 402 continue;
378 } 403 }
379 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 404 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
380 controller.type = DecideBestController(PREFERRED_CONTROLLER); 405 const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
381 InitNewlyAddedControler(i); 406 const bool is_handheld = (best_type == NPadControllerType::Handheld ||
407 PREFERRED_CONTROLLER == NPadControllerType::Handheld);
408 if (is_handheld) {
409 controller.type = NPadControllerType::None;
410 controller.is_connected = false;
411 AddNewController(best_type);
412 } else {
413 controller.type = best_type;
414 InitNewlyAddedControler(i);
415 }
416 had_controller_update = true;
382 } 417 }
383 } 418 }
419 if (had_controller_update) {
420 styleset_changed_event->Signal();
421 }
384} 422}
385 423
386void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 424void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -458,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
458} 496}
459 497
460void Controller_NPad::ConnectNPad(u32 npad_id) { 498void Controller_NPad::ConnectNPad(u32 npad_id) {
461 if (npad_id >= connected_controllers.size()) 499 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
462 return;
463 connected_controllers[npad_id].is_connected = true;
464} 500}
465 501
466void Controller_NPad::DisconnectNPad(u32 npad_id) { 502void Controller_NPad::DisconnectNPad(u32 npad_id) {
467 if (npad_id >= connected_controllers.size()) 503 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
468 return;
469 connected_controllers[npad_id].is_connected = false;
470} 504}
471 505
472Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 506Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a45fd4954..39631b14f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -286,10 +286,10 @@ public:
286 {519, nullptr, "GetPalmaOperationResult"}, 286 {519, nullptr, "GetPalmaOperationResult"},
287 {520, nullptr, "ReadPalmaPlayLog"}, 287 {520, nullptr, "ReadPalmaPlayLog"},
288 {521, nullptr, "ResetPalmaPlayLog"}, 288 {521, nullptr, "ResetPalmaPlayLog"},
289 {522, nullptr, "SetIsPalmaAllConnectable"}, 289 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
290 {523, nullptr, "SetIsPalmaPairedConnectable"}, 290 {523, nullptr, "SetIsPalmaPairedConnectable"},
291 {524, nullptr, "PairPalma"}, 291 {524, nullptr, "PairPalma"},
292 {525, nullptr, "SetPalmaBoostMode"}, 292 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
293 {1000, nullptr, "SetNpadCommunicationMode"}, 293 {1000, nullptr, "SetNpadCommunicationMode"},
294 {1001, nullptr, "GetNpadCommunicationMode"}, 294 {1001, nullptr, "GetNpadCommunicationMode"},
295 }; 295 };
@@ -596,6 +596,18 @@ private:
596 rb.Push(RESULT_SUCCESS); 596 rb.Push(RESULT_SUCCESS);
597 LOG_WARNING(Service_HID, "(STUBBED) called"); 597 LOG_WARNING(Service_HID, "(STUBBED) called");
598 } 598 }
599
600 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
601 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(RESULT_SUCCESS);
603 LOG_WARNING(Service_HID, "(STUBBED) called");
604 }
605
606 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
607 IPC::ResponseBuilder rb{ctx, 2};
608 rb.Push(RESULT_SUCCESS);
609 LOG_WARNING(Service_HID, "(STUBBED) called");
610 }
599}; 611};
600 612
601class HidDbg final : public ServiceFramework<HidDbg> { 613class HidDbg final : public ServiceFramework<HidDbg> {
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 69c260408..b2de2a818 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -28,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
28 28
29 std::size_t size = ctx.GetWriteBufferSize(); 29 std::size_t size = ctx.GetWriteBufferSize();
30 30
31 std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
31 std::vector<u8> data(size); 32 std::vector<u8> data(size);
32 std::generate(data.begin(), data.end(), rng); 33 std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
33 34
34 ctx.WriteBuffer(data); 35 ctx.WriteBuffer(data);
35 36
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index e3cbd7004..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -23,7 +23,8 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
27 "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 28 {501, nullptr, "CalculateSpanBetween"},
28 }; 29 };
29 RegisterHandlers(functions); 30 RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 85e7b1195..e561a0c52 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -299,6 +299,21 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); 299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300} 300}
301 301
302void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
303 Kernel::HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called");
305
306 IPC::RequestParser rp{ctx};
307 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
308 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
309 const u64 difference =
310 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
311
312 IPC::ResponseBuilder rb{ctx, 4};
313 rb.Push(RESULT_SUCCESS);
314 rb.PushRaw<u64>(difference);
315}
316
302Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
303 : ServiceFramework(name), time(std::move(time)) {} 318 : ServiceFramework(name), time(std::move(time)) {}
304 319
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 77871ae07..ea43fbea7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -84,6 +84,7 @@ public:
84 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx); 86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
87 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
87 88
88 protected: 89 protected:
89 std::shared_ptr<Module> time; 90 std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d764b2406..d25fdb1fe 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -237,6 +237,22 @@ private:
237 Data data{}; 237 Data data{};
238}; 238};
239 239
240/// Represents a parcel containing one int '0' as its data
241/// Used by DetachBuffer and Disconnect
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
242 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -554,6 +570,12 @@ private:
554 ctx.WriteBuffer(response.Serialize()); 570 ctx.WriteBuffer(response.Serialize());
555 } else if (transaction == TransactionId::CancelBuffer) { 571 } else if (transaction == TransactionId::CancelBuffer) {
556 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 572 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
573 } else if (transaction == TransactionId::Disconnect ||
574 transaction == TransactionId::DetachBuffer) {
575 const auto buffer = ctx.ReadBuffer();
576
577 IGBPEmptyResponseParcel response{};
578 ctx.WriteBuffer(response.Serialize());
557 } else { 579 } else {
558 ASSERT_MSG(false, "Unimplemented"); 580 ASSERT_MSG(false, "Unimplemented");
559 } 581 }
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 68efca5c0..aaf006309 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
154 program_image.resize(image_size); 154 program_image.resize(image_size);
155 155
156 // Apply patches if necessary 156 // Apply patches if necessary
157 if (pm && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
158 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
diff --git a/src/core/settings.h b/src/core/settings.h
index 84dc5050b..e424479f2 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -159,6 +159,7 @@ struct Values {
159 bool use_gdbstub; 159 bool use_gdbstub;
160 u16 gdbstub_port; 160 u16 gdbstub_port;
161 std::string program_args; 161 std::string program_args;
162 bool dump_nso;
162 163
163 // WebService 164 // WebService
164 bool enable_telemetry; 165 bool enable_telemetry;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 54cc47a9b..84bd91eed 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
107 107
108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); 108 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
109 OpenGLState::ApplyDefaultState(); 109 OpenGLState::ApplyDefaultState();
110 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
111 state.clip_distance[0] = true;
112 110
113 // Create render framebuffer 111 // Create render framebuffer
114 framebuffer.Create(); 112 framebuffer.Create();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 26711e6f7..b44ecfa1c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -550,8 +550,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
550 if (params.is_layered) { 550 if (params.is_layered) {
551 u64 offset = params.GetMipmapLevelOffset(mip_level); 551 u64 offset = params.GetMipmapLevelOffset(mip_level);
552 u64 offset_gl = 0; 552 u64 offset_gl = 0;
553 u64 layer_size = params.LayerMemorySize(); 553 const u64 layer_size = params.LayerMemorySize();
554 u64 gl_size = params.LayerSizeGL(mip_level); 554 const u64 gl_size = params.LayerSizeGL(mip_level);
555 for (u32 i = 0; i < params.depth; i++) { 555 for (u32 i = 0; i < params.depth; i++) {
556 functions[static_cast<std::size_t>(params.pixel_format)]( 556 functions[static_cast<std::size_t>(params.pixel_format)](
557 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 557 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
@@ -561,7 +561,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
561 offset_gl += gl_size; 561 offset_gl += gl_size;
562 } 562 }
563 } else { 563 } else {
564 u64 offset = params.GetMipmapLevelOffset(mip_level); 564 const u64 offset = params.GetMipmapLevelOffset(mip_level);
565 functions[static_cast<std::size_t>(params.pixel_format)]( 565 functions[static_cast<std::size_t>(params.pixel_format)](
566 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 566 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
567 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), 567 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 2635f2b0c..98622a058 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -266,7 +266,8 @@ void OpenGLState::ApplyViewport() const {
266 const auto& updated = viewports[0]; 266 const auto& updated = viewports[0];
267 if (updated.x != current.x || updated.y != current.y || updated.width != current.width || 267 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
268 updated.height != current.height) { 268 updated.height != current.height) {
269 glViewport(updated.x, updated.y, updated.width, updated.height); 269 glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
270 static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
270 } 271 }
271 if (updated.depth_range_near != current.depth_range_near || 272 if (updated.depth_range_near != current.depth_range_near ||
272 updated.depth_range_far != current.depth_range_far) { 273 updated.depth_range_far != current.depth_range_far) {
@@ -313,7 +314,7 @@ void OpenGLState::ApplyGlobalBlending() const {
313 } 314 }
314} 315}
315 316
316void OpenGLState::ApplyTargetBlending(int target, bool force) const { 317void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
317 const Blend& updated = blend[target]; 318 const Blend& updated = blend[target];
318 const Blend& current = cur_state.blend[target]; 319 const Blend& current = cur_state.blend[target];
319 const bool blend_changed = updated.enabled != current.enabled || force; 320 const bool blend_changed = updated.enabled != current.enabled || force;
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index eacca0b9c..e5d1baae6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -208,7 +208,7 @@ private:
208 void ApplyPrimitiveRestart() const; 208 void ApplyPrimitiveRestart() const;
209 void ApplyStencilTest() const; 209 void ApplyStencilTest() const;
210 void ApplyViewport() const; 210 void ApplyViewport() const;
211 void ApplyTargetBlending(int target, bool force) const; 211 void ApplyTargetBlending(std::size_t target, bool force) const;
212 void ApplyGlobalBlending() const; 212 void ApplyGlobalBlending() const;
213 void ApplyBlending() const; 213 void ApplyBlending() const;
214 void ApplyLogicOp() const; 214 void ApplyLogicOp() const;
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index a9d134d14..19f30b1b5 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -45,7 +45,7 @@ constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
45 * Instead of going gob by gob, we map the coordinates inside a block and manage from 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1. 46 * those. Block_Width is assumed to be 1.
47 */ 47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 48void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset, 50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -81,7 +81,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
81 * Instead of going gob by gob, we map the coordinates inside a block and manage from 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1. 82 * those. Block_Width is assumed to be 1.
83 */ 83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 84void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset, 86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -90,10 +90,10 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
90 u32 z_address = tile_offset; 90 u32 z_address = tile_offset;
91 const u32 x_startb = x_start * bytes_per_pixel; 91 const u32 x_startb = x_start * bytes_per_pixel;
92 const u32 x_endb = x_end * bytes_per_pixel; 92 const u32 x_endb = x_end * bytes_per_pixel;
93 const u32 copy_size = 16; 93 constexpr u32 copy_size = 16;
94 const u32 gob_size_x = 64; 94 constexpr u32 gob_size_x = 64;
95 const u32 gob_size_y = 8; 95 constexpr u32 gob_size_y = 8;
96 const u32 gob_size_z = 1; 96 constexpr u32 gob_size_z = 1;
97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
98 for (u32 z = z_start; z < z_end; z++) { 98 for (u32 z = z_start; z < z_end; z++) {
99 u32 y_address = z_address; 99 u32 y_address = z_address;
@@ -126,23 +126,23 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces 126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */ 127 */
128template <bool fast> 128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width, 129void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel, 130 const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) { 131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; 132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel; 133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x; 134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64; 135 constexpr u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; 136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8; 137 constexpr u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1; 138 constexpr u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x; 139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height; 140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth; 141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements); 142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements); 143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements); 144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; 145 constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 xy_block_size = gob_size * block_height; 146 const u32 xy_block_size = gob_size * block_height;
147 const u32 block_size = xy_block_size * block_depth; 147 const u32 block_size = xy_block_size * block_depth;
148 u32 tile_offset = 0; 148 u32 tile_offset = 0;
@@ -171,7 +171,7 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
171} 171}
172 172
173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
174 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 174 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
175 bool unswizzle, u32 block_height, u32 block_depth) { 175 bool unswizzle, u32 block_height, u32 block_depth) {
176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, 177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
@@ -325,9 +325,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
325std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 325std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
326 u32 block_height, u32 block_depth) { 326 u32 block_height, u32 block_depth) {
327 if (tiled) { 327 if (tiled) {
328 const u32 gobs_in_x = 64; 328 constexpr u32 gobs_in_x = 64;
329 const u32 gobs_in_y = 8; 329 constexpr u32 gobs_in_y = 8;
330 const u32 gobs_in_z = 1; 330 constexpr u32 gobs_in_z = 1;
331 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); 331 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
332 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); 332 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
333 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); 333 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d3b7fa59d..be69fb831 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -153,6 +153,7 @@ void Config::ReadValues() {
153 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 153 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
154 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 154 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
155 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 155 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
156 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
156 qt_config->endGroup(); 157 qt_config->endGroup();
157 158
158 qt_config->beginGroup("WebService"); 159 qt_config->beginGroup("WebService");
@@ -170,6 +171,7 @@ void Config::ReadValues() {
170 171
171 qt_config->beginGroup("UIGameList"); 172 qt_config->beginGroup("UIGameList");
172 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 173 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
174 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
173 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 175 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
174 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); 176 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
175 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); 177 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
@@ -295,6 +297,7 @@ void Config::SaveValues() {
295 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 297 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
296 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 298 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
297 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 299 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
300 qt_config->setValue("dump_nso", Settings::values.dump_nso);
298 qt_config->endGroup(); 301 qt_config->endGroup();
299 302
300 qt_config->beginGroup("WebService"); 303 qt_config->beginGroup("WebService");
@@ -310,6 +313,7 @@ void Config::SaveValues() {
310 313
311 qt_config->beginGroup("UIGameList"); 314 qt_config->beginGroup("UIGameList");
312 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 315 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
316 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
313 qt_config->setValue("icon_size", UISettings::values.icon_size); 317 qt_config->setValue("icon_size", UISettings::values.icon_size);
314 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 318 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
315 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); 319 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 9e765fc93..fd5876b41 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
37} 38}
38 39
39void ConfigureDebug::applyConfiguration() { 40void ConfigureDebug::applyConfiguration() {
@@ -42,6 +43,7 @@ void ConfigureDebug::applyConfiguration() {
42 UISettings::values.show_console = ui->toggle_console->isChecked(); 43 UISettings::values.show_console = ui->toggle_console->isChecked();
43 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 44 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
44 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 45 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
46 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
45 Debugger::ToggleConsole(); 47 Debugger::ToggleConsole();
46 Log::Filter filter; 48 Log::Filter filter;
47 filter.ParseFilterString(Settings::values.log_filter); 49 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index ff4987604..9c5b702f8 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>300</height> 10 <height>357</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -130,6 +130,25 @@
130 </widget> 130 </widget>
131 </item> 131 </item>
132 <item> 132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Dump</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_4">
138 <item>
139 <widget class="QCheckBox" name="dump_decompressed_nso">
140 <property name="whatsThis">
141 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
142 </property>
143 <property name="text">
144 <string>Dump Decompressed NSOs</string>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </widget>
150 </item>
151 <item>
133 <spacer name="verticalSpacer"> 152 <spacer name="verticalSpacer">
134 <property name="orientation"> 153 <property name="orientation">
135 <enum>Qt::Vertical</enum> 154 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 8743ce982..639d5df0f 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -42,6 +42,7 @@ ConfigureGameList::~ConfigureGameList() = default;
42 42
43void ConfigureGameList::applyConfiguration() { 43void ConfigureGameList::applyConfiguration() {
44 UISettings::values.show_unknown = ui->show_unknown->isChecked(); 44 UISettings::values.show_unknown = ui->show_unknown->isChecked();
45 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
45 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 46 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
46 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 47 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
47 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 48 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
@@ -50,6 +51,7 @@ void ConfigureGameList::applyConfiguration() {
50 51
51void ConfigureGameList::setConfiguration() { 52void ConfigureGameList::setConfiguration() {
52 ui->show_unknown->setChecked(UISettings::values.show_unknown); 53 ui->show_unknown->setChecked(UISettings::values.show_unknown);
54 ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
53 ui->icon_size_combobox->setCurrentIndex( 55 ui->icon_size_combobox->setCurrentIndex(
54 ui->icon_size_combobox->findData(UISettings::values.icon_size)); 56 ui->icon_size_combobox->findData(UISettings::values.icon_size));
55 ui->row_1_text_combobox->setCurrentIndex( 57 ui->row_1_text_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_gamelist.ui
index 7471fdb60..7a69377e7 100644
--- a/src/yuzu/configuration/configure_gamelist.ui
+++ b/src/yuzu/configuration/configure_gamelist.ui
@@ -1,126 +1,133 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureGameList</class> 3 <class>ConfigureGameList</class>
4 <widget class="QWidget" name="ConfigureGeneral"> 4 <widget class="QWidget" name="ConfigureGameList">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>377</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Form</string> 14 <string>Form</string>
15 </property> 15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout"> 16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout"> 18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
19 <item> 27 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox"> 28 <widget class="QCheckBox" name="show_unknown">
21 <property name="title"> 29 <property name="text">
22 <string>General</string> 30 <string>Show files with type 'Unknown'</string>
23 </property> 31 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> 32 </widget>
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item>
28 <widget class="QCheckBox" name="show_unknown">
29 <property name="text">
30 <string>Show files with type 'Unknown'</string>
31 </property>
32 </widget>
33 </item>
34 </layout>
35 </item>
36 </layout>
37 </widget>
38 </item> 33 </item>
39 <item> 34 <item>
40 <widget class="QGroupBox" name="IconSizeGroupBox"> 35 <widget class="QCheckBox" name="show_add_ons">
41 <property name="title"> 36 <property name="text">
42 <string>Icon Size</string> 37 <string>Show Add-Ons Column</string>
43 </property> 38 </property>
44 <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> 39 </widget>
45 <item>
46 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
47 <item>
48 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
49 <item>
50 <widget class="QLabel" name="icon_size_label">
51 <property name="text">
52 <string>Icon Size:</string>
53 </property>
54 </widget>
55 </item>
56 <item>
57 <widget class="QComboBox" name="icon_size_combobox"/>
58 </item>
59 </layout>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item> 40 </item>
41 </layout>
42 </item>
43 </layout>
44 </widget>
45 </item>
46 <item>
47 <widget class="QGroupBox" name="IconSizeGroupBox">
48 <property name="title">
49 <string>Icon Size</string>
50 </property>
51 <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
52 <item>
53 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
66 <item> 54 <item>
67 <widget class="QGroupBox" name="RowGroupBox"> 55 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
68 <property name="title"> 56 <item>
69 <string>Row Text</string> 57 <widget class="QLabel" name="icon_size_label">
58 <property name="text">
59 <string>Icon Size:</string>
70 </property> 60 </property>
71 <layout class="QHBoxLayout" name="RowHorizontalLayout"> 61 </widget>
72 <item> 62 </item>
73 <layout class="QVBoxLayout" name="RowVerticalLayout"> 63 <item>
74 <item> 64 <widget class="QComboBox" name="icon_size_combobox"/>
75 <layout class="QHBoxLayout" name="row_1_qhbox_layout"> 65 </item>
76 <item> 66 </layout>
77 <widget class="QLabel" name="row_1_label">
78 <property name="text">
79 <string>Row 1 Text:</string>
80 </property>
81 </widget>
82 </item>
83 <item>
84 <widget class="QComboBox" name="row_1_text_combobox"/>
85 </item>
86 </layout>
87 </item>
88 <item>
89 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
90 <item>
91 <widget class="QLabel" name="row_2_label">
92 <property name="text">
93 <string>Row 2 Text:</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QComboBox" name="row_2_text_combobox"/>
99 </item>
100 </layout>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </widget>
106 </item> 67 </item>
68 </layout>
69 </item>
70 </layout>
71 </widget>
72 </item>
73 <item>
74 <widget class="QGroupBox" name="RowGroupBox">
75 <property name="title">
76 <string>Row Text</string>
77 </property>
78 <layout class="QHBoxLayout" name="RowHorizontalLayout">
79 <item>
80 <layout class="QVBoxLayout" name="RowVerticalLayout">
107 <item> 81 <item>
108 <spacer name="verticalSpacer"> 82 <layout class="QHBoxLayout" name="row_1_qhbox_layout">
109 <property name="orientation"> 83 <item>
110 <enum>Qt::Vertical</enum> 84 <widget class="QLabel" name="row_1_label">
85 <property name="text">
86 <string>Row 1 Text:</string>
111 </property> 87 </property>
112 <property name="sizeHint" stdset="0"> 88 </widget>
113 <size> 89 </item>
114 <width>20</width> 90 <item>
115 <height>40</height> 91 <widget class="QComboBox" name="row_1_text_combobox"/>
116 </size> 92 </item>
93 </layout>
94 </item>
95 <item>
96 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
97 <item>
98 <widget class="QLabel" name="row_2_label">
99 <property name="text">
100 <string>Row 2 Text:</string>
117 </property> 101 </property>
118 </spacer> 102 </widget>
103 </item>
104 <item>
105 <widget class="QComboBox" name="row_2_text_combobox"/>
106 </item>
107 </layout>
119 </item> 108 </item>
120 </layout> 109 </layout>
121 </item> 110 </item>
111 </layout>
112 </widget>
113 </item>
114 <item>
115 <spacer name="verticalSpacer">
116 <property name="orientation">
117 <enum>Qt::Vertical</enum>
118 </property>
119 <property name="sizeHint" stdset="0">
120 <size>
121 <width>20</width>
122 <height>40</height>
123 </size>
124 </property>
125 </spacer>
126 </item>
122 </layout> 127 </layout>
123 </widget> 128 </item>
129 </layout>
130 </widget>
124 <resources/> 131 <resources/>
125 <connections/> 132 <connections/>
126</ui> 133</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index b322258a0..314f51203 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -47,6 +47,9 @@ void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
47 } 47 }
48 48
49 Core::System& system{Core::System::GetInstance()}; 49 Core::System& system{Core::System::GetInstance()};
50 if (!system.IsPoweredOn()) {
51 return;
52 }
50 Service::SM::ServiceManager& sm = system.ServiceManager(); 53 Service::SM::ServiceManager& sm = system.ServiceManager();
51 54
52 // Message queue is shared between these services, we just need to signal an operation 55 // Message queue is shared between these services, we just need to signal an operation
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index a5a4aa432..11a8c390b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -215,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
215 tree_view->setUniformRowHeights(true); 215 tree_view->setUniformRowHeights(true);
216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
217 217
218 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); 221
222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); 222 if (UISettings::values.show_add_ons) {
223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); 223 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
224 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
225 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
226 } else {
227 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
228 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
229 }
224 230
225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 231 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 232 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -394,6 +400,25 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
394 } 400 }
395 401
396 tree_view->setEnabled(false); 402 tree_view->setEnabled(false);
403
404 // Update the columns in case UISettings has changed
405 item_model->removeColumns(0, item_model->columnCount());
406 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
407 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
408 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
409
410 if (UISettings::values.show_add_ons) {
411 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
412 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
413 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
414 } else {
415 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
416 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
417 item_model->removeColumns(COLUMN_COUNT - 1, 1);
418 }
419
420 LoadInterfaceLayout();
421
397 // Delete any rows that might already exist if we're repopulating 422 // Delete any rows that might already exist if we're repopulating
398 item_model->removeRows(0, item_model->rowCount()); 423 item_model->removeRows(0, item_model->rowCount());
399 424
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 3d865a12d..362902e46 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -123,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
123 if (it != compatibility_list.end()) 123 if (it != compatibility_list.end())
124 compatibility = it->second.first; 124 compatibility = it->second.first;
125 125
126 emit EntryReady({ 126 QList<QStandardItem*> list{
127 new GameListItemPath( 127 new GameListItemPath(
128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), 128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
130 program_id), 130 program_id),
131 new GameListItemCompat(compatibility), 131 new GameListItemCompat(compatibility),
132 new GameListItem(FormatPatchNameVersions(patch, *loader)),
133 new GameListItem( 132 new GameListItem(
134 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 133 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
135 new GameListItemSize(file->GetSize()), 134 new GameListItemSize(file->GetSize()),
136 }); 135 };
136
137 if (UISettings::values.show_add_ons) {
138 list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
139 }
140
141 emit EntryReady(list);
137 } 142 }
138 143
139 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, 144 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
@@ -216,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
216 if (it != compatibility_list.end()) 221 if (it != compatibility_list.end())
217 compatibility = it->second.first; 222 compatibility = it->second.first;
218 223
219 emit EntryReady({ 224 QList<QStandardItem*> list{
220 new GameListItemPath( 225 new GameListItemPath(
221 FormatGameName(physical_name), icon, QString::fromStdString(name), 226 FormatGameName(physical_name), icon, QString::fromStdString(name),
222 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 227 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
223 program_id), 228 program_id),
224 new GameListItemCompat(compatibility), 229 new GameListItemCompat(compatibility),
225 new GameListItem( 230 new GameListItem(
226 FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
227 new GameListItem(
228 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 231 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
229 new GameListItemSize(FileUtil::GetSize(physical_name)), 232 new GameListItemSize(FileUtil::GetSize(physical_name)),
230 }); 233 };
234
235 if (UISettings::values.show_add_ons) {
236 list.insert(2, new GameListItem(FormatPatchNameVersions(
237 patch, *loader, loader->IsRomFSUpdatable())));
238 }
239
240 emit EntryReady(std::move(list));
231 } else if (is_dir && recursion > 0) { 241 } else if (is_dir && recursion > 0) {
232 watch_list.append(QString::fromStdString(physical_name)); 242 watch_list.append(QString::fromStdString(physical_name));
233 AddFstEntriesToGameList(physical_name, recursion - 1); 243 AddFstEntriesToGameList(physical_name, recursion - 1);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 131ad19de..999086e7f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -12,7 +12,7 @@
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h" 13#include "core/hle/service/acc/profile_manager.h"
14 14
15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 15// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
16// defines. 16// defines.
17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -308,6 +308,8 @@ void GMainWindow::InitializeHotkeys() {
308 Qt::ApplicationShortcut); 308 Qt::ApplicationShortcut);
309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), 309 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
310 Qt::ApplicationShortcut); 310 Qt::ApplicationShortcut);
311 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
312 Qt::ApplicationShortcut);
311 hotkey_registry.LoadHotkeys(); 313 hotkey_registry.LoadHotkeys();
312 314
313 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 315 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -361,6 +363,12 @@ void GMainWindow::InitializeHotkeys() {
361 UpdateStatusBar(); 363 UpdateStatusBar();
362 } 364 }
363 }); 365 });
366 connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
367 this, [&] {
368 if (ui.action_Load_Amiibo->isEnabled()) {
369 OnLoadAmiibo();
370 }
371 });
364} 372}
365 373
366void GMainWindow::SetDefaultUIGeometry() { 374void GMainWindow::SetDefaultUIGeometry() {
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 2e617d52a..32a0d813c 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -59,6 +59,7 @@ struct Values {
59 59
60 // Game List 60 // Game List
61 bool show_unknown; 61 bool show_unknown;
62 bool show_add_ons;
62 uint32_t icon_size; 63 uint32_t icon_size;
63 uint8_t row_1_text_id; 64 uint8_t row_1_text_id;
64 uint8_t row_2_text_id; 65 uint8_t row_2_text_id;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index f3134d4cb..96f1ef636 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -139,6 +139,8 @@ void Config::ReadValues() {
139 Settings::values.rng_seed = std::nullopt; 139 Settings::values.rng_seed = std::nullopt;
140 } 140 }
141 141
142 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
143
142 // Miscellaneous 144 // Miscellaneous
143 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 145 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
144 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 146 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
@@ -148,6 +150,7 @@ void Config::ReadValues() {
148 Settings::values.gdbstub_port = 150 Settings::values.gdbstub_port =
149 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 151 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
150 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 152 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
153 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
151 154
152 // Web Service 155 // Web Service
153 Settings::values.enable_telemetry = 156 Settings::values.enable_telemetry =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index dd6644d79..ecf625e7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -206,6 +206,8 @@ log_filter = *:Trace
206# Port for listening to GDB connections. 206# Port for listening to GDB connections.
207use_gdbstub=false 207use_gdbstub=false
208gdbstub_port=24689 208gdbstub_port=24689
209# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
210dump_nso=false
209 211
210[WebService] 212[WebService]
211# Whether or not to enable telemetry 213# Whether or not to enable telemetry