summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/algorithm/interpolate.cpp5
-rw-r--r--src/audio_core/audio_out.cpp3
-rw-r--r--src/audio_core/audio_renderer.cpp7
-rw-r--r--src/audio_core/cubeb_sink.cpp2
-rw-r--r--src/audio_core/cubeb_sink.h2
-rw-r--r--src/audio_core/null_sink.h2
-rw-r--r--src/audio_core/sink_details.cpp53
-rw-r--r--src/audio_core/sink_details.h25
-rw-r--r--src/common/logging/backend.cpp4
-rw-r--r--src/common/thread_queue_list.h16
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/file_sys/directory.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp63
-rw-r--r--src/core/file_sys/patch_manager.h5
-rw-r--r--src/core/file_sys/savedata_factory.cpp8
-rw-r--r--src/core/file_sys/savedata_factory.h4
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/kernel/process.h3
-rw-r--r--src/core/hle/kernel/scheduler.cpp66
-rw-r--r--src/core/hle/kernel/scheduler.h69
-rw-r--r--src/core/hle/kernel/shared_memory.cpp14
-rw-r--r--src/core/hle/kernel/shared_memory.h6
-rw-r--r--src/core/hle/kernel/svc.cpp107
-rw-r--r--src/core/hle/kernel/svc.h16
-rw-r--r--src/core/hle/kernel/svc_wrap.h24
-rw-r--r--src/core/hle/kernel/thread.h1
-rw-r--r--src/core/hle/kernel/vm_manager.cpp29
-rw-r--r--src/core/hle/kernel/vm_manager.h178
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp12
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp28
-rw-r--r--src/core/hle/service/ldr/ldr.cpp9
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp4
-rw-r--r--src/core/hle/service/service.cpp31
-rw-r--r--src/core/hle/service/service.h8
-rw-r--r--src/core/hle/service/sm/sm.cpp8
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/loader.h12
-rw-r--r--src/core/loader/nax.cpp2
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/loader/nsp.cpp7
-rw-r--r--src/core/loader/nsp.h3
-rw-r--r--src/core/loader/xci.cpp7
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/core/memory.cpp11
-rw-r--r--src/core/settings.h5
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp45
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp266
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h3
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp30
-rw-r--r--src/yuzu/configuration/configure_audio.cpp7
-rw-r--r--src/yuzu/configuration/configure_per_general.cpp170
-rw-r--r--src/yuzu/configuration/configure_per_general.h50
-rw-r--r--src/yuzu/configuration/configure_per_general.ui276
-rw-r--r--src/yuzu/debugger/wait_tree.cpp2
-rw-r--r--src/yuzu/debugger/wait_tree.h2
-rw-r--r--src/yuzu/game_list.cpp3
-rw-r--r--src/yuzu/game_list.h1
-rw-r--r--src/yuzu/game_list_worker.cpp26
-rw-r--r--src/yuzu/main.cpp29
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp18
-rw-r--r--src/yuzu_cmd/default_ini.h7
72 files changed, 1487 insertions, 368 deletions
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 3aea9b0f2..5005ba519 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -54,8 +54,9 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
54 double l = 0.0; 54 double l = 0.0;
55 double r = 0.0; 55 double r = 0.0;
56 for (std::size_t j = 0; j < h.size(); j++) { 56 for (std::size_t j = 0; j < h.size(); j++) {
57 l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; 57 const double lanczos_calc = Lanczos(taps, pos + j - taps + 1);
58 r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; 58 l += lanczos_calc * h[j][0];
59 r += lanczos_calc * h[j][1];
59 } 60 }
60 output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0))); 61 output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
61 output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0))); 62 output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 0c8f5b18e..cbba17632 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -30,8 +30,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, 30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
31 Stream::ReleaseCallback&& release_callback) { 31 Stream::ReleaseCallback&& release_callback) {
32 if (!sink) { 32 if (!sink) {
33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); 33 sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
34 sink = sink_details.factory(Settings::values.audio_device_id);
35 } 34 }
36 35
37 return std::make_shared<Stream>( 36 return std::make_shared<Stream>(
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 2e59894ab..2683f3a5f 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -285,8 +285,11 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
285 break; 285 break;
286 } 286 }
287 287
288 samples = 288 // Only interpolate when necessary, expensive.
289 Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE); 289 if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) {
290 samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate,
291 STREAM_SAMPLE_RATE);
292 }
290 293
291 is_refresh_pending = false; 294 is_refresh_pending = false;
292} 295}
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index d31a1c844..097328901 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -107,7 +107,7 @@ private:
107 static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); 107 static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
108}; 108};
109 109
110CubebSink::CubebSink(std::string target_device_name) { 110CubebSink::CubebSink(std::string_view target_device_name) {
111 if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { 111 if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) {
112 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 112 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
113 return; 113 return;
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
index 59cbf05e9..efb9d1634 100644
--- a/src/audio_core/cubeb_sink.h
+++ b/src/audio_core/cubeb_sink.h
@@ -15,7 +15,7 @@ namespace AudioCore {
15 15
16class CubebSink final : public Sink { 16class CubebSink final : public Sink {
17public: 17public:
18 explicit CubebSink(std::string device_id); 18 explicit CubebSink(std::string_view device_id);
19 ~CubebSink() override; 19 ~CubebSink() override;
20 20
21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, 21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index a78d78893..61a28d542 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -10,7 +10,7 @@ namespace AudioCore {
10 10
11class NullSink final : public Sink { 11class NullSink final : public Sink {
12public: 12public:
13 explicit NullSink(std::string){}; 13 explicit NullSink(std::string_view) {}
14 ~NullSink() override = default; 14 ~NullSink() override = default;
15 15
16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, 16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index 67cf1f3b2..a848eb1c9 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -14,31 +14,68 @@
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15 15
16namespace AudioCore { 16namespace AudioCore {
17namespace {
18struct SinkDetails {
19 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
20 using ListDevicesFn = std::vector<std::string> (*)();
17 21
18// g_sink_details is ordered in terms of desirability, with the best choice at the top. 22 /// Name for this sink.
19const std::vector<SinkDetails> g_sink_details = { 23 const char* id;
24 /// A method to call to construct an instance of this type of sink.
25 FactoryFn factory;
26 /// A method to call to list available devices.
27 ListDevicesFn list_devices;
28};
29
30// sink_details is ordered in terms of desirability, with the best choice at the top.
31constexpr SinkDetails sink_details[] = {
20#ifdef HAVE_CUBEB 32#ifdef HAVE_CUBEB
21 SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices}, 33 SinkDetails{"cubeb",
34 [](std::string_view device_id) -> std::unique_ptr<Sink> {
35 return std::make_unique<CubebSink>(device_id);
36 },
37 &ListCubebSinkDevices},
22#endif 38#endif
23 SinkDetails{"null", &std::make_unique<NullSink, std::string>, 39 SinkDetails{"null",
40 [](std::string_view device_id) -> std::unique_ptr<Sink> {
41 return std::make_unique<NullSink>(device_id);
42 },
24 [] { return std::vector<std::string>{"null"}; }}, 43 [] { return std::vector<std::string>{"null"}; }},
25}; 44};
26 45
27const SinkDetails& GetSinkDetails(std::string_view sink_id) { 46const SinkDetails& GetSinkDetails(std::string_view sink_id) {
28 auto iter = 47 auto iter =
29 std::find_if(g_sink_details.begin(), g_sink_details.end(), 48 std::find_if(std::begin(sink_details), std::end(sink_details),
30 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); 49 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
31 50
32 if (sink_id == "auto" || iter == g_sink_details.end()) { 51 if (sink_id == "auto" || iter == std::end(sink_details)) {
33 if (sink_id != "auto") { 52 if (sink_id != "auto") {
34 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); 53 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id);
35 } 54 }
36 // Auto-select. 55 // Auto-select.
37 // g_sink_details is ordered in terms of desirability, with the best choice at the front. 56 // sink_details is ordered in terms of desirability, with the best choice at the front.
38 iter = g_sink_details.begin(); 57 iter = std::begin(sink_details);
39 } 58 }
40 59
41 return *iter; 60 return *iter;
42} 61}
62} // Anonymous namespace
63
64std::vector<const char*> GetSinkIDs() {
65 std::vector<const char*> sink_ids(std::size(sink_details));
66
67 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
68 [](const auto& sink) { return sink.id; });
69
70 return sink_ids;
71}
72
73std::vector<std::string> GetDeviceListForSink(std::string_view sink_id) {
74 return GetSinkDetails(sink_id).list_devices();
75}
76
77std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) {
78 return GetSinkDetails(sink_id).factory(device_id);
79}
43 80
44} // namespace AudioCore 81} // namespace AudioCore
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h
index 03534b187..bc8786270 100644
--- a/src/audio_core/sink_details.h
+++ b/src/audio_core/sink_details.h
@@ -4,34 +4,21 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <memory>
9#include <string> 7#include <string>
10#include <string_view> 8#include <string_view>
11#include <utility>
12#include <vector> 9#include <vector>
13 10
14namespace AudioCore { 11namespace AudioCore {
15 12
16class Sink; 13class Sink;
17 14
18struct SinkDetails { 15/// Retrieves the IDs for all available audio sinks.
19 using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>; 16std::vector<const char*> GetSinkIDs();
20 using ListDevicesFn = std::function<std::vector<std::string>()>;
21 17
22 SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_) 18/// Gets the list of devices for a particular sink identified by the given ID.
23 : id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {} 19std::vector<std::string> GetDeviceListForSink(std::string_view sink_id);
24 20
25 /// Name for this sink. 21/// Creates an audio sink identified by the given device ID.
26 const char* id; 22std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id);
27 /// A method to call to construct an instance of this type of sink.
28 FactoryFn factory;
29 /// A method to call to list available devices.
30 ListDevicesFn list_devices;
31};
32
33extern const std::vector<SinkDetails> g_sink_details;
34
35const SinkDetails& GetSinkDetails(std::string_view sink_id);
36 23
37} // namespace AudioCore 24} // namespace AudioCore
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 5753b871a..12f6d0114 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -13,7 +13,7 @@
13#include <vector> 13#include <vector>
14#ifdef _WIN32 14#ifdef _WIN32
15#include <share.h> // For _SH_DENYWR 15#include <share.h> // For _SH_DENYWR
16#include <windows.h> // For OutputDebugStringA 16#include <windows.h> // For OutputDebugStringW
17#else 17#else
18#define _SH_DENYWR 0 18#define _SH_DENYWR 0
19#endif 19#endif
@@ -148,7 +148,7 @@ void FileBackend::Write(const Entry& entry) {
148 148
149void DebuggerBackend::Write(const Entry& entry) { 149void DebuggerBackend::Write(const Entry& entry) {
150#ifdef _WIN32 150#ifdef _WIN32
151 ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str()); 151 ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
152#endif 152#endif
153} 153}
154 154
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 133122c5f..e7594db68 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -49,6 +49,22 @@ struct ThreadQueueList {
49 return T(); 49 return T();
50 } 50 }
51 51
52 template <typename UnaryPredicate>
53 T get_first_filter(UnaryPredicate filter) const {
54 const Queue* cur = first;
55 while (cur != nullptr) {
56 if (!cur->data.empty()) {
57 for (const auto& item : cur->data) {
58 if (filter(item))
59 return item;
60 }
61 }
62 cur = cur->next_nonempty;
63 }
64
65 return T();
66 }
67
52 T pop_first() { 68 T pop_first() {
53 Queue* cur = first; 69 Queue* cur = first;
54 while (cur != nullptr) { 70 while (cur != nullptr) {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 795fabc65..ce7851538 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -8,6 +8,7 @@
8#include <thread> 8#include <thread>
9#include <utility> 9#include <utility>
10 10
11#include "common/file_util.h"
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/string_util.h" 13#include "common/string_util.h"
13#include "core/arm/exclusive_monitor.h" 14#include "core/arm/exclusive_monitor.h"
@@ -40,7 +41,6 @@ namespace Core {
40 41
41/*static*/ System System::s_instance; 42/*static*/ System System::s_instance;
42 43
43namespace {
44FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 44FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
45 const std::string& path) { 45 const std::string& path) {
46 // To account for split 00+01+etc files. 46 // To account for split 00+01+etc files.
@@ -69,11 +69,13 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
69 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); 69 return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
70 } 70 }
71 71
72 if (FileUtil::IsDirectory(path))
73 return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
74
72 return vfs->OpenFile(path, FileSys::Mode::Read); 75 return vfs->OpenFile(path, FileSys::Mode::Read);
73} 76}
74} // Anonymous namespace
75
76struct System::Impl { 77struct System::Impl {
78
77 Cpu& CurrentCpuCore() { 79 Cpu& CurrentCpuCore() {
78 return cpu_core_manager.GetCurrentCore(); 80 return cpu_core_manager.GetCurrentCore();
79 } 81 }
diff --git a/src/core/core.h b/src/core/core.h
index be71bd437..71031dfcf 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -9,6 +9,7 @@
9#include <string> 9#include <string>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/file_sys/vfs_types.h"
12#include "core/hle/kernel/object.h" 13#include "core/hle/kernel/object.h"
13 14
14namespace Core::Frontend { 15namespace Core::Frontend {
@@ -55,6 +56,9 @@ class TelemetrySession;
55 56
56struct PerfStatsResults; 57struct PerfStatsResults;
57 58
59FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
60 const std::string& path);
61
58class System { 62class System {
59public: 63public:
60 System(const System&) = delete; 64 System(const System&) = delete;
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 12bb90ec8..6690aa575 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -29,8 +29,8 @@ struct Entry {
29 filename[copy_size] = '\0'; 29 filename[copy_size] = '\0';
30 } 30 }
31 31
32 char filename[0x300]; 32 char filename[0x301];
33 INSERT_PADDING_BYTES(4); 33 INSERT_PADDING_BYTES(3);
34 EntryType type; 34 EntryType type;
35 INSERT_PADDING_BYTES(3); 35 INSERT_PADDING_BYTES(3);
36 u64 file_size; 36 u64 file_size;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 6b14e08be..61706966e 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -56,6 +56,10 @@ PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
56 56
57PatchManager::~PatchManager() = default; 57PatchManager::~PatchManager() = default;
58 58
59u64 PatchManager::GetTitleID() const {
60 return title_id;
61}
62
59VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { 63VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
60 LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); 64 LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
61 65
@@ -73,11 +77,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
73 77
74 const auto installed = Service::FileSystem::GetUnionContents(); 78 const auto installed = Service::FileSystem::GetUnionContents();
75 79
80 const auto& disabled = Settings::values.disabled_addons[title_id];
81 const auto update_disabled =
82 std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
83
76 // Game Updates 84 // Game Updates
77 const auto update_tid = GetUpdateTitleID(title_id); 85 const auto update_tid = GetUpdateTitleID(title_id);
78 const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); 86 const auto update = installed.GetEntry(update_tid, ContentRecordType::Program);
79 87
80 if (update != nullptr && update->GetExeFS() != nullptr && 88 if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
81 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { 89 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
82 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 90 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
83 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 91 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
@@ -95,6 +103,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
95 std::vector<VirtualDir> layers; 103 std::vector<VirtualDir> layers;
96 layers.reserve(patch_dirs.size() + 1); 104 layers.reserve(patch_dirs.size() + 1);
97 for (const auto& subdir : patch_dirs) { 105 for (const auto& subdir : patch_dirs) {
106 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
107 continue;
108
98 auto exefs_dir = subdir->GetSubdirectory("exefs"); 109 auto exefs_dir = subdir->GetSubdirectory("exefs");
99 if (exefs_dir != nullptr) 110 if (exefs_dir != nullptr)
100 layers.push_back(std::move(exefs_dir)); 111 layers.push_back(std::move(exefs_dir));
@@ -111,11 +122,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
111 return exefs; 122 return exefs;
112} 123}
113 124
114static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, 125std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
115 const std::string& build_id) { 126 const std::string& build_id) const {
127 const auto& disabled = Settings::values.disabled_addons[title_id];
128
116 std::vector<VirtualFile> out; 129 std::vector<VirtualFile> out;
117 out.reserve(patch_dirs.size()); 130 out.reserve(patch_dirs.size());
118 for (const auto& subdir : patch_dirs) { 131 for (const auto& subdir : patch_dirs) {
132 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
133 continue;
134
119 auto exefs_dir = subdir->GetSubdirectory("exefs"); 135 auto exefs_dir = subdir->GetSubdirectory("exefs");
120 if (exefs_dir != nullptr) { 136 if (exefs_dir != nullptr) {
121 for (const auto& file : exefs_dir->GetFiles()) { 137 for (const auto& file : exefs_dir->GetFiles()) {
@@ -228,6 +244,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
228 return; 244 return;
229 } 245 }
230 246
247 const auto& disabled = Settings::values.disabled_addons[title_id];
231 auto patch_dirs = load_dir->GetSubdirectories(); 248 auto patch_dirs = load_dir->GetSubdirectories();
232 std::sort(patch_dirs.begin(), patch_dirs.end(), 249 std::sort(patch_dirs.begin(), patch_dirs.end(),
233 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 250 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -237,6 +254,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
237 layers.reserve(patch_dirs.size() + 1); 254 layers.reserve(patch_dirs.size() + 1);
238 layers_ext.reserve(patch_dirs.size() + 1); 255 layers_ext.reserve(patch_dirs.size() + 1);
239 for (const auto& subdir : patch_dirs) { 256 for (const auto& subdir : patch_dirs) {
257 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
258 continue;
259
240 auto romfs_dir = subdir->GetSubdirectory("romfs"); 260 auto romfs_dir = subdir->GetSubdirectory("romfs");
241 if (romfs_dir != nullptr) 261 if (romfs_dir != nullptr)
242 layers.push_back(std::move(romfs_dir)); 262 layers.push_back(std::move(romfs_dir));
@@ -266,13 +286,12 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
266VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, 286VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
267 VirtualFile update_raw) const { 287 VirtualFile update_raw) const {
268 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", 288 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
269 title_id, static_cast<u8>(type)) 289 title_id, static_cast<u8>(type));
270 .c_str();
271 290
272 if (type == ContentRecordType::Program || type == ContentRecordType::Data) 291 if (type == ContentRecordType::Program || type == ContentRecordType::Data)
273 LOG_INFO(Loader, log_string); 292 LOG_INFO(Loader, "{}", log_string);
274 else 293 else
275 LOG_DEBUG(Loader, log_string); 294 LOG_DEBUG(Loader, "{}", log_string);
276 295
277 if (romfs == nullptr) 296 if (romfs == nullptr)
278 return romfs; 297 return romfs;
@@ -282,7 +301,12 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
282 // Game Updates 301 // Game Updates
283 const auto update_tid = GetUpdateTitleID(title_id); 302 const auto update_tid = GetUpdateTitleID(title_id);
284 const auto update = installed.GetEntryRaw(update_tid, type); 303 const auto update = installed.GetEntryRaw(update_tid, type);
285 if (update != nullptr) { 304
305 const auto& disabled = Settings::values.disabled_addons[title_id];
306 const auto update_disabled =
307 std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
308
309 if (!update_disabled && update != nullptr) {
286 const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); 310 const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
287 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 311 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
288 new_nca->GetRomFS() != nullptr) { 312 new_nca->GetRomFS() != nullptr) {
@@ -290,7 +314,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
290 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); 314 FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
291 romfs = new_nca->GetRomFS(); 315 romfs = new_nca->GetRomFS();
292 } 316 }
293 } else if (update_raw != nullptr) { 317 } else if (!update_disabled && update_raw != nullptr) {
294 const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); 318 const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
295 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 319 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
296 new_nca->GetRomFS() != nullptr) { 320 new_nca->GetRomFS() != nullptr) {
@@ -320,25 +344,30 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
320 VirtualFile update_raw) const { 344 VirtualFile update_raw) const {
321 std::map<std::string, std::string, std::less<>> out; 345 std::map<std::string, std::string, std::less<>> out;
322 const auto installed = Service::FileSystem::GetUnionContents(); 346 const auto installed = Service::FileSystem::GetUnionContents();
347 const auto& disabled = Settings::values.disabled_addons[title_id];
323 348
324 // Game Updates 349 // Game Updates
325 const auto update_tid = GetUpdateTitleID(title_id); 350 const auto update_tid = GetUpdateTitleID(title_id);
326 PatchManager update{update_tid}; 351 PatchManager update{update_tid};
327 auto [nacp, discard_icon_file] = update.GetControlMetadata(); 352 auto [nacp, discard_icon_file] = update.GetControlMetadata();
328 353
354 const auto update_disabled =
355 std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
356 const auto update_label = update_disabled ? "[D] Update" : "Update";
357
329 if (nacp != nullptr) { 358 if (nacp != nullptr) {
330 out.insert_or_assign("Update", nacp->GetVersionString()); 359 out.insert_or_assign(update_label, nacp->GetVersionString());
331 } else { 360 } else {
332 if (installed.HasEntry(update_tid, ContentRecordType::Program)) { 361 if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
333 const auto meta_ver = installed.GetEntryVersion(update_tid); 362 const auto meta_ver = installed.GetEntryVersion(update_tid);
334 if (meta_ver.value_or(0) == 0) { 363 if (meta_ver.value_or(0) == 0) {
335 out.insert_or_assign("Update", ""); 364 out.insert_or_assign(update_label, "");
336 } else { 365 } else {
337 out.insert_or_assign( 366 out.insert_or_assign(
338 "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); 367 update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
339 } 368 }
340 } else if (update_raw != nullptr) { 369 } else if (update_raw != nullptr) {
341 out.insert_or_assign("Update", "PACKED"); 370 out.insert_or_assign(update_label, "PACKED");
342 } 371 }
343 } 372 }
344 373
@@ -378,7 +407,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
378 if (types.empty()) 407 if (types.empty())
379 continue; 408 continue;
380 409
381 out.insert_or_assign(mod->GetName(), types); 410 const auto mod_disabled =
411 std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
412 out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types);
382 } 413 }
383 } 414 }
384 415
@@ -401,7 +432,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
401 432
402 list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); 433 list += fmt::format("{}", dlc_match.back().title_id & 0x7FF);
403 434
404 out.insert_or_assign("DLC", std::move(list)); 435 const auto dlc_disabled =
436 std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
437 out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list));
405 } 438 }
406 439
407 return out; 440 return out;
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 7d168837f..b8a1652fd 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -30,6 +30,8 @@ public:
30 explicit PatchManager(u64 title_id); 30 explicit PatchManager(u64 title_id);
31 ~PatchManager(); 31 ~PatchManager();
32 32
33 u64 GetTitleID() const;
34
33 // Currently tracked ExeFS patches: 35 // Currently tracked ExeFS patches:
34 // - Game Updates 36 // - Game Updates
35 VirtualDir PatchExeFS(VirtualDir exefs) const; 37 VirtualDir PatchExeFS(VirtualDir exefs) const;
@@ -63,6 +65,9 @@ public:
63 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; 65 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
64 66
65private: 67private:
68 std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
69 const std::string& build_id) const;
70
66 u64 title_id; 71 u64 title_id;
67}; 72};
68 73
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 5434f2149..bd50fedc7 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -18,7 +18,11 @@ std::string SaveDataDescriptor::DebugInfo() const {
18 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); 18 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
19} 19}
20 20
21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} 21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
22 // Delete all temporary storages
23 // On hardware, it is expected that temporary storage be empty at first use.
24 dir->DeleteSubdirectoryRecursive("temp");
25}
22 26
23SaveDataFactory::~SaveDataFactory() = default; 27SaveDataFactory::~SaveDataFactory() = default;
24 28
@@ -120,6 +124,8 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
120 case SaveDataType::TemporaryStorage: 124 case SaveDataType::TemporaryStorage:
121 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 125 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
122 title_id); 126 title_id);
127 case SaveDataType::CacheStorage:
128 return fmt::format("{}save/cache/{:016X}", out, title_id);
123 default: 129 default:
124 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 130 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
125 } 131 }
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 2a0088040..bd4919610 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -17,8 +17,10 @@ namespace FileSys {
17enum class SaveDataSpaceId : u8 { 17enum class SaveDataSpaceId : u8 {
18 NandSystem = 0, 18 NandSystem = 0,
19 NandUser = 1, 19 NandUser = 1,
20 SdCard = 2, 20 SdCardSystem = 2,
21 TemporaryStorage = 3, 21 TemporaryStorage = 3,
22 SdCardUser = 4,
23 ProperSystem = 100,
22}; 24};
23 25
24enum class SaveDataType : u8 { 26enum class SaveDataType : u8 {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 211bf6686..5356a4a3f 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -66,6 +66,7 @@ ResultCode Process::ClearSignalState() {
66 66
67void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { 67void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
68 program_id = metadata.GetTitleID(); 68 program_id = metadata.GetTitleID();
69 ideal_processor = metadata.GetMainThreadCore();
69 is_64bit_process = metadata.Is64BitProgram(); 70 is_64bit_process = metadata.Is64BitProgram();
70 vm_manager.Reset(metadata.GetAddressSpaceType()); 71 vm_manager.Reset(metadata.GetAddressSpaceType());
71} 72}
@@ -149,7 +150,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
149 vm_manager 150 vm_manager
150 .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, 151 .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
151 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, 152 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
152 MemoryState::Mapped) 153 MemoryState::Stack)
153 .Unwrap(); 154 .Unwrap();
154 155
155 vm_manager.LogLayout(); 156 vm_manager.LogLayout();
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index bcb9ac4b8..459eedfa6 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -262,8 +262,7 @@ public:
262 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 262 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
263 ResultCode HeapFree(VAddr target, u32 size); 263 ResultCode HeapFree(VAddr target, u32 size);
264 264
265 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, 265 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
266 MemoryState state = MemoryState::Mapped);
267 266
268 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 267 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
269 268
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5a5f4cef1..df4d6cf0a 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/core_cpu.h"
12#include "core/core_timing.h" 13#include "core/core_timing.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -179,4 +180,69 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
179 ready_queue.prepare(priority); 180 ready_queue.prepare(priority);
180} 181}
181 182
183Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
184 std::lock_guard<std::mutex> lock(scheduler_mutex);
185
186 const u32 mask = 1U << core;
187 return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
188 return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
189 });
190}
191
192void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
193 ASSERT(thread != nullptr);
194 // Avoid yielding if the thread isn't even running.
195 ASSERT(thread->GetStatus() == ThreadStatus::Running);
196
197 // Sanity check that the priority is valid
198 ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
199
200 // Yield this thread -- sleep for zero time and force reschedule to different thread
201 WaitCurrentThread_Sleep();
202 GetCurrentThread()->WakeAfterDelay(0);
203}
204
205void Scheduler::YieldWithLoadBalancing(Thread* thread) {
206 ASSERT(thread != nullptr);
207 const auto priority = thread->GetPriority();
208 const auto core = static_cast<u32>(thread->GetProcessorID());
209
210 // Avoid yielding if the thread isn't even running.
211 ASSERT(thread->GetStatus() == ThreadStatus::Running);
212
213 // Sanity check that the priority is valid
214 ASSERT(priority < THREADPRIO_COUNT);
215
216 // Sleep for zero time to be able to force reschedule to different thread
217 WaitCurrentThread_Sleep();
218 GetCurrentThread()->WakeAfterDelay(0);
219
220 Thread* suggested_thread = nullptr;
221
222 // Search through all of the cpu cores (except this one) for a suggested thread.
223 // Take the first non-nullptr one
224 for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
225 const auto res =
226 Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
227 core, priority);
228
229 // If scheduler provides a suggested thread
230 if (res != nullptr) {
231 // And its better than the current suggested thread (or is the first valid one)
232 if (suggested_thread == nullptr ||
233 suggested_thread->GetPriority() > res->GetPriority()) {
234 suggested_thread = res;
235 }
236 }
237 }
238
239 // If a suggested thread was found, queue that for this core
240 if (suggested_thread != nullptr)
241 suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
242}
243
244void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {
245 UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
246}
247
182} // namespace Kernel 248} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index c63032b7d..97ced4dfc 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -51,6 +51,75 @@ public:
51 /// Sets the priority of a thread in the scheduler 51 /// Sets the priority of a thread in the scheduler
52 void SetThreadPriority(Thread* thread, u32 priority); 52 void SetThreadPriority(Thread* thread, u32 priority);
53 53
54 /// Gets the next suggested thread for load balancing
55 Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
56
57 /**
58 * YieldWithoutLoadBalancing -- analogous to normal yield on a system
59 * Moves the thread to the end of the ready queue for its priority, and then reschedules the
60 * system to the new head of the queue.
61 *
62 * Example (Single Core -- but can be extrapolated to multi):
63 * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->)
64 * Currently Running: ThreadR
65 *
66 * ThreadR calls YieldWithoutLoadBalancing
67 *
68 * ThreadR is moved to the end of ready_queue[prio=0]:
69 * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->)
70 * Currently Running: Nothing
71 *
72 * System is rescheduled (ThreadA is popped off of queue):
73 * ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->)
74 * Currently Running: ThreadA
75 *
76 * If the queue is empty at time of call, no yielding occurs. This does not cross between cores
77 * or priorities at all.
78 */
79 void YieldWithoutLoadBalancing(Thread* thread);
80
81 /**
82 * YieldWithLoadBalancing -- yield but with better selection of the new running thread
83 * Moves the current thread to the end of the ready queue for its priority, then selects a
84 * 'suggested thread' (a thread on a different core that could run on this core) from the
85 * scheduler, changes its core, and reschedules the current core to that thread.
86 *
87 * Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were
88 * single core):
89 * ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant
90 * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
91 * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
92 *
93 * ThreadQ calls YieldWithLoadBalancing
94 *
95 * ThreadQ is moved to the end of ready_queue[core=0][prio=0]:
96 * ready_queue[core=0][prio=0]: ThreadA, ThreadB
97 * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
98 * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
99 *
100 * A list of suggested threads for each core is compiled
101 * Suggested Threads: {ThreadC on Core 1}
102 * If this were quad core (as the switch is), there could be between 0 and 3 threads in this
103 * list. If there are more than one, the thread is selected by highest prio.
104 *
105 * ThreadC is core changed to Core 0:
106 * ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ
107 * ready_queue[core=1][prio=0]: ThreadD
108 * Currently Running: None on Core 0 || ThreadP on Core 1
109 *
110 * System is rescheduled (ThreadC is popped off of queue):
111 * ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ
112 * ready_queue[core=1][prio=0]: ThreadD
113 * Currently Running: ThreadC on Core 0 || ThreadP on Core 1
114 *
115 * If no suggested threads can be found this will behave just as normal yield. If there are
116 * multiple candidates for the suggested thread on a core, the highest prio is taken.
117 */
118 void YieldWithLoadBalancing(Thread* thread);
119
120 /// Currently unknown -- asserts as unimplemented on call
121 void YieldAndWaitForLoadBalancing(Thread* thread);
122
54 /// Returns a list of all threads managed by the scheduler 123 /// Returns a list of all threads managed by the scheduler
55 const std::vector<SharedPtr<Thread>>& GetThreadList() const { 124 const std::vector<SharedPtr<Thread>>& GetThreadList() const {
56 return thread_list; 125 return thread_list;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 0494581f5..22d0c1dd5 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -17,13 +17,13 @@ namespace Kernel {
17SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {} 17SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
18SharedMemory::~SharedMemory() = default; 18SharedMemory::~SharedMemory() = default;
19 19
20SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Process> owner_process, 20SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, u64 size,
21 u64 size, MemoryPermission permissions, 21 MemoryPermission permissions,
22 MemoryPermission other_permissions, VAddr address, 22 MemoryPermission other_permissions, VAddr address,
23 MemoryRegion region, std::string name) { 23 MemoryRegion region, std::string name) {
24 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); 24 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
25 25
26 shared_memory->owner_process = std::move(owner_process); 26 shared_memory->owner_process = owner_process;
27 shared_memory->name = std::move(name); 27 shared_memory->name = std::move(name);
28 shared_memory->size = size; 28 shared_memory->size = size;
29 shared_memory->permissions = permissions; 29 shared_memory->permissions = permissions;
@@ -39,15 +39,15 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
39 shared_memory->backing_block.get()); 39 shared_memory->backing_block.get());
40 } 40 }
41 } else { 41 } else {
42 auto& vm_manager = shared_memory->owner_process->VMManager(); 42 const auto& vm_manager = shared_memory->owner_process->VMManager();
43 43
44 // The memory is already available and mapped in the owner process. 44 // The memory is already available and mapped in the owner process.
45 auto vma = vm_manager.FindVMA(address); 45 const auto vma = vm_manager.FindVMA(address);
46 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); 46 ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address");
47 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); 47 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
48 48
49 // The returned VMA might be a bigger one encompassing the desired address. 49 // The returned VMA might be a bigger one encompassing the desired address.
50 auto vma_offset = address - vma->first; 50 const auto vma_offset = address - vma->first;
51 ASSERT_MSG(vma_offset + size <= vma->second.size, 51 ASSERT_MSG(vma_offset + size <= vma->second.size,
52 "Shared memory exceeds bounds of mapped block"); 52 "Shared memory exceeds bounds of mapped block");
53 53
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 0b48db699..dab2a6bea 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -45,8 +45,8 @@ public:
45 * linear heap. 45 * linear heap.
46 * @param name Optional object name, used for debugging purposes. 46 * @param name Optional object name, used for debugging purposes.
47 */ 47 */
48 static SharedPtr<SharedMemory> Create(KernelCore& kernel, SharedPtr<Process> owner_process, 48 static SharedPtr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, u64 size,
49 u64 size, MemoryPermission permissions, 49 MemoryPermission permissions,
50 MemoryPermission other_permissions, VAddr address = 0, 50 MemoryPermission other_permissions, VAddr address = 0,
51 MemoryRegion region = MemoryRegion::BASE, 51 MemoryRegion region = MemoryRegion::BASE,
52 std::string name = "Unknown"); 52 std::string name = "Unknown");
@@ -139,7 +139,7 @@ private:
139 /// Permission restrictions applied to other processes mapping the block. 139 /// Permission restrictions applied to other processes mapping the block.
140 MemoryPermission other_permissions{}; 140 MemoryPermission other_permissions{};
141 /// Process that created this shared memory block. 141 /// Process that created this shared memory block.
142 SharedPtr<Process> owner_process; 142 Process* owner_process;
143 /// Address of shared memory block in the owner process if specified. 143 /// Address of shared memory block in the owner process if specified.
144 VAddr base_address = 0; 144 VAddr base_address = 0;
145 /// Name of shared memory object. 145 /// Name of shared memory object.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 84df2040e..348a22904 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -35,6 +35,7 @@
35#include "core/hle/lock.h" 35#include "core/hle/lock.h"
36#include "core/hle/result.h" 36#include "core/hle/result.h"
37#include "core/hle/service/service.h" 37#include "core/hle/service/service.h"
38#include "core/memory.h"
38 39
39namespace Kernel { 40namespace Kernel {
40namespace { 41namespace {
@@ -239,7 +240,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
239 } 240 }
240 241
241 const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); 242 const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
242 if (iter == vm_manager.vma_map.end()) { 243 if (!vm_manager.IsValidHandle(iter)) {
243 LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); 244 LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr);
244 return ERR_INVALID_ADDRESS_STATE; 245 return ERR_INVALID_ADDRESS_STATE;
245 } 246 }
@@ -273,7 +274,7 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
273 return result; 274 return result;
274 } 275 }
275 276
276 return current_process->MirrorMemory(dst_addr, src_addr, size); 277 return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
277} 278}
278 279
279/// Unmaps a region that was previously mapped with svcMapMemory 280/// Unmaps a region that was previously mapped with svcMapMemory
@@ -1066,10 +1067,9 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
1066 return shared_memory->Unmap(*current_process, addr); 1067 return shared_memory->Unmap(*current_process, addr);
1067} 1068}
1068 1069
1069/// Query process memory 1070static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
1070static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 1071 Handle process_handle, VAddr address) {
1071 Handle process_handle, u64 addr) { 1072 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1072 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr);
1073 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1073 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1074 SharedPtr<Process> process = handle_table.Get<Process>(process_handle); 1074 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
1075 if (!process) { 1075 if (!process) {
@@ -1077,26 +1077,34 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
1077 process_handle); 1077 process_handle);
1078 return ERR_INVALID_HANDLE; 1078 return ERR_INVALID_HANDLE;
1079 } 1079 }
1080 auto vma = process->VMManager().FindVMA(addr); 1080
1081 memory_info->attributes = 0; 1081 const auto& vm_manager = process->VMManager();
1082 if (vma == process->VMManager().vma_map.end()) { 1082 const MemoryInfo memory_info = vm_manager.QueryMemory(address);
1083 memory_info->base_address = 0; 1083
1084 memory_info->permission = static_cast<u32>(VMAPermission::None); 1084 Memory::Write64(memory_info_address, memory_info.base_address);
1085 memory_info->size = 0; 1085 Memory::Write64(memory_info_address + 8, memory_info.size);
1086 memory_info->type = static_cast<u32>(MemoryState::Unmapped); 1086 Memory::Write32(memory_info_address + 16, memory_info.state);
1087 } else { 1087 Memory::Write32(memory_info_address + 20, memory_info.attributes);
1088 memory_info->base_address = vma->second.base; 1088 Memory::Write32(memory_info_address + 24, memory_info.permission);
1089 memory_info->permission = static_cast<u32>(vma->second.permissions); 1089 Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count);
1090 memory_info->size = vma->second.size; 1090 Memory::Write32(memory_info_address + 28, memory_info.device_ref_count);
1091 memory_info->type = static_cast<u32>(vma->second.meminfo_state); 1091 Memory::Write32(memory_info_address + 36, 0);
1092 } 1092
1093 // Page info appears to be currently unused by the kernel and is always set to zero.
1094 Memory::Write32(page_info_address, 0);
1095
1093 return RESULT_SUCCESS; 1096 return RESULT_SUCCESS;
1094} 1097}
1095 1098
1096/// Query memory 1099static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address,
1097static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { 1100 VAddr query_address) {
1098 LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); 1101 LOG_TRACE(Kernel_SVC,
1099 return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); 1102 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
1103 "query_address=0x{:016X}",
1104 memory_info_address, page_info_address, query_address);
1105
1106 return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess,
1107 query_address);
1100} 1108}
1101 1109
1102/// Exits the current process 1110/// Exits the current process
@@ -1200,18 +1208,38 @@ static void ExitThread() {
1200static void SleepThread(s64 nanoseconds) { 1208static void SleepThread(s64 nanoseconds) {
1201 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 1209 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1202 1210
1203 // Don't attempt to yield execution if there are no available threads to run, 1211 enum class SleepType : s64 {
1204 // this way we avoid a useless reschedule to the idle thread. 1212 YieldWithoutLoadBalancing = 0,
1205 if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads()) 1213 YieldWithLoadBalancing = -1,
1206 return; 1214 YieldAndWaitForLoadBalancing = -2,
1215 };
1207 1216
1208 // Sleep current thread and check for next thread to schedule 1217 if (nanoseconds <= 0) {
1209 WaitCurrentThread_Sleep(); 1218 auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
1219 switch (static_cast<SleepType>(nanoseconds)) {
1220 case SleepType::YieldWithoutLoadBalancing:
1221 scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
1222 break;
1223 case SleepType::YieldWithLoadBalancing:
1224 scheduler.YieldWithLoadBalancing(GetCurrentThread());
1225 break;
1226 case SleepType::YieldAndWaitForLoadBalancing:
1227 scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
1228 break;
1229 default:
1230 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1231 }
1232 } else {
1233 // Sleep current thread and check for next thread to schedule
1234 WaitCurrentThread_Sleep();
1210 1235
1211 // Create an event to wake the thread up after the specified nanosecond delay has passed 1236 // Create an event to wake the thread up after the specified nanosecond delay has passed
1212 GetCurrentThread()->WakeAfterDelay(nanoseconds); 1237 GetCurrentThread()->WakeAfterDelay(nanoseconds);
1238 }
1213 1239
1214 Core::System::GetInstance().PrepareReschedule(); 1240 // Reschedule all CPU cores
1241 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
1242 Core::System::GetInstance().CpuCore(i).PrepareReschedule();
1215} 1243}
1216 1244
1217/// Wait process wide key atomic 1245/// Wait process wide key atomic
@@ -1483,9 +1511,9 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1483 } 1511 }
1484 1512
1485 auto& kernel = Core::System::GetInstance().Kernel(); 1513 auto& kernel = Core::System::GetInstance().Kernel();
1486 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1514 auto process = kernel.CurrentProcess();
1487 const auto shared_mem_handle = SharedMemory::Create( 1515 auto& handle_table = process->GetHandleTable();
1488 kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr); 1516 const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
1489 1517
1490 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); 1518 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
1491 return RESULT_SUCCESS; 1519 return RESULT_SUCCESS;
@@ -1595,10 +1623,9 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1595 } 1623 }
1596 1624
1597 auto& kernel = Core::System::GetInstance().Kernel(); 1625 auto& kernel = Core::System::GetInstance().Kernel();
1598 auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1626 auto process = kernel.CurrentProcess();
1599 auto shared_mem_handle = 1627 auto& handle_table = process->GetHandleTable();
1600 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1628 auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
1601 local_perms, remote_perms);
1602 1629
1603 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); 1630 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
1604 return RESULT_SUCCESS; 1631 return RESULT_SUCCESS;
@@ -1904,7 +1931,7 @@ static const FunctionDef SVC_Table[] = {
1904 {0x73, nullptr, "SetProcessMemoryPermission"}, 1931 {0x73, nullptr, "SetProcessMemoryPermission"},
1905 {0x74, nullptr, "MapProcessMemory"}, 1932 {0x74, nullptr, "MapProcessMemory"},
1906 {0x75, nullptr, "UnmapProcessMemory"}, 1933 {0x75, nullptr, "UnmapProcessMemory"},
1907 {0x76, nullptr, "QueryProcessMemory"}, 1934 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
1908 {0x77, nullptr, "MapProcessCodeMemory"}, 1935 {0x77, nullptr, "MapProcessCodeMemory"},
1909 {0x78, nullptr, "UnmapProcessCodeMemory"}, 1936 {0x78, nullptr, "UnmapProcessCodeMemory"},
1910 {0x79, nullptr, "CreateProcess"}, 1937 {0x79, nullptr, "CreateProcess"},
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index b06aac4ec..c37ae0f98 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -8,22 +8,6 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11struct MemoryInfo {
12 u64 base_address;
13 u64 size;
14 u32 type;
15 u32 attributes;
16 u32 permission;
17 u32 device_refcount;
18 u32 ipc_refcount;
19 INSERT_PADDING_WORDS(1);
20};
21static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size.");
22
23struct PageInfo {
24 u64 flags;
25};
26
27void CallSVC(u32 immediate); 11void CallSVC(u32 immediate);
28 12
29} // namespace Kernel 13} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 24aef46c9..2f758b959 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -7,9 +7,7 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/svc.h"
11#include "core/hle/result.h" 10#include "core/hle/result.h"
12#include "core/memory.h"
13 11
14namespace Kernel { 12namespace Kernel {
15 13
@@ -129,7 +127,12 @@ void SvcWrap() {
129template <ResultCode func(u64, u64, u32, u32)> 127template <ResultCode func(u64, u64, u32, u32)>
130void SvcWrap() { 128void SvcWrap() {
131 FuncReturn( 129 FuncReturn(
132 func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw); 130 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
131}
132
133template <ResultCode func(u64, u64, u32, u64)>
134void SvcWrap() {
135 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
133} 136}
134 137
135template <ResultCode func(u32, u64, u32)> 138template <ResultCode func(u32, u64, u32)>
@@ -191,21 +194,6 @@ void SvcWrap() {
191 FuncReturn(retval); 194 FuncReturn(retval);
192} 195}
193 196
194template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
195void SvcWrap() {
196 MemoryInfo memory_info = {};
197 PageInfo page_info = {};
198 u32 retval = func(&memory_info, &page_info, Param(2)).raw;
199
200 Memory::Write64(Param(0), memory_info.base_address);
201 Memory::Write64(Param(0) + 8, memory_info.size);
202 Memory::Write32(Param(0) + 16, memory_info.type);
203 Memory::Write32(Param(0) + 20, memory_info.attributes);
204 Memory::Write32(Param(0) + 24, memory_info.permission);
205
206 FuncReturn(retval);
207}
208
209template <ResultCode func(u32*, u64, u64, u32)> 197template <ResultCode func(u32*, u64, u64, u32)>
210void SvcWrap() { 198void SvcWrap() {
211 u32 param_1 = 0; 199 u32 param_1 = 0;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index d384d50db..77aec099a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -26,6 +26,7 @@ enum ThreadPriority : u32 {
26 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 26 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
27 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps 27 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
28 THREADPRIO_LOWEST = 63, ///< Lowest thread priority 28 THREADPRIO_LOWEST = 63, ///< Lowest thread priority
29 THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
29}; 30};
30 31
31enum ThreadProcessorId : s32 { 32enum ThreadProcessorId : s32 {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 100f8f6bf..d3b55a51e 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -25,14 +25,14 @@ static const char* GetMemoryStateName(MemoryState state) {
25 "CodeMutable", "Heap", 25 "CodeMutable", "Heap",
26 "Shared", "Unknown1", 26 "Shared", "Unknown1",
27 "ModuleCodeStatic", "ModuleCodeMutable", 27 "ModuleCodeStatic", "ModuleCodeMutable",
28 "IpcBuffer0", "Mapped", 28 "IpcBuffer0", "Stack",
29 "ThreadLocal", "TransferMemoryIsolated", 29 "ThreadLocal", "TransferMemoryIsolated",
30 "TransferMemory", "ProcessMemory", 30 "TransferMemory", "ProcessMemory",
31 "Unknown2", "IpcBuffer1", 31 "Inaccessible", "IpcBuffer1",
32 "IpcBuffer3", "KernelStack", 32 "IpcBuffer3", "KernelStack",
33 }; 33 };
34 34
35 return names[static_cast<int>(state)]; 35 return names[ToSvcMemoryState(state)];
36} 36}
37 37
38bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { 38bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
@@ -87,6 +87,10 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
87 } 87 }
88} 88}
89 89
90bool VMManager::IsValidHandle(VMAHandle handle) const {
91 return handle != vma_map.cend();
92}
93
90ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, 94ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
91 std::shared_ptr<std::vector<u8>> block, 95 std::shared_ptr<std::vector<u8>> block,
92 std::size_t offset, u64 size, 96 std::size_t offset, u64 size,
@@ -298,6 +302,25 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) {
298 return RESULT_SUCCESS; 302 return RESULT_SUCCESS;
299} 303}
300 304
305MemoryInfo VMManager::QueryMemory(VAddr address) const {
306 const auto vma = FindVMA(address);
307 MemoryInfo memory_info{};
308
309 if (IsValidHandle(vma)) {
310 memory_info.base_address = vma->second.base;
311 memory_info.permission = static_cast<u32>(vma->second.permissions);
312 memory_info.size = vma->second.size;
313 memory_info.state = ToSvcMemoryState(vma->second.meminfo_state);
314 } else {
315 memory_info.base_address = address_space_end;
316 memory_info.permission = static_cast<u32>(VMAPermission::None);
317 memory_info.size = 0 - address_space_end;
318 memory_info.state = static_cast<u32>(MemoryState::Inaccessible);
319 }
320
321 return memory_info;
322}
323
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { 324ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
302 const auto vma = FindVMA(src_addr); 325 const auto vma = FindVMA(src_addr);
303 326
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index d522404fe..10bacac3e 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -43,26 +43,129 @@ enum class VMAPermission : u8 {
43 ReadWriteExecute = Read | Write | Execute, 43 ReadWriteExecute = Read | Write | Execute,
44}; 44};
45 45
46/// Set of values returned in MemoryInfo.state by svcQueryMemory. 46// clang-format off
47/// Represents memory states and any relevant flags, as used by the kernel.
48/// svcQueryMemory interprets these by masking away all but the first eight
49/// bits when storing memory state into a MemoryInfo instance.
47enum class MemoryState : u32 { 50enum class MemoryState : u32 {
48 Unmapped = 0x0, 51 Mask = 0xFF,
49 Io = 0x1, 52 FlagProtect = 1U << 8,
50 Normal = 0x2, 53 FlagDebug = 1U << 9,
51 CodeStatic = 0x3, 54 FlagIPC0 = 1U << 10,
52 CodeMutable = 0x4, 55 FlagIPC3 = 1U << 11,
53 Heap = 0x5, 56 FlagIPC1 = 1U << 12,
54 Shared = 0x6, 57 FlagMapped = 1U << 13,
55 ModuleCodeStatic = 0x8, 58 FlagCode = 1U << 14,
56 ModuleCodeMutable = 0x9, 59 FlagAlias = 1U << 15,
57 IpcBuffer0 = 0xA, 60 FlagModule = 1U << 16,
58 Mapped = 0xB, 61 FlagTransfer = 1U << 17,
59 ThreadLocal = 0xC, 62 FlagQueryPhysicalAddressAllowed = 1U << 18,
60 TransferMemoryIsolated = 0xD, 63 FlagSharedDevice = 1U << 19,
61 TransferMemory = 0xE, 64 FlagSharedDeviceAligned = 1U << 20,
62 ProcessMemory = 0xF, 65 FlagIPCBuffer = 1U << 21,
63 IpcBuffer1 = 0x11, 66 FlagMemoryPoolAllocated = 1U << 22,
64 IpcBuffer3 = 0x12, 67 FlagMapProcess = 1U << 23,
65 KernelStack = 0x13, 68 FlagUncached = 1U << 24,
69 FlagCodeMemory = 1U << 25,
70
71 // Convenience flag sets to reduce repetition
72 IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
73
74 CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed |
75 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
76
77 DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer |
78 FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned |
79 FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached,
80
81 Unmapped = 0x00,
82 Io = 0x01 | FlagMapped,
83 Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
84 CodeStatic = 0x03 | CodeFlags | FlagMapProcess,
85 CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory,
86 Heap = 0x05 | DataFlags | FlagCodeMemory,
87 Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
88 ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
89 ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
90
91 IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
92 IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
93
94 Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed |
95 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
96
97 ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated,
98
99 TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed |
100 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated |
101 FlagUncached,
102
103 TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
104 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
105
106 ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated,
107
108 // Used to signify an inaccessible or invalid memory region with memory queries
109 Inaccessible = 0x10,
110
111 IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
112 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
113
114 IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed |
115 FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
116
117 KernelStack = 0x13 | FlagMapped,
118};
119// clang-format on
120
121constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) {
122 return static_cast<MemoryState>(u32(lhs) | u32(rhs));
123}
124
125constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) {
126 return static_cast<MemoryState>(u32(lhs) & u32(rhs));
127}
128
129constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) {
130 return static_cast<MemoryState>(u32(lhs) ^ u32(rhs));
131}
132
133constexpr MemoryState operator~(MemoryState lhs) {
134 return static_cast<MemoryState>(~u32(lhs));
135}
136
137constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) {
138 lhs = lhs | rhs;
139 return lhs;
140}
141
142constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) {
143 lhs = lhs & rhs;
144 return lhs;
145}
146
147constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) {
148 lhs = lhs ^ rhs;
149 return lhs;
150}
151
152constexpr u32 ToSvcMemoryState(MemoryState state) {
153 return static_cast<u32>(state & MemoryState::Mask);
154}
155
156struct MemoryInfo {
157 u64 base_address;
158 u64 size;
159 u32 state;
160 u32 attributes;
161 u32 permission;
162 u32 ipc_ref_count;
163 u32 device_ref_count;
164};
165static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size.");
166
167struct PageInfo {
168 u32 flags;
66}; 169};
67 170
68/** 171/**
@@ -113,16 +216,10 @@ struct VirtualMemoryArea {
113 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ 216 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
114 */ 217 */
115class VMManager final { 218class VMManager final {
219 using VMAMap = std::map<VAddr, VirtualMemoryArea>;
220
116public: 221public:
117 /** 222 using VMAHandle = VMAMap::const_iterator;
118 * A map covering the entirety of the managed address space, keyed by the `base` field of each
119 * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
120 * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
121 * merged when possible so that no two similar and adjacent regions exist that have not been
122 * merged.
123 */
124 std::map<VAddr, VirtualMemoryArea> vma_map;
125 using VMAHandle = decltype(vma_map)::const_iterator;
126 223
127 VMManager(); 224 VMManager();
128 ~VMManager(); 225 ~VMManager();
@@ -133,6 +230,9 @@ public:
133 /// Finds the VMA in which the given address is included in, or `vma_map.end()`. 230 /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
134 VMAHandle FindVMA(VAddr target) const; 231 VMAHandle FindVMA(VAddr target) const;
135 232
233 /// Indicates whether or not the given handle is within the VMA map.
234 bool IsValidHandle(VMAHandle handle) const;
235
136 // TODO(yuriks): Should these functions actually return the handle? 236 // TODO(yuriks): Should these functions actually return the handle?
137 237
138 /** 238 /**
@@ -189,8 +289,15 @@ public:
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 289 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size); 290 ResultCode HeapFree(VAddr target, u64 size);
191 291
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, 292 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
193 MemoryState state = MemoryState::Mapped); 293
294 /// Queries the memory manager for information about the given address.
295 ///
296 /// @param address The address to query the memory manager about for information.
297 ///
298 /// @return A MemoryInfo instance containing information about the given address.
299 ///
300 MemoryInfo QueryMemory(VAddr address) const;
194 301
195 /** 302 /**
196 * Scans all VMAs and updates the page table range of any that use the given vector as backing 303 * Scans all VMAs and updates the page table range of any that use the given vector as backing
@@ -281,7 +388,7 @@ public:
281 Memory::PageTable page_table; 388 Memory::PageTable page_table;
282 389
283private: 390private:
284 using VMAIter = decltype(vma_map)::iterator; 391 using VMAIter = VMAMap::iterator;
285 392
286 /// Converts a VMAHandle to a mutable VMAIter. 393 /// Converts a VMAHandle to a mutable VMAIter.
287 VMAIter StripIterConstness(const VMAHandle& iter); 394 VMAIter StripIterConstness(const VMAHandle& iter);
@@ -328,6 +435,15 @@ private:
328 /// Clears out the page table 435 /// Clears out the page table
329 void ClearPageTable(); 436 void ClearPageTable();
330 437
438 /**
439 * A map covering the entirety of the managed address space, keyed by the `base` field of each
440 * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
441 * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
442 * merged when possible so that no two similar and adjacent regions exist that have not been
443 * merged.
444 */
445 VMAMap vma_map;
446
331 u32 address_space_width = 0; 447 u32 address_space_width = 0;
332 VAddr address_space_base = 0; 448 VAddr address_space_base = 0;
333 VAddr address_space_end = 0; 449 VAddr address_space_end = 0;
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0417fdb92..b506bc3dd 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -20,6 +20,7 @@
20#include "core/hle/service/aoc/aoc_u.h" 20#include "core/hle/service/aoc/aoc_u.h"
21#include "core/hle/service/filesystem/filesystem.h" 21#include "core/hle/service/filesystem/filesystem.h"
22#include "core/loader/loader.h" 22#include "core/loader/loader.h"
23#include "core/settings.h"
23 24
24namespace Service::AOC { 25namespace Service::AOC {
25 26
@@ -76,6 +77,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
76 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
77 78
78 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 79 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
80
81 const auto& disabled = Settings::values.disabled_addons[current];
82 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
83 rb.Push<u32>(0);
84 return;
85 }
86
79 rb.Push<u32>(static_cast<u32>( 87 rb.Push<u32>(static_cast<u32>(
80 std::count_if(add_on_content.begin(), add_on_content.end(), 88 std::count_if(add_on_content.begin(), add_on_content.end(),
81 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); 89 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
@@ -96,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
96 out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); 104 out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
97 } 105 }
98 106
107 const auto& disabled = Settings::values.disabled_addons[current];
108 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
109 out = {};
110
99 if (out.size() < offset) { 111 if (out.size() < offset) {
100 IPC::ResponseBuilder rb{ctx, 2}; 112 IPC::ResponseBuilder rb{ctx, 2};
101 // TODO(DarkLordZach): Find the correct error code. 113 // TODO(DarkLordZach): Find the correct error code.
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d2ffd5776..74c4e583b 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -45,8 +45,12 @@ public:
45 explicit IStorage(FileSys::VirtualFile backend_) 45 explicit IStorage(FileSys::VirtualFile backend_)
46 : ServiceFramework("IStorage"), backend(std::move(backend_)) { 46 : ServiceFramework("IStorage"), backend(std::move(backend_)) {
47 static const FunctionInfo functions[] = { 47 static const FunctionInfo functions[] = {
48 {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, 48 {0, &IStorage::Read, "Read"},
49 {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, 49 {1, nullptr, "Write"},
50 {2, nullptr, "Flush"},
51 {3, nullptr, "SetSize"},
52 {4, &IStorage::GetSize, "GetSize"},
53 {5, nullptr, "OperateRange"},
50 }; 54 };
51 RegisterHandlers(functions); 55 RegisterHandlers(functions);
52 } 56 }
@@ -83,6 +87,15 @@ private:
83 IPC::ResponseBuilder rb{ctx, 2}; 87 IPC::ResponseBuilder rb{ctx, 2};
84 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
85 } 89 }
90
91 void GetSize(Kernel::HLERequestContext& ctx) {
92 const u64 size = backend->GetSize();
93 LOG_DEBUG(Service_FS, "called, size={}", size);
94
95 IPC::ResponseBuilder rb{ctx, 4};
96 rb.Push(RESULT_SUCCESS);
97 rb.Push<u64>(size);
98 }
86}; 99};
87 100
88class IFile final : public ServiceFramework<IFile> { 101class IFile final : public ServiceFramework<IFile> {
@@ -796,9 +809,18 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
796void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 809void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
797 LOG_WARNING(Service_FS, "(STUBBED) called"); 810 LOG_WARNING(Service_FS, "(STUBBED) called");
798 811
812 enum class LogMode : u32 {
813 Off,
814 Log,
815 RedirectToSdCard,
816 LogToSdCard = Log | RedirectToSdCard,
817 };
818
819 // Given we always want to receive logging information,
820 // we always specify logging as enabled.
799 IPC::ResponseBuilder rb{ctx, 3}; 821 IPC::ResponseBuilder rb{ctx, 3};
800 rb.Push(RESULT_SUCCESS); 822 rb.Push(RESULT_SUCCESS);
801 rb.Push<u32>(5); 823 rb.PushEnum(LogMode::Log);
802} 824}
803 825
804void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { 826void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 453d90a22..13bcefe07 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -408,13 +408,13 @@ private:
408 using SHA256Hash = std::array<u8, 0x20>; 408 using SHA256Hash = std::array<u8, 0x20>;
409 409
410 struct NROHeader { 410 struct NROHeader {
411 u32_le entrypoint_insn; 411 INSERT_PADDING_WORDS(1);
412 u32_le mod_offset; 412 u32_le mod_offset;
413 INSERT_PADDING_WORDS(2); 413 INSERT_PADDING_WORDS(2);
414 u32_le magic; 414 u32_le magic;
415 INSERT_PADDING_WORDS(1); 415 u32_le version;
416 u32_le nro_size; 416 u32_le nro_size;
417 INSERT_PADDING_WORDS(1); 417 u32_le flags;
418 u32_le text_offset; 418 u32_le text_offset;
419 u32_le text_size; 419 u32_le text_size;
420 u32_le ro_offset; 420 u32_le ro_offset;
@@ -430,9 +430,10 @@ private:
430 430
431 struct NRRHeader { 431 struct NRRHeader {
432 u32_le magic; 432 u32_le magic;
433 INSERT_PADDING_BYTES(0x1C); 433 INSERT_PADDING_BYTES(12);
434 u64_le title_id_mask; 434 u64_le title_id_mask;
435 u64_le title_id_pattern; 435 u64_le title_id_pattern;
436 INSERT_PADDING_BYTES(16);
436 std::array<u8, 0x100> modulus; 437 std::array<u8, 0x100> modulus;
437 std::array<u8, 0x100> signature_1; 438 std::array<u8, 0x100> signature_1;
438 std::array<u8, 0x100> signature_2; 439 std::array<u8, 0x100> signature_2;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 3bfce0110..0a650f36c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -137,6 +137,10 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
137} 137}
138 138
139static void PushGPUEntries(Tegra::CommandList&& entries) { 139static void PushGPUEntries(Tegra::CommandList&& entries) {
140 if (entries.empty()) {
141 return;
142 }
143
140 auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; 144 auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
141 dma_pusher.Push(std::move(entries)); 145 dma_pusher.Push(std::move(entries));
142 dma_pusher.DispatchCalls(); 146 dma_pusher.DispatchCalls();
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 1ec340466..d25b80ab0 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -70,10 +70,6 @@
70#include "core/hle/service/vi/vi.h" 70#include "core/hle/service/vi/vi.h"
71#include "core/hle/service/wlan/wlan.h" 71#include "core/hle/service/wlan/wlan.h"
72 72
73using Kernel::ClientPort;
74using Kernel::ServerPort;
75using Kernel::SharedPtr;
76
77namespace Service { 73namespace Service {
78 74
79/** 75/**
@@ -101,33 +97,33 @@ ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_ses
101ServiceFrameworkBase::~ServiceFrameworkBase() = default; 97ServiceFrameworkBase::~ServiceFrameworkBase() = default;
102 98
103void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { 99void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
104 ASSERT(port == nullptr); 100 ASSERT(!port_installed);
105 port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); 101
102 auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
106 port->SetHleHandler(shared_from_this()); 103 port->SetHleHandler(shared_from_this());
104 port_installed = true;
107} 105}
108 106
109void ServiceFrameworkBase::InstallAsNamedPort() { 107void ServiceFrameworkBase::InstallAsNamedPort() {
110 ASSERT(port == nullptr); 108 ASSERT(!port_installed);
111 109
112 auto& kernel = Core::System::GetInstance().Kernel(); 110 auto& kernel = Core::System::GetInstance().Kernel();
113 SharedPtr<ServerPort> server_port; 111 auto [server_port, client_port] =
114 SharedPtr<ClientPort> client_port; 112 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
115 std::tie(server_port, client_port) =
116 ServerPort::CreatePortPair(kernel, max_sessions, service_name);
117 server_port->SetHleHandler(shared_from_this()); 113 server_port->SetHleHandler(shared_from_this());
118 kernel.AddNamedPort(service_name, std::move(client_port)); 114 kernel.AddNamedPort(service_name, std::move(client_port));
115 port_installed = true;
119} 116}
120 117
121Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { 118Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
122 ASSERT(port == nullptr); 119 ASSERT(!port_installed);
123 120
124 auto& kernel = Core::System::GetInstance().Kernel(); 121 auto& kernel = Core::System::GetInstance().Kernel();
125 Kernel::SharedPtr<Kernel::ServerPort> server_port; 122 auto [server_port, client_port] =
126 Kernel::SharedPtr<Kernel::ClientPort> client_port;
127 std::tie(server_port, client_port) =
128 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); 123 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
129 port = MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)).Unwrap(); 124 auto port = MakeResult(std::move(server_port)).Unwrap();
130 port->SetHleHandler(shared_from_this()); 125 port->SetHleHandler(shared_from_this());
126 port_installed = true;
131 return client_port; 127 return client_port;
132} 128}
133 129
@@ -152,8 +148,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
152 } 148 }
153 buf.push_back('}'); 149 buf.push_back('}');
154 150
155 LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); 151 UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
156 UNIMPLEMENTED();
157} 152}
158 153
159void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { 154void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 98483ecf1..029533628 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -96,11 +96,9 @@ private:
96 /// Maximum number of concurrent sessions that this service can handle. 96 /// Maximum number of concurrent sessions that this service can handle.
97 u32 max_sessions; 97 u32 max_sessions;
98 98
99 /** 99 /// Flag to store if a port was already create/installed to detect multiple install attempts,
100 * Port where incoming connections will be received. Only created when InstallAsService() or 100 /// which is not supported.
101 * InstallAsNamedPort() are called. 101 bool port_installed = false;
102 */
103 Kernel::SharedPtr<Kernel::ServerPort> port;
104 102
105 /// Function used to safely up-cast pointers to the derived class before invoking a handler. 103 /// Function used to safely up-cast pointers to the derived class before invoking a handler.
106 InvokerFn* handler_invoker; 104 InvokerFn* handler_invoker;
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 0d0f63a78..d73530086 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -54,13 +54,11 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
54 return ERR_ALREADY_REGISTERED; 54 return ERR_ALREADY_REGISTERED;
55 55
56 auto& kernel = Core::System::GetInstance().Kernel(); 56 auto& kernel = Core::System::GetInstance().Kernel();
57 Kernel::SharedPtr<Kernel::ServerPort> server_port; 57 auto [server_port, client_port] =
58 Kernel::SharedPtr<Kernel::ClientPort> client_port;
59 std::tie(server_port, client_port) =
60 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); 58 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
61 59
62 registered_services.emplace(std::move(name), std::move(client_port)); 60 registered_services.emplace(std::move(name), std::move(client_port));
63 return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); 61 return MakeResult(std::move(server_port));
64} 62}
65 63
66ResultCode ServiceManager::UnregisterService(const std::string& name) { 64ResultCode ServiceManager::UnregisterService(const std::string& name) {
@@ -83,7 +81,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
83 return ERR_SERVICE_NOT_REGISTERED; 81 return ERR_SERVICE_NOT_REGISTERED;
84 } 82 }
85 83
86 return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); 84 return MakeResult(it->second);
87} 85}
88 86
89ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( 87ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService(
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index d109ed2b5..1615cb5a8 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -33,7 +33,7 @@ public:
33 */ 33 */
34 static FileType IdentifyType(const FileSys::VirtualFile& file); 34 static FileType IdentifyType(const FileSys::VirtualFile& file);
35 35
36 FileType GetFileType() override { 36 FileType GetFileType() const override {
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 6af76441c..a2d33021c 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -22,7 +22,7 @@ public:
22 */ 22 */
23 static FileType IdentifyType(const FileSys::VirtualFile& file); 23 static FileType IdentifyType(const FileSys::VirtualFile& file);
24 24
25 FileType GetFileType() override { 25 FileType GetFileType() const override {
26 return IdentifyType(file); 26 return IdentifyType(file);
27 } 27 }
28 28
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7686634bf..0838e303b 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
16 17
17namespace Kernel { 18namespace Kernel {
@@ -131,7 +132,7 @@ public:
131 * Returns the type of this file 132 * Returns the type of this file
132 * @return FileType corresponding to the loaded file 133 * @return FileType corresponding to the loaded file
133 */ 134 */
134 virtual FileType GetFileType() = 0; 135 virtual FileType GetFileType() const = 0;
135 136
136 /** 137 /**
137 * Load the application and return the created Process instance 138 * Load the application and return the created Process instance
@@ -243,6 +244,15 @@ public:
243 return ResultStatus::ErrorNotImplemented; 244 return ResultStatus::ErrorNotImplemented;
244 } 245 }
245 246
247 /**
248 * Get the developer of the application
249 * @param developer Reference to store the application developer into
250 * @return ResultStatus result of function
251 */
252 virtual ResultStatus ReadDeveloper(std::string& developer) {
253 return ResultStatus::ErrorNotImplemented;
254 }
255
246protected: 256protected:
247 FileSys::VirtualFile file; 257 FileSys::VirtualFile file;
248 bool is_loaded = false; 258 bool is_loaded = false;
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 42f4a777b..a093e3d36 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -37,7 +37,7 @@ FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) {
37 return IdentifyTypeImpl(nax); 37 return IdentifyTypeImpl(nax);
38} 38}
39 39
40FileType AppLoader_NAX::GetFileType() { 40FileType AppLoader_NAX::GetFileType() const {
41 return IdentifyTypeImpl(*nax); 41 return IdentifyTypeImpl(*nax);
42} 42}
43 43
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index b4d93bd01..0a97511b8 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -31,7 +31,7 @@ public:
31 */ 31 */
32 static FileType IdentifyType(const FileSys::VirtualFile& file); 32 static FileType IdentifyType(const FileSys::VirtualFile& file);
33 33
34 FileType GetFileType() override; 34 FileType GetFileType() const override;
35 35
36 ResultStatus Load(Kernel::Process& process) override; 36 ResultStatus Load(Kernel::Process& process) override;
37 37
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 95d9b73a1..cbbe701d2 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -29,7 +29,7 @@ public:
29 */ 29 */
30 static FileType IdentifyType(const FileSys::VirtualFile& file); 30 static FileType IdentifyType(const FileSys::VirtualFile& file);
31 31
32 FileType GetFileType() override { 32 FileType GetFileType() const override {
33 return IdentifyType(file); 33 return IdentifyType(file);
34 } 34 }
35 35
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 6deff3a51..013d629c0 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -33,7 +33,7 @@ public:
33 */ 33 */
34 static FileType IdentifyType(const FileSys::VirtualFile& file); 34 static FileType IdentifyType(const FileSys::VirtualFile& file);
35 35
36 FileType GetFileType() override { 36 FileType GetFileType() const override {
37 return IdentifyType(file); 37 return IdentifyType(file);
38 } 38 }
39 39
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 0c1defbb6..135b6ea5a 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -37,7 +37,7 @@ public:
37 */ 37 */
38 static FileType IdentifyType(const FileSys::VirtualFile& file); 38 static FileType IdentifyType(const FileSys::VirtualFile& file);
39 39
40 FileType GetFileType() override { 40 FileType GetFileType() const override {
41 return IdentifyType(file); 41 return IdentifyType(file);
42 } 42 }
43 43
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 080d89904..b4ab88ae8 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -151,4 +151,11 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
151 title = nacp_file->GetApplicationName(); 151 title = nacp_file->GetApplicationName();
152 return ResultStatus::Success; 152 return ResultStatus::Success;
153} 153}
154
155ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) {
156 if (nacp_file == nullptr)
157 return ResultStatus::ErrorNoControl;
158 developer = nacp_file->GetDeveloperName();
159 return ResultStatus::Success;
160}
154} // namespace Loader 161} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index db91cd01e..2b1e0719b 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -31,7 +31,7 @@ public:
31 */ 31 */
32 static FileType IdentifyType(const FileSys::VirtualFile& file); 32 static FileType IdentifyType(const FileSys::VirtualFile& file);
33 33
34 FileType GetFileType() override { 34 FileType GetFileType() const override {
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
@@ -43,6 +43,7 @@ public:
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
45 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
46 ResultStatus ReadDeveloper(std::string& developer) override;
46 47
47private: 48private:
48 std::unique_ptr<FileSys::NSP> nsp; 49 std::unique_ptr<FileSys::NSP> nsp;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 461607c95..bd5a83b49 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -120,4 +120,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
120 title = nacp_file->GetApplicationName(); 120 title = nacp_file->GetApplicationName();
121 return ResultStatus::Success; 121 return ResultStatus::Success;
122} 122}
123
124ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) {
125 if (nacp_file == nullptr)
126 return ResultStatus::ErrorNoControl;
127 developer = nacp_file->GetDeveloperName();
128 return ResultStatus::Success;
129}
123} // namespace Loader 130} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 46f8dfc9e..15d1b1a23 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -31,7 +31,7 @@ public:
31 */ 31 */
32 static FileType IdentifyType(const FileSys::VirtualFile& file); 32 static FileType IdentifyType(const FileSys::VirtualFile& file);
33 33
34 FileType GetFileType() override { 34 FileType GetFileType() const override {
35 return IdentifyType(file); 35 return IdentifyType(file);
36 } 36 }
37 37
@@ -43,6 +43,7 @@ public:
43 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
45 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
46 ResultStatus ReadDeveloper(std::string& developer) override;
46 47
47private: 48private:
48 std::unique_ptr<FileSys::XCI> xci; 49 std::unique_ptr<FileSys::XCI> xci;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 41fd2a6a0..643afdee8 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -125,14 +125,13 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin
125 * using a VMA from the current process 125 * using a VMA from the current process
126 */ 126 */
127static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { 127static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
128 u8* direct_pointer = nullptr; 128 const auto& vm_manager = process.VMManager();
129
130 auto& vm_manager = process.VMManager();
131 129
132 auto it = vm_manager.FindVMA(vaddr); 130 const auto it = vm_manager.FindVMA(vaddr);
133 ASSERT(it != vm_manager.vma_map.end()); 131 DEBUG_ASSERT(vm_manager.IsValidHandle(it));
134 132
135 auto& vma = it->second; 133 u8* direct_pointer = nullptr;
134 const auto& vma = it->second;
136 switch (vma.type) { 135 switch (vma.type) {
137 case Kernel::VMAType::AllocatedMemoryBlock: 136 case Kernel::VMAType::AllocatedMemoryBlock:
138 direct_pointer = vma.backing_block->data() + vma.offset; 137 direct_pointer = vma.backing_block->data() + vma.offset;
diff --git a/src/core/settings.h b/src/core/settings.h
index a0c5fd447..de01b05c0 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,8 +6,10 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <map>
9#include <optional> 10#include <optional>
10#include <string> 11#include <string>
12#include <vector>
11#include "common/common_types.h" 13#include "common/common_types.h"
12 14
13namespace Settings { 15namespace Settings {
@@ -411,6 +413,9 @@ struct Values {
411 std::string web_api_url; 413 std::string web_api_url;
412 std::string yuzu_username; 414 std::string yuzu_username;
413 std::string yuzu_token; 415 std::string yuzu_token;
416
417 // Add-Ons
418 std::map<u64, std::vector<std::string>> disabled_addons;
414} extern values; 419} extern values;
415 420
416void Apply(); 421void Apply();
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index a3b08c740..09ed74d78 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -103,13 +103,8 @@ bool VerifyLogin(const std::string& username, const std::string& token) {
103 103
104TelemetrySession::TelemetrySession() { 104TelemetrySession::TelemetrySession() {
105#ifdef ENABLE_WEB_SERVICE 105#ifdef ENABLE_WEB_SERVICE
106 if (Settings::values.enable_telemetry) { 106 backend = std::make_unique<WebService::TelemetryJson>(
107 backend = std::make_unique<WebService::TelemetryJson>(Settings::values.web_api_url, 107 Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token);
108 Settings::values.yuzu_username,
109 Settings::values.yuzu_token);
110 } else {
111 backend = std::make_unique<Telemetry::NullVisitor>();
112 }
113#else 108#else
114 backend = std::make_unique<Telemetry::NullVisitor>(); 109 backend = std::make_unique<Telemetry::NullVisitor>();
115#endif 110#endif
@@ -180,7 +175,8 @@ TelemetrySession::~TelemetrySession() {
180 // This is just a placeholder to wrap up the session once the core completes and this is 175 // This is just a placeholder to wrap up the session once the core completes and this is
181 // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. 176 // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service.
182 field_collection.Accept(*backend); 177 field_collection.Accept(*backend);
183 backend->Complete(); 178 if (Settings::values.enable_telemetry)
179 backend->Complete();
184 backend = nullptr; 180 backend = nullptr;
185} 181}
186 182
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 5ea094e64..5198cd268 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -575,7 +575,7 @@ union Instruction {
575 575
576 union { 576 union {
577 BitField<39, 2, u64> tab5cb8_2; 577 BitField<39, 2, u64> tab5cb8_2;
578 BitField<41, 3, u64> tab5c68_1; 578 BitField<41, 3, u64> postfactor;
579 BitField<44, 2, u64> tab5c68_0; 579 BitField<44, 2, u64> tab5c68_0;
580 BitField<48, 1, u64> negate_b; 580 BitField<48, 1, u64> negate_b;
581 } fmul; 581 } fmul;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 038b25c75..aea6bf1af 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -2,7 +2,9 @@
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 <boost/functional/hash.hpp>
5#include "common/assert.h" 6#include "common/assert.h"
7#include "common/hash.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/memory.h" 9#include "core/memory.h"
8#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
@@ -66,14 +68,17 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
66 // stage here. 68 // stage here.
67 setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); 69 setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)));
68 case Maxwell::ShaderProgram::VertexB: 70 case Maxwell::ShaderProgram::VertexB:
71 CalculateProperties();
69 program_result = GLShader::GenerateVertexShader(setup); 72 program_result = GLShader::GenerateVertexShader(setup);
70 gl_type = GL_VERTEX_SHADER; 73 gl_type = GL_VERTEX_SHADER;
71 break; 74 break;
72 case Maxwell::ShaderProgram::Geometry: 75 case Maxwell::ShaderProgram::Geometry:
76 CalculateProperties();
73 program_result = GLShader::GenerateGeometryShader(setup); 77 program_result = GLShader::GenerateGeometryShader(setup);
74 gl_type = GL_GEOMETRY_SHADER; 78 gl_type = GL_GEOMETRY_SHADER;
75 break; 79 break;
76 case Maxwell::ShaderProgram::Fragment: 80 case Maxwell::ShaderProgram::Fragment:
81 CalculateProperties();
77 program_result = GLShader::GenerateFragmentShader(setup); 82 program_result = GLShader::GenerateFragmentShader(setup);
78 gl_type = GL_FRAGMENT_SHADER; 83 gl_type = GL_FRAGMENT_SHADER;
79 break; 84 break;
@@ -140,6 +145,46 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
140 return target_program.handle; 145 return target_program.handle;
141}; 146};
142 147
148static bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
149 // sched instructions appear once every 4 instructions.
150 static constexpr std::size_t SchedPeriod = 4;
151 const std::size_t absolute_offset = offset - main_offset;
152 return (absolute_offset % SchedPeriod) == 0;
153}
154
155static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) {
156 constexpr std::size_t start_offset = 10;
157 std::size_t offset = start_offset;
158 std::size_t size = start_offset * sizeof(u64);
159 while (offset < program.size()) {
160 const u64 inst = program[offset];
161 if (!IsSchedInstruction(offset, start_offset)) {
162 if (inst == 0 || (inst >> 52) == 0x50b) {
163 break;
164 }
165 }
166 size += sizeof(inst);
167 offset++;
168 }
169 return size;
170}
171
172void CachedShader::CalculateProperties() {
173 setup.program.real_size = CalculateProgramSize(setup.program.code);
174 setup.program.real_size_b = 0;
175 setup.program.unique_identifier = Common::CityHash64(
176 reinterpret_cast<const char*>(setup.program.code.data()), setup.program.real_size);
177 if (program_type == Maxwell::ShaderProgram::VertexA) {
178 std::size_t seed = 0;
179 boost::hash_combine(seed, setup.program.unique_identifier);
180 setup.program.real_size_b = CalculateProgramSize(setup.program.code_b);
181 const u64 identifier_b = Common::CityHash64(
182 reinterpret_cast<const char*>(setup.program.code_b.data()), setup.program.real_size_b);
183 boost::hash_combine(seed, identifier_b);
184 setup.program.unique_identifier = static_cast<u64>(seed);
185 }
186}
187
143ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} 188ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {}
144 189
145Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 190Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 08f470de3..b4ef6030d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -81,6 +81,8 @@ private:
81 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, 81 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
82 u32 max_vertices, const std::string& debug_name); 82 u32 max_vertices, const std::string& debug_name);
83 83
84 void CalculateProperties();
85
84 VAddr addr; 86 VAddr addr;
85 std::size_t shader_length; 87 std::size_t shader_length;
86 Maxwell::ShaderProgram program_type; 88 Maxwell::ShaderProgram program_type;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4fc09cac6..bd61af463 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -928,7 +928,7 @@ private:
928 case Attribute::Index::FrontFacing: 928 case Attribute::Index::FrontFacing:
929 // TODO(Subv): Find out what the values are for the other elements. 929 // TODO(Subv): Find out what the values are for the other elements.
930 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 930 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
931 return "vec4(0, 0, 0, uintBitsToFloat(gl_FrontFacing ? 1 : 0))"; 931 return "vec4(0, 0, 0, intBitsToFloat(gl_FrontFacing ? -1 : 0))";
932 default: 932 default:
933 const u32 index{static_cast<u32>(attribute) - 933 const u32 index{static_cast<u32>(attribute) -
934 static_cast<u32>(Attribute::Index::Attribute_0)}; 934 static_cast<u32>(Attribute::Index::Attribute_0)};
@@ -1681,7 +1681,7 @@ private:
1681 for (size_t i = 0; i < coord_count; ++i) { 1681 for (size_t i = 0; i < coord_count; ++i) {
1682 const bool last = (i == (coord_count - 1)) && (coord_count > 1); 1682 const bool last = (i == (coord_count - 1)) && (coord_count > 1);
1683 coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); 1683 coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i);
1684 if (!last) { 1684 if (i < coord_count - 1) {
1685 coord += ','; 1685 coord += ',';
1686 } 1686 }
1687 } 1687 }
@@ -1702,6 +1702,99 @@ private:
1702 is_array, (coord_count > 2 ? 1 : 0))); 1702 is_array, (coord_count > 2 ? 1 : 0)));
1703 } 1703 }
1704 1704
1705 std::pair<std::string, std::string> GetTLD4Code(const Instruction& instr,
1706 const Tegra::Shader::TextureType texture_type,
1707 const bool depth_compare, const bool is_array) {
1708
1709 const size_t coord_count = TextureCoordinates(texture_type);
1710 const size_t total_coord_count = coord_count + (is_array ? 1 : 0);
1711 const size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0);
1712
1713 constexpr std::array<const char*, 5> coord_container{
1714 {"", "", "vec2 coord = vec2(", "vec3 coord = vec3(", "vec4 coord = vec4("}};
1715
1716 // If enabled arrays index is always stored in the gpr8 field
1717 const u64 array_register = instr.gpr8.Value();
1718 // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
1719 const u64 coord_register = array_register + (is_array ? 1 : 0);
1720
1721 std::string coord = coord_container[total_coord_count];
1722 for (size_t i = 0; i < coord_count;) {
1723 coord += regs.GetRegisterAsFloat(coord_register + i);
1724 ++i;
1725 if (i != coord_count) {
1726 coord += ',';
1727 }
1728 }
1729
1730 if (is_array) {
1731 coord += ',' + regs.GetRegisterAsInteger(array_register);
1732 }
1733 coord += ");";
1734
1735 const std::string sampler =
1736 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
1737
1738 std::string texture = "textureGather(" + sampler + ", coord, ";
1739 if (depth_compare) {
1740 // Depth is always stored in the register signaled by gpr20
1741 texture += regs.GetRegisterAsFloat(instr.gpr20.Value()) + ')';
1742 } else {
1743 texture += std::to_string(instr.tld4.component) + ')';
1744 }
1745 return std::make_pair(coord, texture);
1746 }
1747
1748 std::pair<std::string, std::string> GetTLDSCode(const Instruction& instr,
1749 const Tegra::Shader::TextureType texture_type,
1750 const bool is_array) {
1751
1752 const size_t coord_count = TextureCoordinates(texture_type);
1753 const size_t total_coord_count = coord_count + (is_array ? 1 : 0);
1754 const bool lod_enabled =
1755 instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL;
1756
1757 constexpr std::array<const char*, 4> coord_container{
1758 {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}};
1759
1760 std::string coord = coord_container[total_coord_count];
1761
1762 // If enabled arrays index is always stored in the gpr8 field
1763 const u64 array_register = instr.gpr8.Value();
1764
1765 // if is array gpr20 is used
1766 const u64 coord_register = is_array ? instr.gpr20.Value() : instr.gpr8.Value();
1767
1768 const u64 last_coord_register =
1769 ((coord_count > 2) || (coord_count == 2 && !lod_enabled)) && !is_array
1770 ? static_cast<u64>(instr.gpr20.Value())
1771 : coord_register + 1;
1772
1773 for (size_t i = 0; i < coord_count; ++i) {
1774 const bool last = (i == (coord_count - 1)) && (coord_count > 1);
1775 coord += regs.GetRegisterAsInteger(last ? last_coord_register : coord_register + i);
1776 if (i < coord_count - 1) {
1777 coord += ',';
1778 }
1779 }
1780 if (is_array) {
1781 coord += ',' + regs.GetRegisterAsInteger(array_register);
1782 }
1783 coord += ");";
1784
1785 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false);
1786
1787 std::string texture = "texelFetch(" + sampler + ", coords";
1788
1789 if (lod_enabled) {
1790 // When lod is used always is in grp20
1791 texture += ", " + regs.GetRegisterAsInteger(instr.gpr20) + ')';
1792 } else {
1793 texture += ", 0)";
1794 }
1795 return std::make_pair(coord, texture);
1796 }
1797
1705 /** 1798 /**
1706 * Compiles a single instruction from Tegra to GLSL. 1799 * Compiles a single instruction from Tegra to GLSL.
1707 * @param offset the offset of the Tegra shader instruction. 1800 * @param offset the offset of the Tegra shader instruction.
@@ -1774,9 +1867,6 @@ private:
1774 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, 1867 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
1775 "FMUL tab5cb8_2({}) is not implemented", 1868 "FMUL tab5cb8_2({}) is not implemented",
1776 instr.fmul.tab5cb8_2.Value()); 1869 instr.fmul.tab5cb8_2.Value());
1777 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0,
1778 "FMUL tab5cb8_1({}) is not implemented",
1779 instr.fmul.tab5c68_1.Value());
1780 UNIMPLEMENTED_IF_MSG( 1870 UNIMPLEMENTED_IF_MSG(
1781 instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", 1871 instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
1782 instr.fmul.tab5c68_0 1872 instr.fmul.tab5c68_0
@@ -1786,7 +1876,26 @@ private:
1786 1876
1787 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1877 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1788 1878
1789 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1879 std::string postfactor_op;
1880 if (instr.fmul.postfactor != 0) {
1881 s8 postfactor = static_cast<s8>(instr.fmul.postfactor);
1882
1883 // postfactor encoded as 3-bit 1's complement in instruction,
1884 // interpreted with below logic.
1885 if (postfactor >= 4) {
1886 postfactor = 7 - postfactor;
1887 } else {
1888 postfactor = 0 - postfactor;
1889 }
1890
1891 if (postfactor > 0) {
1892 postfactor_op = " * " + std::to_string(1 << postfactor);
1893 } else {
1894 postfactor_op = " / " + std::to_string(1 << -postfactor);
1895 }
1896 }
1897
1898 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1,
1790 instr.alu.saturate_d, 0, true); 1899 instr.alu.saturate_d, 0, true);
1791 break; 1900 break;
1792 } 1901 }
@@ -2825,9 +2934,6 @@ private:
2825 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; 2934 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
2826 const bool is_array{instr.tlds.IsArrayTexture()}; 2935 const bool is_array{instr.tlds.IsArrayTexture()};
2827 2936
2828 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D);
2829 ASSERT(is_array == false);
2830
2831 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2937 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2832 "NODEP is not implemented"); 2938 "NODEP is not implemented");
2833 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2939 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
@@ -2835,54 +2941,16 @@ private:
2835 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2941 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2836 "MZ is not implemented"); 2942 "MZ is not implemented");
2837 2943
2838 u32 extra_op_offset = 0; 2944 const auto [coord, texture] = GetTLDSCode(instr, texture_type, is_array);
2839
2840 ShaderScopedScope scope = shader.Scope();
2841
2842 switch (texture_type) {
2843 case Tegra::Shader::TextureType::Texture1D: {
2844 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2845 shader.AddLine("float coords = " + x + ';');
2846 break;
2847 }
2848 case Tegra::Shader::TextureType::Texture2D: {
2849 UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture");
2850
2851 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2852 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2853 // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
2854 shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
2855 extra_op_offset = 1;
2856 break;
2857 }
2858 default:
2859 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
2860 }
2861 const std::string sampler =
2862 GetSampler(instr.sampler, texture_type, is_array, false);
2863 2945
2864 const std::string texture = [&]() { 2946 const auto scope = shader.Scope();
2865 switch (instr.tlds.GetTextureProcessMode()) {
2866 case Tegra::Shader::TextureProcessMode::LZ:
2867 return "texelFetch(" + sampler + ", coords, 0)";
2868 case Tegra::Shader::TextureProcessMode::LL:
2869 shader.AddLine(
2870 "float lod = " +
2871 regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
2872 return "texelFetch(" + sampler + ", coords, lod)";
2873 default:
2874 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2875 static_cast<u32>(instr.tlds.GetTextureProcessMode()));
2876 return "texelFetch(" + sampler + ", coords, 0)";
2877 }
2878 }();
2879 2947
2880 WriteTexsInstructionFloat(instr, texture); 2948 shader.AddLine(coord);
2949 shader.AddLine("vec4 texture_tmp = " + texture + ';');
2950 WriteTexsInstructionFloat(instr, "texture_tmp");
2881 break; 2951 break;
2882 } 2952 }
2883 case OpCode::Id::TLD4: { 2953 case OpCode::Id::TLD4: {
2884 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
2885 ASSERT(instr.tld4.array == 0);
2886 2954
2887 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2955 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2888 "NODEP is not implemented"); 2956 "NODEP is not implemented");
@@ -2892,56 +2960,29 @@ private:
2892 "NDV is not implemented"); 2960 "NDV is not implemented");
2893 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), 2961 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2894 "PTP is not implemented"); 2962 "PTP is not implemented");
2963
2964 auto texture_type = instr.tld4.texture_type.Value();
2895 const bool depth_compare = 2965 const bool depth_compare =
2896 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2966 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2897 auto texture_type = instr.tld4.texture_type.Value(); 2967 const bool is_array = instr.tld4.array != 0;
2898 u32 num_coordinates = TextureCoordinates(texture_type);
2899 if (depth_compare)
2900 num_coordinates += 1;
2901 2968
2902 const auto scope = shader.Scope(); 2969 const auto [coord, texture] =
2903 2970 GetTLD4Code(instr, texture_type, depth_compare, is_array);
2904 switch (num_coordinates) {
2905 case 2: {
2906 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2907 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2908 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2909 break;
2910 }
2911 case 3: {
2912 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2913 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2914 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2915 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2916 break;
2917 }
2918 default:
2919 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2920 static_cast<u32>(num_coordinates));
2921 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2922 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2923 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2924 texture_type = Tegra::Shader::TextureType::Texture2D;
2925 }
2926 2971
2927 const std::string sampler = 2972 const auto scope = shader.Scope();
2928 GetSampler(instr.sampler, texture_type, false, depth_compare);
2929 2973
2930 const std::string texture = "textureGather(" + sampler + ", coords, " + 2974 shader.AddLine(coord);
2931 std::to_string(instr.tld4.component) + ')'; 2975 std::size_t dest_elem{};
2932 2976
2933 if (depth_compare) { 2977 shader.AddLine("vec4 texture_tmp = " + texture + ';');
2934 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); 2978 for (std::size_t elem = 0; elem < 4; ++elem) {
2935 } else { 2979 if (!instr.tex.IsComponentEnabled(elem)) {
2936 std::size_t dest_elem{}; 2980 // Skip disabled components
2937 for (std::size_t elem = 0; elem < 4; ++elem) { 2981 continue;
2938 if (!instr.tex.IsComponentEnabled(elem)) {
2939 // Skip disabled components
2940 continue;
2941 }
2942 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
2943 ++dest_elem;
2944 } 2982 }
2983 regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
2984 dest_elem);
2985 ++dest_elem;
2945 } 2986 }
2946 break; 2987 break;
2947 } 2988 }
@@ -2955,28 +2996,31 @@ private:
2955 2996
2956 const auto scope = shader.Scope(); 2997 const auto scope = shader.Scope();
2957 2998
2999 std::string coords;
3000
2958 const bool depth_compare = 3001 const bool depth_compare =
2959 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 3002 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2960 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 3003
2961 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
2962 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
2963 const std::string sampler = GetSampler( 3004 const std::string sampler = GetSampler(
2964 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); 3005 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
2965 if (depth_compare) {
2966 // Note: TLD4S coordinate encoding works just like TEXS's
2967 const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2968 shader.AddLine("vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");");
2969 } else {
2970 shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");");
2971 }
2972 3006
2973 std::string texture = "textureGather(" + sampler + ", coords, " + 3007 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
2974 std::to_string(instr.tld4s.component) + ')'; 3008 coords = "vec2 coords = vec2(" + op_a + ", ";
2975 if (depth_compare) { 3009 std::string texture = "textureGather(" + sampler + ", coords, ";
2976 texture = "vec4(" + texture + ')';
2977 }
2978 3010
2979 WriteTexsInstructionFloat(instr, texture); 3011 if (!depth_compare) {
3012 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
3013 coords += op_b + ");";
3014 texture += std::to_string(instr.tld4s.component) + ')';
3015 } else {
3016 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
3017 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20);
3018 coords += op_b + ");";
3019 texture += op_c + ')';
3020 }
3021 shader.AddLine(coords);
3022 shader.AddLine("vec4 texture_tmp = " + texture + ';');
3023 WriteTexsInstructionFloat(instr, "texture_tmp");
2980 break; 3024 break;
2981 } 3025 }
2982 case OpCode::Id::TXQ: { 3026 case OpCode::Id::TXQ: {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 23ed91e27..5d0819dc5 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h>
5#include "common/assert.h" 6#include "common/assert.h"
6#include "video_core/engines/maxwell_3d.h" 7#include "video_core/engines/maxwell_3d.h"
7#include "video_core/renderer_opengl/gl_shader_decompiler.h" 8#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -16,6 +17,8 @@ static constexpr u32 PROGRAM_OFFSET{10};
16ProgramResult GenerateVertexShader(const ShaderSetup& setup) { 17ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
17 std::string out = "#version 430 core\n"; 18 std::string out = "#version 430 core\n";
18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 19 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
20 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
21 out += "// Shader Unique Id: VS" + id + "\n\n";
19 out += Decompiler::GetCommonDeclarations(); 22 out += Decompiler::GetCommonDeclarations();
20 23
21 out += R"( 24 out += R"(
@@ -84,6 +87,8 @@ void main() {
84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { 87ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
85 // Version is intentionally skipped in shader generation, it's added by the lazy compilation. 88 // Version is intentionally skipped in shader generation, it's added by the lazy compilation.
86 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; 89 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
90 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
91 out += "// Shader Unique Id: GS" + id + "\n\n";
87 out += Decompiler::GetCommonDeclarations(); 92 out += Decompiler::GetCommonDeclarations();
88 out += "bool exec_geometry();\n"; 93 out += "bool exec_geometry();\n";
89 94
@@ -117,6 +122,8 @@ void main() {
117ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { 122ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
118 std::string out = "#version 430 core\n"; 123 std::string out = "#version 430 core\n";
119 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 124 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
125 const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
126 out += "// Shader Unique Id: FS" + id + "\n\n";
120 out += Decompiler::GetCommonDeclarations(); 127 out += Decompiler::GetCommonDeclarations();
121 out += "bool exec_fragment();\n"; 128 out += "bool exec_fragment();\n";
122 129
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 4fa6d7612..fcc20d3b4 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -177,6 +177,9 @@ struct ShaderSetup {
177 struct { 177 struct {
178 ProgramCode code; 178 ProgramCode code;
179 ProgramCode code_b; // Used for dual vertex shaders 179 ProgramCode code_b; // Used for dual vertex shaders
180 u64 unique_identifier;
181 std::size_t real_size;
182 std::size_t real_size_b;
180 } program; 183 } program;
181 184
182 /// Used in scenarios where we have a dual vertex shaders 185 /// Used in scenarios where we have a dual vertex shaders
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cfca8f4a8..3232aa8fb 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -35,6 +35,8 @@ add_executable(yuzu
35 configuration/configure_mouse_advanced.h 35 configuration/configure_mouse_advanced.h
36 configuration/configure_system.cpp 36 configuration/configure_system.cpp
37 configuration/configure_system.h 37 configuration/configure_system.h
38 configuration/configure_per_general.cpp
39 configuration/configure_per_general.h
38 configuration/configure_touchscreen_advanced.cpp 40 configuration/configure_touchscreen_advanced.cpp
39 configuration/configure_touchscreen_advanced.h 41 configuration/configure_touchscreen_advanced.h
40 configuration/configure_web.cpp 42 configuration/configure_web.cpp
@@ -86,6 +88,7 @@ set(UIS
86 configuration/configure_input.ui 88 configuration/configure_input.ui
87 configuration/configure_input_player.ui 89 configuration/configure_input_player.ui
88 configuration/configure_mouse_advanced.ui 90 configuration/configure_mouse_advanced.ui
91 configuration/configure_per_general.ui
89 configuration/configure_system.ui 92 configuration/configure_system.ui
90 configuration/configure_touchscreen_advanced.ui 93 configuration/configure_touchscreen_advanced.ui
91 configuration/configure_web.ui 94 configuration/configure_web.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index c26161169..eb2077b0f 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -441,6 +441,21 @@ void Config::ReadValues() {
441 Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); 441 Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString();
442 qt_config->endGroup(); 442 qt_config->endGroup();
443 443
444 const auto size = qt_config->beginReadArray("DisabledAddOns");
445 for (int i = 0; i < size; ++i) {
446 qt_config->setArrayIndex(i);
447 const auto title_id = qt_config->value("title_id", 0).toULongLong();
448 std::vector<std::string> out;
449 const auto d_size = qt_config->beginReadArray("disabled");
450 for (int j = 0; j < d_size; ++j) {
451 qt_config->setArrayIndex(j);
452 out.push_back(qt_config->value("d", "").toString().toStdString());
453 }
454 qt_config->endArray();
455 Settings::values.disabled_addons.insert_or_assign(title_id, out);
456 }
457 qt_config->endArray();
458
444 qt_config->beginGroup("UI"); 459 qt_config->beginGroup("UI");
445 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 460 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
446 UISettings::values.enable_discord_presence = 461 UISettings::values.enable_discord_presence =
@@ -645,6 +660,21 @@ void Config::SaveValues() {
645 qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); 660 qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
646 qt_config->endGroup(); 661 qt_config->endGroup();
647 662
663 qt_config->beginWriteArray("DisabledAddOns");
664 int i = 0;
665 for (const auto& elem : Settings::values.disabled_addons) {
666 qt_config->setArrayIndex(i);
667 qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first));
668 qt_config->beginWriteArray("disabled");
669 for (std::size_t j = 0; j < elem.second.size(); ++j) {
670 qt_config->setArrayIndex(j);
671 qt_config->setValue("d", QString::fromStdString(elem.second[j]));
672 }
673 qt_config->endArray();
674 ++i;
675 }
676 qt_config->endArray();
677
648 qt_config->beginGroup("UI"); 678 qt_config->beginGroup("UI");
649 qt_config->setValue("theme", UISettings::values.theme); 679 qt_config->setValue("theme", UISettings::values.theme);
650 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 680 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index eb1da0f9e..5d9ccc6e8 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -17,8 +17,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
17 17
18 ui->output_sink_combo_box->clear(); 18 ui->output_sink_combo_box->clear();
19 ui->output_sink_combo_box->addItem("auto"); 19 ui->output_sink_combo_box->addItem("auto");
20 for (const auto& sink_detail : AudioCore::g_sink_details) { 20 for (const char* id : AudioCore::GetSinkIDs()) {
21 ui->output_sink_combo_box->addItem(sink_detail.id); 21 ui->output_sink_combo_box->addItem(id);
22 } 22 }
23 23
24 connect(ui->volume_slider, &QSlider::valueChanged, this, 24 connect(ui->volume_slider, &QSlider::valueChanged, this,
@@ -97,8 +97,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
97 ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); 97 ui->audio_device_combo_box->addItem(AudioCore::auto_device_name);
98 98
99 const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); 99 const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
100 const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); 100 for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) {
101 for (const auto& device : device_list) {
102 ui->audio_device_combo_box->addItem(QString::fromStdString(device)); 101 ui->audio_device_combo_box->addItem(QString::fromStdString(device));
103 } 102 }
104} 103}
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp
new file mode 100644
index 000000000..80109b434
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.cpp
@@ -0,0 +1,170 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8
9#include <QHeaderView>
10#include <QMenu>
11#include <QMessageBox>
12#include <QStandardItemModel>
13#include <QString>
14#include <QTimer>
15#include <QTreeView>
16
17#include "core/file_sys/control_metadata.h"
18#include "core/file_sys/patch_manager.h"
19#include "core/file_sys/xts_archive.h"
20#include "core/loader/loader.h"
21#include "ui_configure_per_general.h"
22#include "yuzu/configuration/config.h"
23#include "yuzu/configuration/configure_input.h"
24#include "yuzu/configuration/configure_per_general.h"
25#include "yuzu/ui_settings.h"
26#include "yuzu/util/util.h"
27
28ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
29 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) {
30
31 ui->setupUi(this);
32 setFocusPolicy(Qt::ClickFocus);
33 setWindowTitle(tr("Properties"));
34
35 layout = new QVBoxLayout;
36 tree_view = new QTreeView;
37 item_model = new QStandardItemModel(tree_view);
38 tree_view->setModel(item_model);
39 tree_view->setAlternatingRowColors(true);
40 tree_view->setSelectionMode(QHeaderView::SingleSelection);
41 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
42 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
43 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
44 tree_view->setSortingEnabled(true);
45 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
46 tree_view->setUniformRowHeights(true);
47 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
48
49 item_model->insertColumns(0, 2);
50 item_model->setHeaderData(0, Qt::Horizontal, "Patch Name");
51 item_model->setHeaderData(1, Qt::Horizontal, "Version");
52
53 // We must register all custom types with the Qt Automoc system so that we are able to use it
54 // with signals/slots. In this case, QList falls under the umbrells of custom types.
55 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
56
57 layout->setContentsMargins(0, 0, 0, 0);
58 layout->setSpacing(0);
59 layout->addWidget(tree_view);
60
61 ui->scrollArea->setLayout(layout);
62
63 scene = new QGraphicsScene;
64 ui->icon_view->setScene(scene);
65
66 connect(item_model, &QStandardItemModel::itemChanged,
67 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
68
69 this->loadConfiguration();
70}
71
72ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default;
73
74void ConfigurePerGameGeneral::applyConfiguration() {
75 std::vector<std::string> disabled_addons;
76
77 for (const auto& item : list_items) {
78 const auto disabled = item.front()->checkState() == Qt::Unchecked;
79 if (disabled)
80 disabled_addons.push_back(item.front()->text().toStdString());
81 }
82
83 Settings::values.disabled_addons[title_id] = disabled_addons;
84}
85
86void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) {
87 this->file = std::move(file);
88 this->loadConfiguration();
89}
90
91void ConfigurePerGameGeneral::loadConfiguration() {
92 if (file == nullptr)
93 return;
94
95 const auto loader = Loader::GetLoader(file);
96
97 ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str());
98
99 FileSys::PatchManager pm{title_id};
100 const auto control = pm.GetControlMetadata();
101
102 if (control.first != nullptr) {
103 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
104 ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
105 ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
106 } else {
107 std::string title;
108 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
109 ui->display_name->setText(QString::fromStdString(title));
110
111 std::string developer;
112 if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success)
113 ui->display_developer->setText(QString::fromStdString(developer));
114
115 ui->display_version->setText(QStringLiteral("1.0.0"));
116 }
117
118 if (control.second != nullptr) {
119 scene->clear();
120
121 QPixmap map;
122 const auto bytes = control.second->ReadAllBytes();
123 map.loadFromData(bytes.data(), bytes.size());
124
125 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
126 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
127 } else {
128 std::vector<u8> bytes;
129 if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
130 scene->clear();
131
132 QPixmap map;
133 map.loadFromData(bytes.data(), bytes.size());
134
135 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
136 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
137 }
138 }
139
140 FileSys::VirtualFile update_raw;
141 loader->ReadUpdateRaw(update_raw);
142
143 const auto& disabled = Settings::values.disabled_addons[title_id];
144
145 for (const auto& patch : pm.GetPatchVersionNames(update_raw)) {
146 QStandardItem* first_item = new QStandardItem;
147 const auto name = QString::fromStdString(patch.first).replace("[D] ", "");
148 first_item->setText(name);
149 first_item->setCheckable(true);
150
151 const auto patch_disabled =
152 std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end();
153
154 first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked);
155
156 list_items.push_back(QList<QStandardItem*>{
157 first_item, new QStandardItem{QString::fromStdString(patch.second)}});
158 item_model->appendRow(list_items.back());
159 }
160
161 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
162
163 ui->display_filename->setText(QString::fromStdString(file->GetName()));
164
165 ui->display_format->setText(
166 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
167
168 const auto valueText = ReadableByteSize(file->GetSize());
169 ui->display_size->setText(valueText);
170}
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h
new file mode 100644
index 000000000..a4494446c
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.h
@@ -0,0 +1,50 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include <QKeyEvent>
11#include <QList>
12#include <QWidget>
13
14#include "core/file_sys/vfs_types.h"
15
16class QTreeView;
17class QGraphicsScene;
18class QStandardItem;
19class QStandardItemModel;
20
21namespace Ui {
22class ConfigurePerGameGeneral;
23}
24
25class ConfigurePerGameGeneral : public QDialog {
26 Q_OBJECT
27
28public:
29 explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id);
30 ~ConfigurePerGameGeneral() override;
31
32 /// Save all button configurations to settings file
33 void applyConfiguration();
34
35 void loadFromFile(FileSys::VirtualFile file);
36
37private:
38 std::unique_ptr<Ui::ConfigurePerGameGeneral> ui;
39 FileSys::VirtualFile file;
40 u64 title_id;
41
42 QVBoxLayout* layout;
43 QTreeView* tree_view;
44 QStandardItemModel* item_model;
45 QGraphicsScene* scene;
46
47 std::vector<QList<QStandardItem*>> list_items;
48
49 void loadConfiguration();
50};
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui
new file mode 100644
index 000000000..8fdd96fa4
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.ui
@@ -0,0 +1,276 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGameGeneral</class>
4 <widget class="QDialog" name="ConfigurePerGameGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>520</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigurePerGameGeneral</string>
15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>Info</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QGridLayout" name="gridLayout_2">
27 <item row="6" column="1" colspan="2">
28 <widget class="QLineEdit" name="display_filename">
29 <property name="enabled">
30 <bool>true</bool>
31 </property>
32 <property name="readOnly">
33 <bool>true</bool>
34 </property>
35 </widget>
36 </item>
37 <item row="0" column="1">
38 <widget class="QLineEdit" name="display_name">
39 <property name="enabled">
40 <bool>true</bool>
41 </property>
42 <property name="readOnly">
43 <bool>true</bool>
44 </property>
45 </widget>
46 </item>
47 <item row="1" column="0">
48 <widget class="QLabel" name="label_2">
49 <property name="text">
50 <string>Developer</string>
51 </property>
52 </widget>
53 </item>
54 <item row="5" column="1" colspan="2">
55 <widget class="QLineEdit" name="display_size">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="readOnly">
60 <bool>true</bool>
61 </property>
62 </widget>
63 </item>
64 <item row="0" column="0">
65 <widget class="QLabel" name="label">
66 <property name="text">
67 <string>Name</string>
68 </property>
69 </widget>
70 </item>
71 <item row="6" column="0">
72 <widget class="QLabel" name="label_7">
73 <property name="text">
74 <string>Filename</string>
75 </property>
76 </widget>
77 </item>
78 <item row="2" column="0">
79 <widget class="QLabel" name="label_3">
80 <property name="text">
81 <string>Version</string>
82 </property>
83 </widget>
84 </item>
85 <item row="4" column="0">
86 <widget class="QLabel" name="label_5">
87 <property name="text">
88 <string>Format</string>
89 </property>
90 </widget>
91 </item>
92 <item row="2" column="1">
93 <widget class="QLineEdit" name="display_version">
94 <property name="enabled">
95 <bool>true</bool>
96 </property>
97 <property name="readOnly">
98 <bool>true</bool>
99 </property>
100 </widget>
101 </item>
102 <item row="4" column="1">
103 <widget class="QLineEdit" name="display_format">
104 <property name="enabled">
105 <bool>true</bool>
106 </property>
107 <property name="readOnly">
108 <bool>true</bool>
109 </property>
110 </widget>
111 </item>
112 <item row="5" column="0">
113 <widget class="QLabel" name="label_6">
114 <property name="text">
115 <string>Size</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="1">
120 <widget class="QLineEdit" name="display_developer">
121 <property name="enabled">
122 <bool>true</bool>
123 </property>
124 <property name="readOnly">
125 <bool>true</bool>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="0">
130 <widget class="QLabel" name="label_4">
131 <property name="text">
132 <string>Title ID</string>
133 </property>
134 </widget>
135 </item>
136 <item row="3" column="1">
137 <widget class="QLineEdit" name="display_title_id">
138 <property name="enabled">
139 <bool>true</bool>
140 </property>
141 <property name="readOnly">
142 <bool>true</bool>
143 </property>
144 </widget>
145 </item>
146 <item row="0" column="2" rowspan="5">
147 <widget class="QGraphicsView" name="icon_view">
148 <property name="sizePolicy">
149 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
150 <horstretch>0</horstretch>
151 <verstretch>0</verstretch>
152 </sizepolicy>
153 </property>
154 <property name="minimumSize">
155 <size>
156 <width>128</width>
157 <height>128</height>
158 </size>
159 </property>
160 <property name="maximumSize">
161 <size>
162 <width>128</width>
163 <height>128</height>
164 </size>
165 </property>
166 <property name="verticalScrollBarPolicy">
167 <enum>Qt::ScrollBarAlwaysOff</enum>
168 </property>
169 <property name="horizontalScrollBarPolicy">
170 <enum>Qt::ScrollBarAlwaysOff</enum>
171 </property>
172 <property name="sizeAdjustPolicy">
173 <enum>QAbstractScrollArea::AdjustToContents</enum>
174 </property>
175 <property name="interactive">
176 <bool>false</bool>
177 </property>
178 </widget>
179 </item>
180 </layout>
181 </item>
182 </layout>
183 </widget>
184 </item>
185 <item>
186 <widget class="QGroupBox" name="PerformanceGroupBox">
187 <property name="title">
188 <string>Add-Ons</string>
189 </property>
190 <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
191 <item>
192 <widget class="QScrollArea" name="scrollArea">
193 <property name="widgetResizable">
194 <bool>true</bool>
195 </property>
196 <widget class="QWidget" name="scrollAreaWidgetContents">
197 <property name="geometry">
198 <rect>
199 <x>0</x>
200 <y>0</y>
201 <width>350</width>
202 <height>169</height>
203 </rect>
204 </property>
205 </widget>
206 </widget>
207 </item>
208 <item>
209 <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item>
215 <spacer name="verticalSpacer">
216 <property name="orientation">
217 <enum>Qt::Vertical</enum>
218 </property>
219 <property name="sizeType">
220 <enum>QSizePolicy::Fixed</enum>
221 </property>
222 <property name="sizeHint" stdset="0">
223 <size>
224 <width>20</width>
225 <height>40</height>
226 </size>
227 </property>
228 </spacer>
229 </item>
230 <item>
231 <widget class="QDialogButtonBox" name="buttonBox">
232 <property name="standardButtons">
233 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item>
239 </layout>
240 </widget>
241 <resources/>
242 <connections>
243 <connection>
244 <sender>buttonBox</sender>
245 <signal>accepted()</signal>
246 <receiver>ConfigurePerGameGeneral</receiver>
247 <slot>accept()</slot>
248 <hints>
249 <hint type="sourcelabel">
250 <x>269</x>
251 <y>567</y>
252 </hint>
253 <hint type="destinationlabel">
254 <x>269</x>
255 <y>294</y>
256 </hint>
257 </hints>
258 </connection>
259 <connection>
260 <sender>buttonBox</sender>
261 <signal>rejected()</signal>
262 <receiver>ConfigurePerGameGeneral</receiver>
263 <slot>reject()</slot>
264 <hints>
265 <hint type="sourcelabel">
266 <x>269</x>
267 <y>567</y>
268 </hint>
269 <hint type="destinationlabel">
270 <x>269</x>
271 <y>294</y>
272 </hint>
273 </hints>
274 </connection>
275 </connections>
276</ui>
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f9c18ede4..6b3a757e0 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -75,7 +75,7 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
75 return item_list; 75 return item_list;
76} 76}
77 77
78WaitTreeText::WaitTreeText(const QString& t) : text(t) {} 78WaitTreeText::WaitTreeText(QString t) : text(std::move(t)) {}
79WaitTreeText::~WaitTreeText() = default; 79WaitTreeText::~WaitTreeText() = default;
80 80
81QString WaitTreeText::GetText() const { 81QString WaitTreeText::GetText() const {
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 492fb6ac9..e639ef412 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -52,7 +52,7 @@ private:
52class WaitTreeText : public WaitTreeItem { 52class WaitTreeText : public WaitTreeItem {
53 Q_OBJECT 53 Q_OBJECT
54public: 54public:
55 explicit WaitTreeText(const QString& text); 55 explicit WaitTreeText(QString text);
56 ~WaitTreeText() override; 56 ~WaitTreeText() override;
57 57
58 QString GetText() const override; 58 QString GetText() const override;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b52a50915..8e9524fd6 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -333,6 +333,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
333 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 333 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
334 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 334 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
335 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 335 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
336 context_menu.addSeparator();
337 QAction* properties = context_menu.addAction(tr("Properties"));
336 338
337 open_save_location->setEnabled(program_id != 0); 339 open_save_location->setEnabled(program_id != 0);
338 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 340 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -346,6 +348,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
346 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); 348 connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
347 connect(navigate_to_gamedb_entry, &QAction::triggered, 349 connect(navigate_to_gamedb_entry, &QAction::triggered,
348 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); 350 [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
351 connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); });
349 352
350 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); 353 context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
351} 354}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 05e115e19..b317eb2fc 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -70,6 +70,7 @@ signals:
70 void CopyTIDRequested(u64 program_id); 70 void CopyTIDRequested(u64 program_id);
71 void NavigateToGamedbEntryRequested(u64 program_id, 71 void NavigateToGamedbEntryRequested(u64 program_id,
72 const CompatibilityList& compatibility_list); 72 const CompatibilityList& compatibility_list);
73 void OpenPerGameGeneralRequested(const std::string& file);
73 74
74private slots: 75private slots:
75 void onTextChanged(const QString& newText); 76 void onTextChanged(const QString& newText);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 9fd074223..b37710f59 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -62,7 +62,7 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
62 FileSys::VirtualFile update_raw; 62 FileSys::VirtualFile update_raw;
63 loader.ReadUpdateRaw(update_raw); 63 loader.ReadUpdateRaw(update_raw);
64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { 64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
65 const bool is_update = kv.first == "Update"; 65 const bool is_update = kv.first == "Update" || kv.first == "[D] Update";
66 if (!updatable && is_update) { 66 if (!updatable && is_update) {
67 continue; 67 continue;
68 } 68 }
@@ -99,12 +99,14 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
99 compatibility = it->second.first; 99 compatibility = it->second.first;
100 } 100 }
101 101
102 const auto file_type = loader.GetFileType();
103 const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type));
104
102 QList<QStandardItem*> list{ 105 QList<QStandardItem*> list{
103 new GameListItemPath( 106 new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name),
104 FormatGameName(path), icon, QString::fromStdString(name), 107 file_type_string, program_id),
105 QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType())), program_id),
106 new GameListItemCompat(compatibility), 108 new GameListItemCompat(compatibility),
107 new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType()))), 109 new GameListItem(file_type_string),
108 new GameListItemSize(FileUtil::GetSize(path)), 110 new GameListItemSize(FileUtil::GetSize(path)),
109 }; 111 };
110 112
@@ -196,12 +198,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
196 const bool is_dir = FileUtil::IsDirectory(physical_name); 198 const bool is_dir = FileUtil::IsDirectory(physical_name);
197 if (!is_dir && 199 if (!is_dir &&
198 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 200 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
199 std::unique_ptr<Loader::AppLoader> loader = 201 auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
200 Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 202 if (!loader) {
201 if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
202 loader->GetFileType() == Loader::FileType::Error) &&
203 !UISettings::values.show_unknown))
204 return true; 203 return true;
204 }
205
206 const auto file_type = loader->GetFileType();
207 if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) &&
208 !UISettings::values.show_unknown) {
209 return true;
210 }
205 211
206 std::vector<u8> icon; 212 std::vector<u8> icon;
207 const auto res1 = loader->ReadIcon(icon); 213 const auto res1 = loader->ReadIcon(icon);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 22c207a3a..90b212ba5 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -9,6 +9,7 @@
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "applets/software_keyboard.h" 11#include "applets/software_keyboard.h"
12#include "configuration/configure_per_general.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13#include "core/file_sys/vfs_real.h" 14#include "core/file_sys/vfs_real.h"
14#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
@@ -441,6 +442,8 @@ void GMainWindow::ConnectWidgetEvents() {
441 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 442 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
442 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 443 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
443 &GMainWindow::OnGameListNavigateToGamedbEntry); 444 &GMainWindow::OnGameListNavigateToGamedbEntry);
445 connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
446 &GMainWindow::OnGameListOpenPerGameProperties);
444 447
445 connect(this, &GMainWindow::EmulationStarting, render_window, 448 connect(this, &GMainWindow::EmulationStarting, render_window,
446 &GRenderWindow::OnEmulationStarting); 449 &GRenderWindow::OnEmulationStarting);
@@ -988,6 +991,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
988 QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); 991 QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory));
989} 992}
990 993
994void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
995 u64 title_id{};
996 const auto v_file = Core::GetGameFileFromPath(vfs, file);
997 const auto loader = Loader::GetLoader(v_file);
998 if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
999 QMessageBox::information(this, tr("Properties"),
1000 tr("The game properties could not be loaded."));
1001 return;
1002 }
1003
1004 ConfigurePerGameGeneral dialog(this, title_id);
1005 dialog.loadFromFile(v_file);
1006 auto result = dialog.exec();
1007 if (result == QDialog::Accepted) {
1008 dialog.applyConfiguration();
1009
1010 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1011 if (reload) {
1012 game_list->PopulateAsync(UISettings::values.gamedir,
1013 UISettings::values.gamedir_deepscan);
1014 }
1015
1016 config->Save();
1017 }
1018}
1019
991void GMainWindow::OnMenuLoadFile() { 1020void GMainWindow::OnMenuLoadFile() {
992 const QString extensions = 1021 const QString extensions =
993 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); 1022 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 674e73412..ca9c50367 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -168,6 +168,7 @@ private slots:
168 void OnGameListCopyTID(u64 program_id); 168 void OnGameListCopyTID(u64 program_id);
169 void OnGameListNavigateToGamedbEntry(u64 program_id, 169 void OnGameListNavigateToGamedbEntry(u64 program_id,
170 const CompatibilityList& compatibility_list); 170 const CompatibilityList& compatibility_list);
171 void OnGameListOpenPerGameProperties(const std::string& file);
171 void OnMenuLoadFile(); 172 void OnMenuLoadFile();
172 void OnMenuLoadFolder(); 173 void OnMenuLoadFolder();
173 void OnMenuInstallToNAND(); 174 void OnMenuInstallToNAND();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 097c1fbe3..fe0d1eebf 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <sstream>
6#include <SDL.h> 7#include <SDL.h>
7#include <inih/cpp/INIReader.h> 8#include <inih/cpp/INIReader.h>
8#include "common/file_util.h" 9#include "common/file_util.h"
@@ -369,6 +370,23 @@ void Config::ReadValues() {
369 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); 370 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
370 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); 371 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
371 372
373 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
374 std::stringstream ss(title_list);
375 std::string line;
376 while (std::getline(ss, line, '|')) {
377 const auto title_id = std::stoul(line, nullptr, 16);
378 const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
379
380 std::stringstream inner_ss(disabled_list);
381 std::string inner_line;
382 std::vector<std::string> out;
383 while (std::getline(inner_ss, inner_line, '|')) {
384 out.push_back(inner_line);
385 }
386
387 Settings::values.disabled_addons.insert_or_assign(title_id, out);
388 }
389
372 // Web Service 390 // Web Service
373 Settings::values.enable_telemetry = 391 Settings::values.enable_telemetry =
374 sdl2_config->GetBoolean("WebService", "enable_telemetry", true); 392 sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index d73669f36..0f3f8da50 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -221,5 +221,12 @@ web_api_url = https://api.yuzu-emu.org
221# See https://profile.yuzu-emu.org/ for more info 221# See https://profile.yuzu-emu.org/ for more info
222yuzu_username = 222yuzu_username =
223yuzu_token = 223yuzu_token =
224
225[AddOns]
226# Used to disable add-ons
227# List of title IDs of games that will have add-ons disabled (separated by '|'):
228title_ids =
229# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
230# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
224)"; 231)";
225} 232}