summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/stream.cpp7
-rw-r--r--src/common/string_util.h12
-rw-r--r--src/common/virtual_buffer.cpp2
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/core_timing.cpp16
-rw-r--r--src/core/core_timing.h7
-rw-r--r--src/core/device_memory.cpp5
-rw-r--r--src/core/device_memory.h8
-rw-r--r--src/core/file_sys/registered_cache.cpp101
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/xts_archive.cpp14
-rw-r--r--src/core/hardware_interrupt_manager.cpp4
-rw-r--r--src/core/hle/kernel/kernel.cpp2
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/time_manager.cpp10
-rw-r--r--src/core/hle/service/am/am.cpp1
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp61
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp53
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp17
-rw-r--r--src/core/hle/service/hid/hid.cpp8
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h30
-rw-r--r--src/core/hle/service/vi/vi.cpp4
-rw-r--r--src/core/memory/cheat_engine.cpp12
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/network/network.cpp2
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/tools/freezer.cpp11
-rw-r--r--src/core/tools/freezer.h2
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp191
-rw-r--r--src/input_common/gcadapter/gc_adapter.h44
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp6
-rw-r--r--src/input_common/udp/client.cpp1
-rw-r--r--src/tests/core/core_timing.cpp4
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h9
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp33
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp26
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp2
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp6
-rw-r--r--src/video_core/shader/decode/other.cpp3
-rw-r--r--src/video_core/shader/decode/video.cpp19
-rw-r--r--src/video_core/shader/decode/xmad.cpp15
-rw-r--r--src/video_core/shader/shader_ir.cpp4
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp74
-rw-r--r--src/yuzu/configuration/configuration_shared.h20
-rw-r--r--src/yuzu/configuration/configure_audio.cpp23
-rw-r--r--src/yuzu/configuration/configure_audio.h6
-rw-r--r--src/yuzu/configuration/configure_audio.ui151
-rw-r--r--src/yuzu/configuration/configure_debug.ui22
-rw-r--r--src/yuzu/configuration/configure_general.cpp37
-rw-r--r--src/yuzu/configuration/configure_general.h7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp54
-rw-r--r--src/yuzu/configuration/configure_graphics.h7
-rw-r--r--src/yuzu/configuration/configure_graphics.ui282
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp68
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h9
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui161
-rw-r--r--src/yuzu/configuration/configure_system.cpp115
-rw-r--r--src/yuzu/configuration/configure_system.h7
-rw-r--r--src/yuzu/configuration/configure_system.ui972
-rw-r--r--src/yuzu/debugger/wait_tree.cpp5
-rw-r--r--src/yuzu/game_list.cpp34
-rw-r--r--src/yuzu/game_list.h15
-rw-r--r--src/yuzu/main.cpp182
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/uisettings.cpp2
-rw-r--r--src/yuzu/uisettings.h2
83 files changed, 1707 insertions, 1391 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index f80ab92e4..7be5d5087 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -36,9 +36,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
36 ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_) 36 ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, 37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { 38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
39 39 release_event =
40 release_event = Core::Timing::CreateEvent( 40 Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
41 name, [this](u64, std::chrono::nanoseconds ns_late) { ReleaseActiveBuffer(ns_late); }); 41 ReleaseActiveBuffer(ns_late);
42 });
42} 43}
43 44
44void Stream::Play() { 45void Stream::Play() {
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 583fd05e6..023dff5dc 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -74,16 +74,4 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, 74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len); 75 std::size_t max_len);
76 76
77/**
78 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
79 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
80 * leaving only the path relative to the sources root.
81 *
82 * @param path The input file path as a null-terminated string
83 * @param root The name of the root source directory as a null-terminated string. Path up to and
84 * including the last occurrence of this name will be stripped
85 * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
86 */
87const char* TrimSourcePath(const char* path, const char* root = "src");
88
89} // namespace Common 77} // namespace Common
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp
index b426f4747..be5b67752 100644
--- a/src/common/virtual_buffer.cpp
+++ b/src/common/virtual_buffer.cpp
@@ -38,7 +38,7 @@ void* AllocateMemoryPages(std::size_t size) {
38 return base; 38 return base;
39} 39}
40 40
41void FreeMemoryPages(void* base, std::size_t size) { 41void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) {
42 if (!base) { 42 if (!base) {
43 return; 43 return;
44 } 44 }
diff --git a/src/core/core.cpp b/src/core/core.cpp
index e598c0e2b..42277e2cd 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -146,7 +146,7 @@ struct System::Impl {
146 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 146 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
147 LOG_DEBUG(HW_Memory, "initialized OK"); 147 LOG_DEBUG(HW_Memory, "initialized OK");
148 148
149 device_memory = std::make_unique<Core::DeviceMemory>(system); 149 device_memory = std::make_unique<Core::DeviceMemory>();
150 150
151 is_multicore = Settings::values.use_multi_core.GetValue(); 151 is_multicore = Settings::values.use_multi_core.GetValue();
152 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); 152 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index b5feb3f24..71af26ec5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -23,7 +23,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
23struct CoreTiming::Event { 23struct CoreTiming::Event {
24 u64 time; 24 u64 time;
25 u64 fifo_order; 25 u64 fifo_order;
26 u64 userdata; 26 std::uintptr_t user_data;
27 std::weak_ptr<EventType> type; 27 std::weak_ptr<EventType> type;
28 28
29 // Sort by time, unless the times are the same, in which case sort by 29 // Sort by time, unless the times are the same, in which case sort by
@@ -58,7 +58,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
58 event_fifo_id = 0; 58 event_fifo_id = 0;
59 shutting_down = false; 59 shutting_down = false;
60 ticks = 0; 60 ticks = 0;
61 const auto empty_timed_callback = [](u64, std::chrono::nanoseconds) {}; 61 const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 62 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
63 if (is_multicore) { 63 if (is_multicore) {
64 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); 64 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
@@ -107,22 +107,24 @@ bool CoreTiming::HasPendingEvents() const {
107} 107}
108 108
109void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, 109void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
110 const std::shared_ptr<EventType>& event_type, u64 userdata) { 110 const std::shared_ptr<EventType>& event_type,
111 std::uintptr_t user_data) {
111 { 112 {
112 std::scoped_lock scope{basic_lock}; 113 std::scoped_lock scope{basic_lock};
113 const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); 114 const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
114 115
115 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 116 event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
116 117
117 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 118 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
118 } 119 }
119 event.Set(); 120 event.Set();
120} 121}
121 122
122void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { 123void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
124 std::uintptr_t user_data) {
123 std::scoped_lock scope{basic_lock}; 125 std::scoped_lock scope{basic_lock};
124 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 126 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
125 return e.type.lock().get() == event_type.get() && e.userdata == userdata; 127 return e.type.lock().get() == event_type.get() && e.user_data == user_data;
126 }); 128 });
127 129
128 // Removing random items breaks the invariant so we have to re-establish it. 130 // Removing random items breaks the invariant so we have to re-establish it.
@@ -197,7 +199,7 @@ std::optional<s64> CoreTiming::Advance() {
197 199
198 if (const auto event_type{evt.type.lock()}) { 200 if (const auto event_type{evt.type.lock()}) {
199 event_type->callback( 201 event_type->callback(
200 evt.userdata, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); 202 evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
201 } 203 }
202 204
203 basic_lock.lock(); 205 basic_lock.lock();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 120c74e46..b0b6036e4 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -22,7 +22,8 @@
22namespace Core::Timing { 22namespace Core::Timing {
23 23
24/// A callback that may be scheduled for a particular core timing event. 24/// A callback that may be scheduled for a particular core timing event.
25using TimedCallback = std::function<void(u64 userdata, std::chrono::nanoseconds ns_late)>; 25using TimedCallback =
26 std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
26 27
27/// Contains the characteristics of a particular event. 28/// Contains the characteristics of a particular event.
28struct EventType { 29struct EventType {
@@ -94,9 +95,9 @@ public:
94 95
95 /// Schedules an event in core timing 96 /// Schedules an event in core timing
96 void ScheduleEvent(std::chrono::nanoseconds ns_into_future, 97 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
97 const std::shared_ptr<EventType>& event_type, u64 userdata = 0); 98 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
98 99
99 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); 100 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
100 101
101 /// We only permit one event of each type in the queue at a time. 102 /// We only permit one event of each type in the queue at a time.
102 void RemoveEvent(const std::shared_ptr<EventType>& event_type); 103 void RemoveEvent(const std::shared_ptr<EventType>& event_type);
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp
index 51097ced3..0c4b440ed 100644
--- a/src/core/device_memory.cpp
+++ b/src/core/device_memory.cpp
@@ -2,14 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h"
6#include "core/device_memory.h" 5#include "core/device_memory.h"
7#include "core/memory.h"
8 6
9namespace Core { 7namespace Core {
10 8
11DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} 9DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
12
13DeviceMemory::~DeviceMemory() = default; 10DeviceMemory::~DeviceMemory() = default;
14 11
15} // namespace Core 12} // namespace Core
diff --git a/src/core/device_memory.h b/src/core/device_memory.h
index 9efa088d0..5b1ae28f3 100644
--- a/src/core/device_memory.h
+++ b/src/core/device_memory.h
@@ -4,14 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/assert.h" 7#include "common/common_types.h"
8#include "common/common_funcs.h"
9#include "common/virtual_buffer.h" 8#include "common/virtual_buffer.h"
10 9
11namespace Core { 10namespace Core {
12 11
13class System;
14
15namespace DramMemoryMap { 12namespace DramMemoryMap {
16enum : u64 { 13enum : u64 {
17 Base = 0x80000000ULL, 14 Base = 0x80000000ULL,
@@ -26,7 +23,7 @@ enum : u64 {
26 23
27class DeviceMemory : NonCopyable { 24class DeviceMemory : NonCopyable {
28public: 25public:
29 explicit DeviceMemory(Core::System& system); 26 explicit DeviceMemory();
30 ~DeviceMemory(); 27 ~DeviceMemory();
31 28
32 template <typename T> 29 template <typename T>
@@ -45,7 +42,6 @@ public:
45 42
46private: 43private:
47 Common::VirtualBuffer<u8> buffer; 44 Common::VirtualBuffer<u8> buffer;
48 Core::System& system;
49}; 45};
50 46
51} // namespace Core 47} // namespace Core
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 37351c561..e94eed3b6 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -547,56 +547,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
547 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); 547 return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
548} 548}
549 549
550bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
551 const auto delete_nca = [this](const NcaID& id) {
552 const auto path = GetRelativePathFromNcaID(id, false, true, false);
553
554 if (dir->GetFileRelative(path) == nullptr) {
555 return false;
556 }
557
558 Core::Crypto::SHA256Hash hash{};
559 mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
560 const auto dirname = fmt::format("000000{:02X}", hash[0]);
561
562 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
563
564 const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
565
566 return res;
567 };
568
569 // If an entry exists in the registered cache, remove it
570 if (HasEntry(title_id, ContentRecordType::Meta)) {
571 LOG_INFO(Loader,
572 "Previously installed entry (v{}) for title_id={:016X} detected! "
573 "Attempting to remove...",
574 GetEntryVersion(title_id).value_or(0), title_id);
575 // Get all the ncas associated with the current CNMT and delete them
576 const auto meta_old_id =
577 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
578 const auto program_id =
579 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
580 const auto data_id =
581 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
582 const auto control_id =
583 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
584 const auto html_id =
585 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
586 const auto legal_id =
587 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
588
589 delete_nca(meta_old_id);
590 delete_nca(program_id);
591 delete_nca(data_id);
592 delete_nca(control_id);
593 delete_nca(html_id);
594 delete_nca(legal_id);
595 return true;
596 }
597 return false;
598}
599
600InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, 550InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
601 const VfsCopyFunction& copy) { 551 const VfsCopyFunction& copy) {
602 const auto ncas = nsp.GetNCAsCollapsed(); 552 const auto ncas = nsp.GetNCAsCollapsed();
@@ -692,6 +642,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
692 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); 642 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
693} 643}
694 644
645bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
646 const auto delete_nca = [this](const NcaID& id) {
647 const auto path = GetRelativePathFromNcaID(id, false, true, false);
648
649 const bool isFile = dir->GetFileRelative(path) != nullptr;
650 const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
651
652 if (isFile) {
653 return dir->DeleteFile(path);
654 } else if (isDir) {
655 return dir->DeleteSubdirectoryRecursive(path);
656 }
657
658 return false;
659 };
660
661 // If an entry exists in the registered cache, remove it
662 if (HasEntry(title_id, ContentRecordType::Meta)) {
663 LOG_INFO(Loader,
664 "Previously installed entry (v{}) for title_id={:016X} detected! "
665 "Attempting to remove...",
666 GetEntryVersion(title_id).value_or(0), title_id);
667
668 // Get all the ncas associated with the current CNMT and delete them
669 const auto meta_old_id =
670 GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
671 const auto program_id =
672 GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
673 const auto data_id =
674 GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
675 const auto control_id =
676 GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
677 const auto html_id =
678 GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
679 const auto legal_id =
680 GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
681
682 const auto deleted_meta = delete_nca(meta_old_id);
683 const auto deleted_program = delete_nca(program_id);
684 const auto deleted_data = delete_nca(data_id);
685 const auto deleted_control = delete_nca(control_id);
686 const auto deleted_html = delete_nca(html_id);
687 const auto deleted_legal = delete_nca(legal_id);
688
689 return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
690 deleted_control || deleted_html || deleted_legal);
691 }
692
693 return false;
694}
695
695InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, 696InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
696 bool overwrite_if_exists, 697 bool overwrite_if_exists,
697 std::optional<NcaID> override_id) { 698 std::optional<NcaID> override_id) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 29cf0d40c..ec1d54f27 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -155,9 +155,6 @@ public:
155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 155 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
156 std::optional<u64> title_id = {}) const override; 156 std::optional<u64> title_id = {}) const override;
157 157
158 // Removes an existing entry based on title id
159 bool RemoveExistingEntry(u64 title_id);
160
161 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 158 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
162 // there is a meta NCA and all of them are accessible. 159 // there is a meta NCA and all of them are accessible.
163 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, 160 InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
@@ -172,6 +169,9 @@ public:
172 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, 169 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
173 const VfsCopyFunction& copy = &VfsRawCopy); 170 const VfsCopyFunction& copy = &VfsRawCopy);
174 171
172 // Removes an existing entry based on title id
173 bool RemoveExistingEntry(u64 title_id) const;
174
175private: 175private:
176 template <typename T> 176 template <typename T>
177 void IterateAllMetadata(std::vector<T>& out, 177 void IterateAllMetadata(std::vector<T>& out,
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 86e06ccb9..81413c684 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
70NAX::~NAX() = default; 70NAX::~NAX() = default;
71 71
72Loader::ResultStatus NAX::Parse(std::string_view path) { 72Loader::ResultStatus NAX::Parse(std::string_view path) {
73 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) 73 if (file == nullptr) {
74 return Loader::ResultStatus::ErrorNullFile;
75 }
76 if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
74 return Loader::ResultStatus::ErrorBadNAXHeader; 77 return Loader::ResultStatus::ErrorBadNAXHeader;
75 78 }
76 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) 79 if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
77 return Loader::ResultStatus::ErrorBadNAXHeader; 80 return Loader::ResultStatus::ErrorBadNAXHeader;
78 81 }
79 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) 82 if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
80 return Loader::ResultStatus::ErrorIncorrectNAXFileSize; 83 return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
84 }
81 85
82 keys.DeriveSDSeedLazy(); 86 keys.DeriveSDSeedLazy();
83 std::array<Core::Crypto::Key256, 2> sd_keys{}; 87 std::array<Core::Crypto::Key256, 2> sd_keys{};
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
index efc1030c1..645f26e91 100644
--- a/src/core/hardware_interrupt_manager.cpp
+++ b/src/core/hardware_interrupt_manager.cpp
@@ -11,8 +11,8 @@
11namespace Core::Hardware { 11namespace Core::Hardware {
12 12
13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { 13InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
14 gpu_interrupt_event = 14 gpu_interrupt_event = Core::Timing::CreateEvent(
15 Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, std::chrono::nanoseconds) { 15 "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) {
16 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); 16 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
17 const u32 syncpt = static_cast<u32>(message >> 32); 17 const u32 syncpt = static_cast<u32>(message >> 32);
18 const u32 value = static_cast<u32>(message); 18 const u32 value = static_cast<u32>(message);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8dd4a2637..cabe8d418 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -145,7 +145,7 @@ struct KernelCore::Impl {
145 145
146 void InitializePreemption(KernelCore& kernel) { 146 void InitializePreemption(KernelCore& kernel) {
147 preemption_event = Core::Timing::CreateEvent( 147 preemption_event = Core::Timing::CreateEvent(
148 "PreemptionCallback", [this, &kernel](u64, std::chrono::nanoseconds) { 148 "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
149 { 149 {
150 SchedulerLock lock(kernel); 150 SchedulerLock lock(kernel);
151 global_scheduler.PreemptThreads(); 151 global_scheduler.PreemptThreads();
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index af22f4c33..7e6391c6c 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -33,8 +33,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
33 std::string name) { 33 std::string name) {
34 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; 34 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
35 35
36 session->request_event = Core::Timing::CreateEvent( 36 session->request_event =
37 name, [session](u64, std::chrono::nanoseconds) { session->CompleteSyncRequest(); }); 37 Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
38 session->CompleteSyncRequest();
39 });
38 session->name = std::move(name); 40 session->name = std::move(name);
39 session->parent = std::move(parent); 41 session->parent = std::move(parent);
40 42
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 88b01b751..95f2446c9 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -16,14 +16,14 @@ namespace Kernel {
16 16
17TimeManager::TimeManager(Core::System& system_) : system{system_} { 17TimeManager::TimeManager(Core::System& system_) : system{system_} {
18 time_manager_event_type = Core::Timing::CreateEvent( 18 time_manager_event_type = Core::Timing::CreateEvent(
19 "Kernel::TimeManagerCallback", [this](u64 thread_handle, std::chrono::nanoseconds) { 19 "Kernel::TimeManagerCallback",
20 SchedulerLock lock(system.Kernel()); 20 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
21 Handle proper_handle = static_cast<Handle>(thread_handle); 21 const SchedulerLock lock(system.Kernel());
22 const auto proper_handle = static_cast<Handle>(thread_handle);
22 if (cancelled_events[proper_handle]) { 23 if (cancelled_events[proper_handle]) {
23 return; 24 return;
24 } 25 }
25 std::shared_ptr<Thread> thread = 26 auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
26 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
27 thread->OnWakeUp(); 27 thread->OnWakeUp();
28 }); 28 });
29} 29}
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4e7a0bec9..ceed20609 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1405,7 +1405,6 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
1405 // Get supported languages from NACP, if possible 1405 // Get supported languages from NACP, if possible
1406 // Default to 0 (all languages supported) 1406 // Default to 0 (all languages supported)
1407 u32 supported_languages = 0; 1407 u32 supported_languages = 0;
1408 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
1409 1408
1410 const auto res = [this] { 1409 const auto res = [this] {
1411 const auto title_id = system.CurrentProcess()->GetTitleID(); 1410 const auto title_id = system.CurrentProcess()->GetTitleID();
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index fbe3686ae..289da2619 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -13,11 +13,23 @@
13 13
14namespace Service::AM::Applets { 14namespace Service::AM::Applets {
15 15
16namespace {
17enum class Request : u32 {
18 Finalize = 0x4,
19 SetUserWordInfo = 0x6,
20 SetCustomizeDic = 0x7,
21 Calc = 0xa,
22 SetCustomizedDictionaries = 0xb,
23 UnsetCustomizedDictionaries = 0xc,
24 UnknownD = 0xd,
25 UnknownE = 0xe,
26};
27constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8;
16constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; 28constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
17constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; 29constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
18constexpr std::size_t DEFAULT_MAX_LENGTH = 500; 30constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
19constexpr bool INTERACTIVE_STATUS_OK = false; 31constexpr bool INTERACTIVE_STATUS_OK = false;
20 32} // Anonymous namespace
21static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( 33static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
22 KeyboardConfig config, std::u16string initial_text) { 34 KeyboardConfig config, std::u16string initial_text) {
23 Core::Frontend::SoftwareKeyboardParameters params{}; 35 Core::Frontend::SoftwareKeyboardParameters params{};
@@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default;
47 59
48void SoftwareKeyboard::Initialize() { 60void SoftwareKeyboard::Initialize() {
49 complete = false; 61 complete = false;
62 is_inline = false;
50 initial_text.clear(); 63 initial_text.clear();
51 final_data.clear(); 64 final_data.clear();
52 65
@@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() {
56 ASSERT(keyboard_config_storage != nullptr); 69 ASSERT(keyboard_config_storage != nullptr);
57 const auto& keyboard_config = keyboard_config_storage->GetData(); 70 const auto& keyboard_config = keyboard_config_storage->GetData();
58 71
72 if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
73 is_inline = true;
74 return;
75 }
76
59 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); 77 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
60 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); 78 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
61 79
@@ -87,16 +105,32 @@ void SoftwareKeyboard::ExecuteInteractive() {
87 const auto storage = broker.PopInteractiveDataToApplet(); 105 const auto storage = broker.PopInteractiveDataToApplet();
88 ASSERT(storage != nullptr); 106 ASSERT(storage != nullptr);
89 const auto data = storage->GetData(); 107 const auto data = storage->GetData();
90 const auto status = static_cast<bool>(data[0]); 108 if (!is_inline) {
91 109 const auto status = static_cast<bool>(data[0]);
92 if (status == INTERACTIVE_STATUS_OK) { 110 if (status == INTERACTIVE_STATUS_OK) {
93 complete = true; 111 complete = true;
112 } else {
113 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
114 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
115 frontend.SendTextCheckDialog(
116 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
117 [this] { broker.SignalStateChanged(); });
118 }
94 } else { 119 } else {
95 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; 120 Request request{};
96 std::memcpy(string.data(), data.data() + 4, string.size() * 2); 121 std::memcpy(&request, data.data(), sizeof(Request));
97 frontend.SendTextCheckDialog( 122
98 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), 123 switch (request) {
99 [this] { broker.SignalStateChanged(); }); 124 case Request::Calc: {
125 broker.PushNormalDataFromApplet(
126 std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
127 broker.SignalStateChanged();
128 break;
129 }
130 default:
131 UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
132 break;
133 }
100 } 134 }
101} 135}
102 136
@@ -108,9 +142,10 @@ void SoftwareKeyboard::Execute() {
108 } 142 }
109 143
110 const auto parameters = ConvertToFrontendParameters(config, initial_text); 144 const auto parameters = ConvertToFrontendParameters(config, initial_text);
111 145 if (!is_inline) {
112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, 146 frontend.RequestText(
113 parameters); 147 [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters);
148 }
114} 149}
115 150
116void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { 151void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index ef4801fc6..5a3824b5a 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -78,6 +78,7 @@ private:
78 KeyboardConfig config; 78 KeyboardConfig config;
79 std::u16string initial_text; 79 std::u16string initial_text;
80 bool complete = false; 80 bool complete = false;
81 bool is_inline = false;
81 std::vector<u8> final_data; 82 std::vector<u8> final_data;
82}; 83};
83 84
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index cb35919e9..ad251ed4a 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing,
39 39
40 cur_entry.sampling_number = last_entry.sampling_number + 1; 40 cur_entry.sampling_number = last_entry.sampling_number + 1;
41 cur_entry.sampling_number2 = cur_entry.sampling_number; 41 cur_entry.sampling_number2 = cur_entry.sampling_number;
42 cur_entry.attribute.connected.Assign(1);
43 auto& pad = cur_entry.pad_state;
44 42
45 using namespace Settings::NativeButton; 43 if (Settings::values.debug_pad_enabled) {
46 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); 44 cur_entry.attribute.connected.Assign(1);
47 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); 45 auto& pad = cur_entry.pad_state;
48 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
49 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
50 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
51 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
52 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
53 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
54 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
55 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
56 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
57 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
60 46
61 const auto [stick_l_x_f, stick_l_y_f] = 47 using namespace Settings::NativeButton;
62 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 48 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
63 const auto [stick_r_x_f, stick_r_y_f] = 49 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); 50 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
65 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); 51 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
66 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); 52 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
67 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); 53 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
68 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); 54 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
55 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
56 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
57 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
59 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
60 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
61 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
62
63 const auto [stick_l_x_f, stick_l_y_f] =
64 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
65 const auto [stick_r_x_f, stick_r_y_f] =
66 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
67 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
68 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
69 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
70 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
71 }
69 72
70 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 73 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
71} 74}
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index feae89525..0b896d5ad 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -40,15 +40,16 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
40 40
41 cur_entry.key.fill(0); 41 cur_entry.key.fill(0);
42 cur_entry.modifier = 0; 42 cur_entry.modifier = 0;
43 43 if (Settings::values.keyboard_enabled) {
44 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { 44 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
45 cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); 45 cur_entry.key[i / KEYS_PER_BYTE] |=
46 } 46 (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE));
47 47 }
48 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { 48
49 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); 49 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
50 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
51 }
50 } 52 }
51
52 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 53 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
53} 54}
54 55
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 680290cbd..1e95b7580 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -77,8 +77,9 @@ IAppletResource::IAppletResource(Core::System& system)
77 77
78 // Register update callbacks 78 // Register update callbacks
79 pad_update_event = Core::Timing::CreateEvent( 79 pad_update_event = Core::Timing::CreateEvent(
80 "HID::UpdatePadCallback", [this](u64 userdata, std::chrono::nanoseconds ns_late) { 80 "HID::UpdatePadCallback",
81 UpdateControllers(userdata, ns_late); 81 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
82 UpdateControllers(user_data, ns_late);
82 }); 83 });
83 84
84 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 85 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
@@ -108,7 +109,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
108 rb.PushCopyObjects(shared_mem); 109 rb.PushCopyObjects(shared_mem);
109} 110}
110 111
111void IAppletResource::UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late) { 112void IAppletResource::UpdateControllers(std::uintptr_t user_data,
113 std::chrono::nanoseconds ns_late) {
112 auto& core_timing = system.CoreTiming(); 114 auto& core_timing = system.CoreTiming();
113 115
114 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 116 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c6f0a2584..efb07547f 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -64,7 +64,7 @@ private:
64 } 64 }
65 65
66 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 66 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
67 void UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late); 67 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
68 68
69 std::shared_ptr<Kernel::SharedMemory> shared_mem; 69 std::shared_ptr<Kernel::SharedMemory> shared_mem;
70 70
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 789856118..f644a460d 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -67,8 +67,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
67 67
68 // Schedule the screen composition events 68 // Schedule the screen composition events
69 composition_event = Core::Timing::CreateEvent( 69 composition_event = Core::Timing::CreateEvent(
70 "ScreenComposition", [this](u64, std::chrono::nanoseconds ns_late) { 70 "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
71 Lock(); 71 const auto guard = Lock();
72 Compose(); 72 Compose();
73 73
74 const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; 74 const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index e4959a9af..ff85cbba6 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -54,12 +54,12 @@ public:
54 /// Opens the specified display and returns the ID. 54 /// Opens the specified display and returns the ID.
55 /// 55 ///
56 /// If an invalid display name is provided, then an empty optional is returned. 56 /// If an invalid display name is provided, then an empty optional is returned.
57 std::optional<u64> OpenDisplay(std::string_view name); 57 [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
58 58
59 /// Creates a layer on the specified display and returns the layer ID. 59 /// Creates a layer on the specified display and returns the layer ID.
60 /// 60 ///
61 /// If an invalid display ID is specified, then an empty optional is returned. 61 /// If an invalid display ID is specified, then an empty optional is returned.
62 std::optional<u64> CreateLayer(u64 display_id); 62 [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
63 63
64 /// Closes a layer on all displays for the given layer ID. 64 /// Closes a layer on all displays for the given layer ID.
65 void CloseLayer(u64 layer_id); 65 void CloseLayer(u64 layer_id);
@@ -67,41 +67,39 @@ public:
67 /// Finds the buffer queue ID of the specified layer in the specified display. 67 /// Finds the buffer queue ID of the specified layer in the specified display.
68 /// 68 ///
69 /// If an invalid display ID or layer ID is provided, then an empty optional is returned. 69 /// If an invalid display ID or layer ID is provided, then an empty optional is returned.
70 std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; 70 [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const;
71 71
72 /// Gets the vsync event for the specified display. 72 /// Gets the vsync event for the specified display.
73 /// 73 ///
74 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
75 std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; 75 [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
76 76
77 /// Obtains a buffer queue identified by the ID. 77 /// Obtains a buffer queue identified by the ID.
78 BufferQueue& FindBufferQueue(u32 id); 78 [[nodiscard]] BufferQueue& FindBufferQueue(u32 id);
79 79
80 /// Obtains a buffer queue identified by the ID. 80 /// Obtains a buffer queue identified by the ID.
81 const BufferQueue& FindBufferQueue(u32 id) const; 81 [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const;
82 82
83 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 83 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
84 /// finished. 84 /// finished.
85 void Compose(); 85 void Compose();
86 86
87 s64 GetNextTicks() const; 87 [[nodiscard]] s64 GetNextTicks() const;
88 88
89 std::unique_lock<std::mutex> Lock() { 89 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; }
90 return std::unique_lock{*guard};
91 }
92 90
93private: 91 private :
94 /// Finds the display identified by the specified ID. 92 /// Finds the display identified by the specified ID.
95 VI::Display* FindDisplay(u64 display_id); 93 [[nodiscard]] VI::Display* FindDisplay(u64 display_id);
96 94
97 /// Finds the display identified by the specified ID. 95 /// Finds the display identified by the specified ID.
98 const VI::Display* FindDisplay(u64 display_id) const; 96 [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
99 97
100 /// Finds the layer identified by the specified ID in the desired display. 98 /// Finds the layer identified by the specified ID in the desired display.
101 VI::Layer* FindLayer(u64 display_id, u64 layer_id); 99 [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
102 100
103 /// Finds the layer identified by the specified ID in the desired display. 101 /// Finds the layer identified by the specified ID in the desired display.
104 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; 102 [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
105 103
106 static void VSyncThread(NVFlinger& nv_flinger); 104 static void VSyncThread(NVFlinger& nv_flinger);
107 105
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index ea7b4ae13..825d11a3f 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -511,7 +511,7 @@ private:
511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
512 static_cast<u32>(transaction), flags); 512 static_cast<u32>(transaction), flags);
513 513
514 nv_flinger->Lock(); 514 const auto guard = nv_flinger->Lock();
515 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 515 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
516 516
517 switch (transaction) { 517 switch (transaction) {
@@ -551,7 +551,7 @@ private:
551 [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 551 [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
552 Kernel::ThreadWakeupReason reason) { 552 Kernel::ThreadWakeupReason reason) {
553 // Repeat TransactParcel DequeueBuffer when a buffer is available 553 // Repeat TransactParcel DequeueBuffer when a buffer is available
554 nv_flinger->Lock(); 554 const auto guard = nv_flinger->Lock();
555 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 555 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
556 auto result = buffer_queue.DequeueBuffer(width, height); 556 auto result = buffer_queue.DequeueBuffer(width, height);
557 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 557 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index ced41b1fe..eeebdf02e 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -188,11 +188,11 @@ CheatEngine::~CheatEngine() {
188} 188}
189 189
190void CheatEngine::Initialize() { 190void CheatEngine::Initialize() {
191 event = Core::Timing::CreateEvent("CheatEngine::FrameCallback::" + 191 event = Core::Timing::CreateEvent(
192 Common::HexToString(metadata.main_nso_build_id), 192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
193 [this](u64 userdata, std::chrono::nanoseconds ns_late) { 193 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
194 FrameCallback(userdata, ns_late); 194 FrameCallback(user_data, ns_late);
195 }); 195 });
196 core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); 196 core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
197 197
198 metadata.process_id = system.CurrentProcess()->GetProcessID(); 198 metadata.process_id = system.CurrentProcess()->GetProcessID();
@@ -219,7 +219,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
219 219
220MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 220MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
221 221
222void CheatEngine::FrameCallback(u64, std::chrono::nanoseconds ns_late) { 222void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
223 if (is_pending_reload.exchange(false)) { 223 if (is_pending_reload.exchange(false)) {
224 vm.LoadProgram(cheats); 224 vm.LoadProgram(cheats);
225 } 225 }
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index d4068cf84..fa039a831 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -72,7 +72,7 @@ public:
72 void Reload(std::vector<CheatEntry> cheats); 72 void Reload(std::vector<CheatEntry> cheats);
73 73
74private: 74private:
75 void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); 75 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
76 76
77 DmntCheatVm vm; 77 DmntCheatVm vm;
78 CheatProcessMetadata metadata; 78 CheatProcessMetadata metadata;
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index fb21b0a47..56d173b5e 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -15,7 +15,9 @@
15#include <errno.h> 15#include <errno.h>
16#include <fcntl.h> 16#include <fcntl.h>
17#include <netdb.h> 17#include <netdb.h>
18#include <netinet/in.h>
18#include <poll.h> 19#include <poll.h>
20#include <sys/socket.h>
19#include <unistd.h> 21#include <unistd.h>
20#else 22#else
21#error "Unimplemented platform" 23#error "Unimplemented platform"
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 44252dd81..416b2d866 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -173,7 +173,6 @@ void RestoreGlobalState() {
173 values.use_assembly_shaders.SetGlobal(true); 173 values.use_assembly_shaders.SetGlobal(true);
174 values.use_asynchronous_shaders.SetGlobal(true); 174 values.use_asynchronous_shaders.SetGlobal(true);
175 values.use_fast_gpu_time.SetGlobal(true); 175 values.use_fast_gpu_time.SetGlobal(true);
176 values.force_30fps_mode.SetGlobal(true);
177 values.bg_red.SetGlobal(true); 176 values.bg_red.SetGlobal(true);
178 values.bg_green.SetGlobal(true); 177 values.bg_green.SetGlobal(true);
179 values.bg_blue.SetGlobal(true); 178 values.bg_blue.SetGlobal(true);
diff --git a/src/core/settings.h b/src/core/settings.h
index 386233fdf..bb145f193 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -435,7 +435,6 @@ struct Values {
435 Setting<bool> use_vsync; 435 Setting<bool> use_vsync;
436 Setting<bool> use_assembly_shaders; 436 Setting<bool> use_assembly_shaders;
437 Setting<bool> use_asynchronous_shaders; 437 Setting<bool> use_asynchronous_shaders;
438 Setting<bool> force_30fps_mode;
439 Setting<bool> use_fast_gpu_time; 438 Setting<bool> use_fast_gpu_time;
440 439
441 Setting<float> bg_red; 440 Setting<float> bg_red;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 27b894b51..2003e096f 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -55,10 +55,11 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
55 55
56Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) 56Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
57 : core_timing{core_timing_}, memory{memory_} { 57 : core_timing{core_timing_}, memory{memory_} {
58 event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", 58 event = Core::Timing::CreateEvent(
59 [this](u64 userdata, std::chrono::nanoseconds ns_late) { 59 "MemoryFreezer::FrameCallback",
60 FrameCallback(userdata, ns_late); 60 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
61 }); 61 FrameCallback(user_data, ns_late);
62 });
62 core_timing.ScheduleEvent(memory_freezer_ns, event); 63 core_timing.ScheduleEvent(memory_freezer_ns, event);
63} 64}
64 65
@@ -159,7 +160,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
159 return entries; 160 return entries;
160} 161}
161 162
162void Freezer::FrameCallback(u64, std::chrono::nanoseconds ns_late) { 163void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
163 if (!IsActive()) { 164 if (!IsActive()) {
164 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 165 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
165 return; 166 return;
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 8438783d5..2b2326bc4 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -73,7 +73,7 @@ public:
73 std::vector<Entry> GetEntries() const; 73 std::vector<Entry> GetEntries() const;
74 74
75private: 75private:
76 void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); 76 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
77 void FillEntryReads(); 77 void FillEntryReads();
78 78
79 std::atomic_bool active{false}; 79 std::atomic_bool active{false};
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 898a278a9..74759ea7d 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -24,12 +24,9 @@ Adapter::Adapter() {
24 } 24 }
25 LOG_INFO(Input, "GC Adapter Initialization started"); 25 LOG_INFO(Input, "GC Adapter Initialization started");
26 26
27 current_status = NO_ADAPTER_DETECTED;
28 get_origin.fill(true);
29
30 const int init_res = libusb_init(&libusb_ctx); 27 const int init_res = libusb_init(&libusb_ctx);
31 if (init_res == LIBUSB_SUCCESS) { 28 if (init_res == LIBUSB_SUCCESS) {
32 StartScanThread(); 29 Setup();
33 } else { 30 } else {
34 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res); 31 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
35 } 32 }
@@ -37,9 +34,9 @@ Adapter::Adapter() {
37 34
38GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { 35GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
39 GCPadStatus pad = {}; 36 GCPadStatus pad = {};
37 const std::size_t offset = 1 + (9 * port);
40 38
41 ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4); 39 adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
42 adapter_controllers_status[port] = type;
43 40
44 static constexpr std::array<PadButton, 8> b1_buttons{ 41 static constexpr std::array<PadButton, 8> b1_buttons{
45 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X, 42 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
@@ -54,14 +51,19 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& ad
54 PadButton::PAD_TRIGGER_L, 51 PadButton::PAD_TRIGGER_L,
55 }; 52 };
56 53
54 static constexpr std::array<PadAxes, 6> axes{
55 PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
56 PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
57 };
58
57 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) { 59 if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
58 // Controller may have been disconnected, recalibrate if reconnected. 60 // Controller may have been disconnected, recalibrate if reconnected.
59 get_origin[port] = true; 61 get_origin[port] = true;
60 } 62 }
61 63
62 if (adapter_controllers_status[port] != ControllerTypes::None) { 64 if (adapter_controllers_status[port] != ControllerTypes::None) {
63 const u8 b1 = adapter_payload[1 + (9 * port) + 1]; 65 const u8 b1 = adapter_payload[offset + 1];
64 const u8 b2 = adapter_payload[1 + (9 * port) + 2]; 66 const u8 b2 = adapter_payload[offset + 2];
65 67
66 for (std::size_t i = 0; i < b1_buttons.size(); ++i) { 68 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
67 if ((b1 & (1U << i)) != 0) { 69 if ((b1 & (1U << i)) != 0) {
@@ -74,21 +76,13 @@ GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& ad
74 pad.button |= static_cast<u16>(b2_buttons[j]); 76 pad.button |= static_cast<u16>(b2_buttons[j]);
75 } 77 }
76 } 78 }
77 79 for (PadAxes axis : axes) {
78 pad.stick_x = adapter_payload[1 + (9 * port) + 3]; 80 const std::size_t index = static_cast<std::size_t>(axis);
79 pad.stick_y = adapter_payload[1 + (9 * port) + 4]; 81 pad.axis_values[index] = adapter_payload[offset + 3 + index];
80 pad.substick_x = adapter_payload[1 + (9 * port) + 5]; 82 }
81 pad.substick_y = adapter_payload[1 + (9 * port) + 6];
82 pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
83 pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
84 83
85 if (get_origin[port]) { 84 if (get_origin[port]) {
86 origin_status[port].stick_x = pad.stick_x; 85 origin_status[port].axis_values = pad.axis_values;
87 origin_status[port].stick_y = pad.stick_y;
88 origin_status[port].substick_x = pad.substick_x;
89 origin_status[port].substick_y = pad.substick_y;
90 origin_status[port].trigger_left = pad.trigger_left;
91 origin_status[port].trigger_right = pad.trigger_right;
92 get_origin[port] = false; 86 get_origin[port] = false;
93 } 87 }
94 } 88 }
@@ -101,82 +95,47 @@ void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
101 state.buttons.insert_or_assign(button_value, pad.button & button_value); 95 state.buttons.insert_or_assign(button_value, pad.button & button_value);
102 } 96 }
103 97
104 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x); 98 for (size_t i = 0; i < pad.axis_values.size(); ++i) {
105 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y); 99 state.axes.insert_or_assign(static_cast<u8>(i), pad.axis_values[i]);
106 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x); 100 }
107 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
108 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
109 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
110} 101}
111 102
112void Adapter::Read() { 103void Adapter::Read() {
113 LOG_DEBUG(Input, "GC Adapter Read() thread started"); 104 LOG_DEBUG(Input, "GC Adapter Read() thread started");
114 105
115 int payload_size_in, payload_size_copy; 106 int payload_size;
116 std::array<u8, 37> adapter_payload; 107 std::array<u8, 37> adapter_payload;
117 std::array<u8, 37> adapter_payload_copy;
118 std::array<GCPadStatus, 4> pads; 108 std::array<GCPadStatus, 4> pads;
119 109
120 while (adapter_thread_running) { 110 while (adapter_thread_running) {
121 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), 111 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
122 sizeof(adapter_payload), &payload_size_in, 16); 112 sizeof(adapter_payload), &payload_size, 16);
123 payload_size_copy = 0;
124 // this mutex might be redundant?
125 {
126 std::lock_guard<std::mutex> lk(s_mutex);
127 std::copy(std::begin(adapter_payload), std::end(adapter_payload),
128 std::begin(adapter_payload_copy));
129 payload_size_copy = payload_size_in;
130 }
131 113
132 if (payload_size_copy != sizeof(adapter_payload_copy) || 114 if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
133 adapter_payload_copy[0] != LIBUSB_DT_HID) { 115 LOG_ERROR(Input,
134 LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy, 116 "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
135 adapter_payload_copy[0]); 117 payload_size, adapter_payload[0]);
136 adapter_thread_running = false; // error reading from adapter, stop reading. 118 adapter_thread_running = false; // error reading from adapter, stop reading.
137 break; 119 break;
138 } 120 }
139 for (std::size_t port = 0; port < pads.size(); ++port) { 121 for (std::size_t port = 0; port < pads.size(); ++port) {
140 pads[port] = GetPadStatus(port, adapter_payload_copy); 122 pads[port] = GetPadStatus(port, adapter_payload);
141 if (DeviceConnected(port) && configuring) { 123 if (DeviceConnected(port) && configuring) {
142 if (pads[port].button != 0) { 124 if (pads[port].button != 0) {
143 pad_queue[port].Push(pads[port]); 125 pad_queue[port].Push(pads[port]);
144 } 126 }
145 127
146 // Accounting for a threshold here because of some controller variance 128 // Accounting for a threshold here to ensure an intentional press
147 if (pads[port].stick_x > origin_status[port].stick_x + pads[port].THRESHOLD || 129 for (size_t i = 0; i < pads[port].axis_values.size(); ++i) {
148 pads[port].stick_x < origin_status[port].stick_x - pads[port].THRESHOLD) { 130 const u8 value = pads[port].axis_values[i];
149 pads[port].axis = GCAdapter::PadAxes::StickX; 131 const u8 origin = origin_status[port].axis_values[i];
150 pads[port].axis_value = pads[port].stick_x; 132
151 pad_queue[port].Push(pads[port]); 133 if (value > origin + pads[port].THRESHOLD ||
152 } 134 value < origin - pads[port].THRESHOLD) {
153 if (pads[port].stick_y > origin_status[port].stick_y + pads[port].THRESHOLD || 135 pads[port].axis = static_cast<PadAxes>(i);
154 pads[port].stick_y < origin_status[port].stick_y - pads[port].THRESHOLD) { 136 pads[port].axis_value = pads[port].axis_values[i];
155 pads[port].axis = GCAdapter::PadAxes::StickY; 137 pad_queue[port].Push(pads[port]);
156 pads[port].axis_value = pads[port].stick_y; 138 }
157 pad_queue[port].Push(pads[port]);
158 }
159 if (pads[port].substick_x > origin_status[port].substick_x + pads[port].THRESHOLD ||
160 pads[port].substick_x < origin_status[port].substick_x - pads[port].THRESHOLD) {
161 pads[port].axis = GCAdapter::PadAxes::SubstickX;
162 pads[port].axis_value = pads[port].substick_x;
163 pad_queue[port].Push(pads[port]);
164 }
165 if (pads[port].substick_y > origin_status[port].substick_y + pads[port].THRESHOLD ||
166 pads[port].substick_y < origin_status[port].substick_y - pads[port].THRESHOLD) {
167 pads[port].axis = GCAdapter::PadAxes::SubstickY;
168 pads[port].axis_value = pads[port].substick_y;
169 pad_queue[port].Push(pads[port]);
170 }
171 if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
172 pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
173 pads[port].axis_value = pads[port].trigger_left;
174 pad_queue[port].Push(pads[port]);
175 }
176 if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
177 pads[port].axis = GCAdapter::PadAxes::TriggerRight;
178 pads[port].axis_value = pads[port].trigger_right;
179 pad_queue[port].Push(pads[port]);
180 } 139 }
181 } 140 }
182 PadToState(pads[port], state[port]); 141 PadToState(pads[port], state[port]);
@@ -185,42 +144,11 @@ void Adapter::Read() {
185 } 144 }
186} 145}
187 146
188void Adapter::ScanThreadFunc() {
189 LOG_INFO(Input, "GC Adapter scanning thread started");
190
191 while (detect_thread_running) {
192 if (usb_adapter_handle == nullptr) {
193 std::lock_guard<std::mutex> lk(initialization_mutex);
194 Setup();
195 }
196 std::this_thread::sleep_for(std::chrono::milliseconds(500));
197 }
198}
199
200void Adapter::StartScanThread() {
201 if (detect_thread_running) {
202 return;
203 }
204 if (!libusb_ctx) {
205 return;
206 }
207
208 detect_thread_running = true;
209 detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
210}
211
212void Adapter::StopScanThread() {
213 detect_thread_running = false;
214 detect_thread.join();
215}
216
217void Adapter::Setup() { 147void Adapter::Setup() {
218 // Reset the error status in case the adapter gets unplugged 148 // Initialize all controllers as unplugged
219 if (current_status < 0) {
220 current_status = NO_ADAPTER_DETECTED;
221 }
222
223 adapter_controllers_status.fill(ControllerTypes::None); 149 adapter_controllers_status.fill(ControllerTypes::None);
150 // Initialize all ports to store axis origin values
151 get_origin.fill(true);
224 152
225 // pointer to list of connected usb devices 153 // pointer to list of connected usb devices
226 libusb_device** devices{}; 154 libusb_device** devices{};
@@ -229,8 +157,6 @@ void Adapter::Setup() {
229 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices); 157 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
230 if (device_count < 0) { 158 if (device_count < 0) {
231 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count); 159 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
232 detect_thread_running = false; // Stop the loop constantly checking for gc adapter
233 // TODO: For hotplug+gc adapter checkbox implementation, revert this.
234 return; 160 return;
235 } 161 }
236 162
@@ -244,9 +170,6 @@ void Adapter::Setup() {
244 } 170 }
245 libusb_free_device_list(devices, 1); 171 libusb_free_device_list(devices, 1);
246 } 172 }
247 // Break out of the ScanThreadFunc() loop that is constantly looking for the device
248 // Assumes user has GC adapter plugged in before launch to use the adapter
249 detect_thread_running = false;
250} 173}
251 174
252bool Adapter::CheckDeviceAccess(libusb_device* device) { 175bool Adapter::CheckDeviceAccess(libusb_device* device) {
@@ -331,32 +254,23 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
331 sizeof(clear_payload), nullptr, 16); 254 sizeof(clear_payload), nullptr, 16);
332 255
333 adapter_thread_running = true; 256 adapter_thread_running = true;
334 current_status = ADAPTER_DETECTED; 257 adapter_input_thread = std::thread(&Adapter::Read, this);
335 adapter_input_thread = std::thread([=] { Read(); }); // Read input
336} 258}
337 259
338Adapter::~Adapter() { 260Adapter::~Adapter() {
339 StopScanThread();
340 Reset(); 261 Reset();
341} 262}
342 263
343void Adapter::Reset() { 264void Adapter::Reset() {
344 std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
345 if (!lock.try_lock()) {
346 return;
347 }
348 if (current_status != ADAPTER_DETECTED) {
349 return;
350 }
351
352 if (adapter_thread_running) { 265 if (adapter_thread_running) {
353 adapter_thread_running = false; 266 adapter_thread_running = false;
354 } 267 }
355 adapter_input_thread.join(); 268 if (adapter_input_thread.joinable()) {
269 adapter_input_thread.join();
270 }
356 271
357 adapter_controllers_status.fill(ControllerTypes::None); 272 adapter_controllers_status.fill(ControllerTypes::None);
358 get_origin.fill(true); 273 get_origin.fill(true);
359 current_status = NO_ADAPTER_DETECTED;
360 274
361 if (usb_adapter_handle) { 275 if (usb_adapter_handle) {
362 libusb_release_interface(usb_adapter_handle, 1); 276 libusb_release_interface(usb_adapter_handle, 1);
@@ -409,24 +323,7 @@ const std::array<GCState, 4>& Adapter::GetPadState() const {
409} 323}
410 324
411int Adapter::GetOriginValue(int port, int axis) const { 325int Adapter::GetOriginValue(int port, int axis) const {
412 const auto& status = origin_status[port]; 326 return origin_status[port].axis_values[axis];
413
414 switch (static_cast<PadAxes>(axis)) {
415 case PadAxes::StickX:
416 return status.stick_x;
417 case PadAxes::StickY:
418 return status.stick_y;
419 case PadAxes::SubstickX:
420 return status.substick_x;
421 case PadAxes::SubstickY:
422 return status.substick_y;
423 case PadAxes::TriggerLeft:
424 return status.trigger_left;
425 case PadAxes::TriggerRight:
426 return status.trigger_right;
427 default:
428 return 0;
429 }
430} 327}
431 328
432} // namespace GCAdapter 329} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index 3586c8bda..bed81915c 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -47,24 +47,10 @@ enum class PadAxes : u8 {
47}; 47};
48 48
49struct GCPadStatus { 49struct GCPadStatus {
50 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits 50 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
51 u8 stick_x{}; // 0 <= stick_x <= 255 51
52 u8 stick_y{}; // 0 <= stick_y <= 255 52 std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes
53 u8 substick_x{}; // 0 <= substick_x <= 255 53 static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
54 u8 substick_y{}; // 0 <= substick_y <= 255
55 u8 trigger_left{}; // 0 <= trigger_left <= 255
56 u8 trigger_right{}; // 0 <= trigger_right <= 255
57
58 static constexpr u8 MAIN_STICK_CENTER_X = 0x80;
59 static constexpr u8 MAIN_STICK_CENTER_Y = 0x80;
60 static constexpr u8 MAIN_STICK_RADIUS = 0x7f;
61 static constexpr u8 C_STICK_CENTER_X = 0x80;
62 static constexpr u8 C_STICK_CENTER_Y = 0x80;
63 static constexpr u8 C_STICK_RADIUS = 0x7f;
64 static constexpr u8 THRESHOLD = 10;
65
66 // 256/4, at least a quarter press to count as a press. For polling mostly
67 static constexpr u8 TRIGGER_THRESHOLD = 64;
68 54
69 u8 port{}; 55 u8 port{};
70 PadAxes axis{PadAxes::Undefined}; 56 PadAxes axis{PadAxes::Undefined};
@@ -78,11 +64,6 @@ struct GCState {
78 64
79enum class ControllerTypes { None, Wired, Wireless }; 65enum class ControllerTypes { None, Wired, Wireless };
80 66
81enum {
82 NO_ADAPTER_DETECTED = 0,
83 ADAPTER_DETECTED = 1,
84};
85
86class Adapter { 67class Adapter {
87public: 68public:
88 /// Initialize the GC Adapter capture and read sequence 69 /// Initialize the GC Adapter capture and read sequence
@@ -111,12 +92,6 @@ private:
111 void PadToState(const GCPadStatus& pad, GCState& state); 92 void PadToState(const GCPadStatus& pad, GCState& state);
112 93
113 void Read(); 94 void Read();
114 void ScanThreadFunc();
115 /// Begin scanning for the GC Adapter.
116 void StartScanThread();
117
118 /// Stop scanning for the adapter
119 void StopScanThread();
120 95
121 /// Resets status of device connected to port 96 /// Resets status of device connected to port
122 void ResetDeviceType(std::size_t port); 97 void ResetDeviceType(std::size_t port);
@@ -133,19 +108,11 @@ private:
133 /// For use in initialization, querying devices to find the adapter 108 /// For use in initialization, querying devices to find the adapter
134 void Setup(); 109 void Setup();
135 110
136 int current_status = NO_ADAPTER_DETECTED;
137 libusb_device_handle* usb_adapter_handle = nullptr; 111 libusb_device_handle* usb_adapter_handle = nullptr;
138 std::array<ControllerTypes, 4> adapter_controllers_status{};
139
140 std::mutex s_mutex;
141 112
142 std::thread adapter_input_thread; 113 std::thread adapter_input_thread;
143 bool adapter_thread_running; 114 bool adapter_thread_running;
144 115
145 std::mutex initialization_mutex;
146 std::thread detect_thread;
147 bool detect_thread_running = false;
148
149 libusb_context* libusb_ctx; 116 libusb_context* libusb_ctx;
150 117
151 u8 input_endpoint = 0; 118 u8 input_endpoint = 0;
@@ -153,10 +120,11 @@ private:
153 120
154 bool configuring = false; 121 bool configuring = false;
155 122
156 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
157 std::array<GCState, 4> state; 123 std::array<GCState, 4> state;
158 std::array<bool, 4> get_origin; 124 std::array<bool, 4> get_origin;
159 std::array<GCPadStatus, 4> origin_status; 125 std::array<GCPadStatus, 4> origin_status;
126 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
127 std::array<ControllerTypes, 4> adapter_controllers_status{};
160}; 128};
161 129
162} // namespace GCAdapter 130} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 96e22d3ad..f45983f3f 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -76,8 +76,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
76 76
77 // button is not an axis/stick button 77 // button is not an axis/stick button
78 if (button_id != PAD_STICK_ID) { 78 if (button_id != PAD_STICK_ID) {
79 auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); 79 return std::make_unique<GCButton>(port, button_id, adapter.get());
80 return std::move(button);
81 } 80 }
82 81
83 // For Axis buttons, used by the binary sticks. 82 // For Axis buttons, used by the binary sticks.
@@ -264,7 +263,8 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
264 if (analog_x_axis == -1) { 263 if (analog_x_axis == -1) {
265 analog_x_axis = axis; 264 analog_x_axis = axis;
266 controller_number = static_cast<int>(port); 265 controller_number = static_cast<int>(port);
267 } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { 266 } else if (analog_y_axis == -1 && analog_x_axis != axis &&
267 controller_number == static_cast<int>(port)) {
268 analog_y_axis = axis; 268 analog_y_axis = axis;
269 } 269 }
270 } 270 }
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index e63c73c4f..6c95a8b42 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -9,7 +9,6 @@
9#include <functional> 9#include <functional>
10#include <thread> 10#include <thread>
11#include <boost/asio.hpp> 11#include <boost/asio.hpp>
12#include <boost/bind.hpp>
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14#include "input_common/udp/client.h" 13#include "input_common/udp/client.h"
15#include "input_common/udp/protocol.h" 14#include "input_common/udp/protocol.h"
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 244463a47..022b26e6d 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -25,10 +25,10 @@ std::bitset<CB_IDS.size()> callbacks_ran_flags;
25u64 expected_callback = 0; 25u64 expected_callback = 0;
26 26
27template <unsigned int IDX> 27template <unsigned int IDX>
28void HostCallbackTemplate(u64 userdata, std::chrono::nanoseconds ns_late) { 28void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
29 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 29 static_assert(IDX < CB_IDS.size(), "IDX out of range");
30 callbacks_ran_flags.set(IDX); 30 callbacks_ran_flags.set(IDX);
31 REQUIRE(CB_IDS[IDX] == userdata); 31 REQUIRE(CB_IDS[IDX] == user_data);
32 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); 32 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
33 delays[IDX] = ns_late.count(); 33 delays[IDX] = ns_late.count();
34 ++expected_callback; 34 ++expected_callback;
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index dd7ce8c99..b5dc68902 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -524,11 +524,8 @@ private:
524 void MarkRegionAsWritten(VAddr start, VAddr end) { 524 void MarkRegionAsWritten(VAddr start, VAddr end) {
525 const u64 page_end = end >> WRITE_PAGE_BIT; 525 const u64 page_end = end >> WRITE_PAGE_BIT;
526 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) { 526 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
527 auto it = written_pages.find(page_start); 527 if (const auto [it, inserted] = written_pages.emplace(page_start, 1); !inserted) {
528 if (it != written_pages.end()) { 528 ++it->second;
529 it->second = it->second + 1;
530 } else {
531 written_pages.insert_or_assign(page_start, 1);
532 } 529 }
533 } 530 }
534 } 531 }
@@ -539,7 +536,7 @@ private:
539 auto it = written_pages.find(page_start); 536 auto it = written_pages.find(page_start);
540 if (it != written_pages.end()) { 537 if (it != written_pages.end()) {
541 if (it->second > 1) { 538 if (it->second > 1) {
542 it->second = it->second - 1; 539 --it->second;
543 } else { 540 } else {
544 written_pages.erase(it); 541 written_pages.erase(it);
545 } 542 }
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 19a34c402..ebfc7b0c7 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -252,7 +252,7 @@ public:
252 const Tegra::DmaPusher& DmaPusher() const; 252 const Tegra::DmaPusher& DmaPusher() const;
253 253
254 struct Regs { 254 struct Regs {
255 static constexpr size_t NUM_REGS = 0x100; 255 static constexpr size_t NUM_REGS = 0x40;
256 256
257 union { 257 union {
258 struct { 258 struct {
@@ -271,7 +271,7 @@ public:
271 u32 semaphore_trigger; 271 u32 semaphore_trigger;
272 INSERT_UNION_PADDING_WORDS(0xC); 272 INSERT_UNION_PADDING_WORDS(0xC);
273 273
274 // The puser and the puller share the reference counter, the pusher only has read 274 // The pusher and the puller share the reference counter, the pusher only has read
275 // access 275 // access
276 u32 reference_count; 276 u32 reference_count;
277 INSERT_UNION_PADDING_WORDS(0x5); 277 INSERT_UNION_PADDING_WORDS(0x5);
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 738c6f0c1..bf761abf2 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -44,9 +44,9 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
44 dma_pusher.DispatchCalls(); 44 dma_pusher.DispatchCalls();
45 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { 45 } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
46 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 46 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
47 } else if (const auto data = std::get_if<OnCommandListEndCommand>(&next.data)) { 47 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
48 renderer.Rasterizer().ReleaseFences(); 48 renderer.Rasterizer().ReleaseFences();
49 } else if (const auto data = std::get_if<GPUTickCommand>(&next.data)) { 49 } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
50 system.GPU().TickWork(); 50 system.GPU().TickWork();
51 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { 51 } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
52 renderer.Rasterizer().FlushRegion(data->addr, data->size); 52 renderer.Rasterizer().FlushRegion(data->addr, data->size);
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index 07292702f..c1b9e4ad9 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -419,7 +419,6 @@ void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() {
419 419
420void MacroJITx64Impl::Compile() { 420void MacroJITx64Impl::Compile() {
421 MICROPROFILE_SCOPE(MacroJitCompile); 421 MICROPROFILE_SCOPE(MacroJitCompile);
422 bool keep_executing = true;
423 labels.fill(Xbyak::Label()); 422 labels.fill(Xbyak::Label());
424 423
425 Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8); 424 Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
index 4489abf61..b7e9ed2e9 100644
--- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
@@ -913,11 +913,19 @@ void ARBDecompiler::DeclareCompute() {
913 const ComputeInfo& info = registry.GetComputeInfo(); 913 const ComputeInfo& info = registry.GetComputeInfo();
914 AddLine("GROUP_SIZE {} {} {};", info.workgroup_size[0], info.workgroup_size[1], 914 AddLine("GROUP_SIZE {} {} {};", info.workgroup_size[0], info.workgroup_size[1],
915 info.workgroup_size[2]); 915 info.workgroup_size[2]);
916 if (info.shared_memory_size_in_words > 0) { 916 if (info.shared_memory_size_in_words == 0) {
917 const u32 size_in_bytes = info.shared_memory_size_in_words * 4; 917 return;
918 AddLine("SHARED_MEMORY {};", size_in_bytes); 918 }
919 AddLine("SHARED shared_mem[] = {{program.sharedmem}};"); 919 const u32 limit = device.GetMaxComputeSharedMemorySize();
920 u32 size_in_bytes = info.shared_memory_size_in_words * 4;
921 if (size_in_bytes > limit) {
922 LOG_ERROR(Render_OpenGL, "Shared memory size {} is clamped to host's limit {}",
923 size_in_bytes, limit);
924 size_in_bytes = limit;
920 } 925 }
926
927 AddLine("SHARED_MEMORY {};", size_in_bytes);
928 AddLine("SHARED shared_mem[] = {{program.sharedmem}};");
921} 929}
922 930
923void ARBDecompiler::DeclareInputAttributes() { 931void ARBDecompiler::DeclareInputAttributes() {
@@ -1283,13 +1291,6 @@ std::string ARBDecompiler::Visit(const Node& node) {
1283 return "{0, 0, 0, 0}.x"; 1291 return "{0, 0, 0, 0}.x";
1284 } 1292 }
1285 1293
1286 const auto buffer_index = [this, &abuf]() -> std::string {
1287 if (stage != ShaderType::Geometry) {
1288 return "";
1289 }
1290 return fmt::format("[{}]", Visit(abuf->GetBuffer()));
1291 };
1292
1293 const Attribute::Index index = abuf->GetIndex(); 1294 const Attribute::Index index = abuf->GetIndex();
1294 const u32 element = abuf->GetElement(); 1295 const u32 element = abuf->GetElement();
1295 const char swizzle = Swizzle(element); 1296 const char swizzle = Swizzle(element);
@@ -1395,7 +1396,7 @@ std::string ARBDecompiler::Visit(const Node& node) {
1395 return {}; 1396 return {};
1396 } 1397 }
1397 1398
1398 if (const auto cmt = std::get_if<CommentNode>(&*node)) { 1399 if ([[maybe_unused]] const auto cmt = std::get_if<CommentNode>(&*node)) {
1399 // Uncommenting this will generate invalid code. GLASM lacks comments. 1400 // Uncommenting this will generate invalid code. GLASM lacks comments.
1400 // AddLine("// {}", cmt->GetText()); 1401 // AddLine("// {}", cmt->GetText());
1401 return {}; 1402 return {};
@@ -1703,7 +1704,7 @@ std::string ARBDecompiler::HCastFloat(Operation operation) {
1703} 1704}
1704 1705
1705std::string ARBDecompiler::HUnpack(Operation operation) { 1706std::string ARBDecompiler::HUnpack(Operation operation) {
1706 const std::string operand = Visit(operation[0]); 1707 std::string operand = Visit(operation[0]);
1707 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) { 1708 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
1708 case Tegra::Shader::HalfType::H0_H1: 1709 case Tegra::Shader::HalfType::H0_H1:
1709 return operand; 1710 return operand;
@@ -2053,7 +2054,7 @@ std::string ARBDecompiler::InvocationId(Operation) {
2053 2054
2054std::string ARBDecompiler::YNegate(Operation) { 2055std::string ARBDecompiler::YNegate(Operation) {
2055 LOG_WARNING(Render_OpenGL, "(STUBBED)"); 2056 LOG_WARNING(Render_OpenGL, "(STUBBED)");
2056 const std::string temporary = AllocTemporary(); 2057 std::string temporary = AllocTemporary();
2057 AddLine("MOV.F {}, 1;", temporary); 2058 AddLine("MOV.F {}, 1;", temporary);
2058 return temporary; 2059 return temporary;
2059} 2060}
@@ -2076,10 +2077,6 @@ std::string ARBDecompiler::ShuffleIndexed(Operation operation) {
2076} 2077}
2077 2078
2078std::string ARBDecompiler::Barrier(Operation) { 2079std::string ARBDecompiler::Barrier(Operation) {
2079 if (!ir.IsDecompiled()) {
2080 LOG_ERROR(Render_OpenGL, "BAR used but shader is not decompiled");
2081 return {};
2082 }
2083 AddLine("BAR;"); 2080 AddLine("BAR;");
2084 return {}; 2081 return {};
2085} 2082}
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 630acb73b..e7d95149f 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -212,6 +212,7 @@ Device::Device()
212 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); 212 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
213 max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); 213 max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
214 max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS); 214 max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
215 max_compute_shared_memory_size = GetInteger<u32>(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE);
215 has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && 216 has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
216 GLAD_GL_NV_shader_thread_shuffle; 217 GLAD_GL_NV_shader_thread_shuffle;
217 has_shader_ballot = GLAD_GL_ARB_shader_ballot; 218 has_shader_ballot = GLAD_GL_ARB_shader_ballot;
@@ -250,6 +251,7 @@ Device::Device(std::nullptr_t) {
250 shader_storage_alignment = 4; 251 shader_storage_alignment = 4;
251 max_vertex_attributes = 16; 252 max_vertex_attributes = 16;
252 max_varyings = 15; 253 max_varyings = 15;
254 max_compute_shared_memory_size = 0x10000;
253 has_warp_intrinsics = true; 255 has_warp_intrinsics = true;
254 has_shader_ballot = true; 256 has_shader_ballot = true;
255 has_vertex_viewport_layer = true; 257 has_vertex_viewport_layer = true;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 94d38d7d1..8a4b6b9fc 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -52,6 +52,10 @@ public:
52 return max_varyings; 52 return max_varyings;
53 } 53 }
54 54
55 u32 GetMaxComputeSharedMemorySize() const {
56 return max_compute_shared_memory_size;
57 }
58
55 bool HasWarpIntrinsics() const { 59 bool HasWarpIntrinsics() const {
56 return has_warp_intrinsics; 60 return has_warp_intrinsics;
57 } 61 }
@@ -118,6 +122,7 @@ private:
118 std::size_t shader_storage_alignment{}; 122 std::size_t shader_storage_alignment{};
119 u32 max_vertex_attributes{}; 123 u32 max_vertex_attributes{};
120 u32 max_varyings{}; 124 u32 max_varyings{};
125 u32 max_compute_shared_memory_size{};
121 bool has_warp_intrinsics{}; 126 bool has_warp_intrinsics{};
122 bool has_shader_ballot{}; 127 bool has_shader_ballot{};
123 bool has_vertex_viewport_layer{}; 128 bool has_vertex_viewport_layer{};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index f469ed656..be71e1733 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -126,7 +126,7 @@ std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
126 const VideoCore::GuestDriverProfile guest_profile{entry.texture_handler_size}; 126 const VideoCore::GuestDriverProfile guest_profile{entry.texture_handler_size};
127 const VideoCommon::Shader::SerializedRegistryInfo info{guest_profile, entry.bound_buffer, 127 const VideoCommon::Shader::SerializedRegistryInfo info{guest_profile, entry.bound_buffer,
128 entry.graphics_info, entry.compute_info}; 128 entry.graphics_info, entry.compute_info};
129 const auto registry = std::make_shared<Registry>(entry.type, info); 129 auto registry = std::make_shared<Registry>(entry.type, info);
130 for (const auto& [address, value] : entry.keys) { 130 for (const auto& [address, value] : entry.keys) {
131 const auto [buffer, offset] = address; 131 const auto [buffer, offset] = address;
132 registry->InsertKey(buffer, offset, value); 132 registry->InsertKey(buffer, offset, value);
@@ -237,7 +237,6 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
237 const ShaderParameters& params, Maxwell::ShaderProgram program_type, ProgramCode code, 237 const ShaderParameters& params, Maxwell::ShaderProgram program_type, ProgramCode code,
238 ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) { 238 ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
239 const auto shader_type = GetShaderType(program_type); 239 const auto shader_type = GetShaderType(program_type);
240 const std::size_t size_in_bytes = code.size() * sizeof(u64);
241 240
242 auto& gpu = params.system.GPU(); 241 auto& gpu = params.system.GPU();
243 gpu.ShaderNotify().MarkSharderBuilding(); 242 gpu.ShaderNotify().MarkSharderBuilding();
@@ -287,8 +286,6 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
287 286
288std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params, 287std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
289 ProgramCode code) { 288 ProgramCode code) {
290 const std::size_t size_in_bytes = code.size() * sizeof(u64);
291
292 auto& gpu = params.system.GPU(); 289 auto& gpu = params.system.GPU();
293 gpu.ShaderNotify().MarkSharderBuilding(); 290 gpu.ShaderNotify().MarkSharderBuilding();
294 291
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 2c49aeaac..3f75fcd2b 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -602,8 +602,15 @@ private:
602 return; 602 return;
603 } 603 }
604 const auto& info = registry.GetComputeInfo(); 604 const auto& info = registry.GetComputeInfo();
605 if (const u32 size = info.shared_memory_size_in_words; size > 0) { 605 if (u32 size = info.shared_memory_size_in_words * 4; size > 0) {
606 code.AddLine("shared uint smem[{}];", size); 606 const u32 limit = device.GetMaxComputeSharedMemorySize();
607 if (size > limit) {
608 LOG_ERROR(Render_OpenGL, "Shared memory size {} is clamped to host's limit {}",
609 size, limit);
610 size = limit;
611 }
612
613 code.AddLine("shared uint smem[{}];", size / 4);
607 code.AddNewLine(); 614 code.AddNewLine();
608 } 615 }
609 code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;", 616 code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
@@ -1912,7 +1919,7 @@ private:
1912 Expression Comparison(Operation operation) { 1919 Expression Comparison(Operation operation) {
1913 static_assert(!unordered || type == Type::Float); 1920 static_assert(!unordered || type == Type::Float);
1914 1921
1915 const Expression expr = GenerateBinaryInfix(operation, op, Type::Bool, type, type); 1922 Expression expr = GenerateBinaryInfix(operation, op, Type::Bool, type, type);
1916 1923
1917 if constexpr (op.compare("!=") == 0 && type == Type::Float && !unordered) { 1924 if constexpr (op.compare("!=") == 0 && type == Type::Float && !unordered) {
1918 // GLSL's operator!=(float, float) doesn't seem be ordered. This happens on both AMD's 1925 // GLSL's operator!=(float, float) doesn't seem be ordered. This happens on both AMD's
@@ -1952,10 +1959,6 @@ private:
1952 return {fmt::format("({} != 0)", carry), Type::Bool}; 1959 return {fmt::format("({} != 0)", carry), Type::Bool};
1953 } 1960 }
1954 1961
1955 Expression LogicalFIsNan(Operation operation) {
1956 return GenerateUnary(operation, "isnan", Type::Bool, Type::Float);
1957 }
1958
1959 Expression LogicalAssign(Operation operation) { 1962 Expression LogicalAssign(Operation operation) {
1960 const Node& dest = operation[0]; 1963 const Node& dest = operation[0];
1961 const Node& src = operation[1]; 1964 const Node& src = operation[1];
@@ -2771,15 +2774,6 @@ private:
2771 return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings); 2774 return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
2772 } 2775 }
2773 2776
2774 bool IsRenderTargetEnabled(u32 render_target) const {
2775 for (u32 component = 0; component < 4; ++component) {
2776 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
2777 return true;
2778 }
2779 }
2780 return false;
2781 }
2782
2783 const Device& device; 2777 const Device& device;
2784 const ShaderIR& ir; 2778 const ShaderIR& ir;
2785 const Registry& registry; 2779 const Registry& registry;
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index d1f0ea932..81a39a3b8 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -40,7 +40,6 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
40} // Anonymous namespace 40} // Anonymous namespace
41 41
42void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) { 42void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
43 const auto& clip = regs.view_volume_clip_control;
44 const std::array enabled_lut = {regs.polygon_offset_point_enable, 43 const std::array enabled_lut = {regs.polygon_offset_point_enable,
45 regs.polygon_offset_line_enable, 44 regs.polygon_offset_line_enable,
46 regs.polygon_offset_fill_enable}; 45 regs.polygon_offset_fill_enable};
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index ae5c21baa..529744f2d 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -122,6 +122,11 @@ public:
122 return properties.limits.maxPushConstantsSize; 122 return properties.limits.maxPushConstantsSize;
123 } 123 }
124 124
125 /// Returns the maximum size for shared memory.
126 u32 GetMaxComputeSharedMemorySize() const {
127 return properties.limits.maxComputeSharedMemorySize;
128 }
129
125 /// Returns true if ASTC is natively supported. 130 /// Returns true if ASTC is natively supported.
126 bool IsOptimalAstcSupported() const { 131 bool IsOptimalAstcSupported() const {
127 return is_optimal_astc_supported; 132 return is_optimal_astc_supported;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 31e44aa2b..2ed2004f0 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -532,10 +532,6 @@ void RasterizerVulkan::Clear() {
532 532
533 scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, 533 scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
534 clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { 534 clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
535 VkClearValue clear_value;
536 clear_value.depthStencil.depth = clear_depth;
537 clear_value.depthStencil.stencil = clear_stencil;
538
539 VkClearAttachment attachment; 535 VkClearAttachment attachment;
540 attachment.aspectMask = aspect_flags; 536 attachment.aspectMask = aspect_flags;
541 attachment.colorAttachment = 0; 537 attachment.colorAttachment = 0;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 97429cc59..cd7d7a4e4 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -685,13 +685,19 @@ private:
685 } 685 }
686 t_smem_uint = TypePointer(spv::StorageClass::Workgroup, t_uint); 686 t_smem_uint = TypePointer(spv::StorageClass::Workgroup, t_uint);
687 687
688 const u32 smem_size = specialization.shared_memory_size; 688 u32 smem_size = specialization.shared_memory_size * 4;
689 if (smem_size == 0) { 689 if (smem_size == 0) {
690 // Avoid declaring an empty array. 690 // Avoid declaring an empty array.
691 return; 691 return;
692 } 692 }
693 const auto element_count = static_cast<u32>(Common::AlignUp(smem_size, 4) / 4); 693 const u32 limit = device.GetMaxComputeSharedMemorySize();
694 const Id type_array = TypeArray(t_uint, Constant(t_uint, element_count)); 694 if (smem_size > limit) {
695 LOG_ERROR(Render_Vulkan, "Shared memory size {} is clamped to host's limit {}",
696 smem_size, limit);
697 smem_size = limit;
698 }
699
700 const Id type_array = TypeArray(t_uint, Constant(t_uint, smem_size / 4));
695 const Id type_pointer = TypePointer(spv::StorageClass::Workgroup, type_array); 701 const Id type_pointer = TypePointer(spv::StorageClass::Workgroup, type_array);
696 Name(type_pointer, "SharedMemory"); 702 Name(type_pointer, "SharedMemory");
697 703
@@ -700,9 +706,9 @@ private:
700 } 706 }
701 707
702 void DeclareInternalFlags() { 708 void DeclareInternalFlags() {
703 constexpr std::array names = {"zero", "sign", "carry", "overflow"}; 709 static constexpr std::array names{"zero", "sign", "carry", "overflow"};
710
704 for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { 711 for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) {
705 const auto flag_code = static_cast<InternalFlag>(flag);
706 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); 712 const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
707 internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); 713 internal_flags[flag] = AddGlobalVariable(Name(id, names[flag]));
708 } 714 }
@@ -2798,7 +2804,6 @@ private:
2798 std::map<GlobalMemoryBase, Id> global_buffers; 2804 std::map<GlobalMemoryBase, Id> global_buffers;
2799 std::map<u32, TexelBuffer> uniform_texels; 2805 std::map<u32, TexelBuffer> uniform_texels;
2800 std::map<u32, SampledImage> sampled_images; 2806 std::map<u32, SampledImage> sampled_images;
2801 std::map<u32, TexelBuffer> storage_texels;
2802 std::map<u32, StorageImage> images; 2807 std::map<u32, StorageImage> images;
2803 2808
2804 std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; 2809 std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index c25e312b6..6bfd2abae 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -156,6 +156,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
156 .minImageCount = requested_image_count, 156 .minImageCount = requested_image_count,
157 .imageFormat = surface_format.format, 157 .imageFormat = surface_format.format,
158 .imageColorSpace = surface_format.colorSpace, 158 .imageColorSpace = surface_format.colorSpace,
159 .imageExtent = {},
159 .imageArrayLayers = 1, 160 .imageArrayLayers = 1,
160 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 161 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
161 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 162 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
@@ -204,6 +205,7 @@ void VKSwapchain::CreateImageViews() {
204 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 205 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
205 .pNext = nullptr, 206 .pNext = nullptr,
206 .flags = 0, 207 .flags = 0,
208 .image = {},
207 .viewType = VK_IMAGE_VIEW_TYPE_2D, 209 .viewType = VK_IMAGE_VIEW_TYPE_2D,
208 .format = image_format, 210 .format = image_format,
209 .components = 211 .components =
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index d102e6d27..efd4bb13b 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -138,6 +138,7 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
138 .flags = 0, 138 .flags = 0,
139 .imageType = SurfaceTargetToImage(params.target), 139 .imageType = SurfaceTargetToImage(params.target),
140 .format = format, 140 .format = format,
141 .extent = {},
141 .mipLevels = params.num_levels, 142 .mipLevels = params.num_levels,
142 .arrayLayers = static_cast<u32>(params.GetNumLayers()), 143 .arrayLayers = static_cast<u32>(params.GetNumLayers()),
143 .samples = VK_SAMPLE_COUNT_1_BIT, 144 .samples = VK_SAMPLE_COUNT_1_BIT,
@@ -458,6 +459,7 @@ VkImageView CachedSurfaceView::GetAttachment() {
458 .pNext = nullptr, 459 .pNext = nullptr,
459 .flags = 0, 460 .flags = 0,
460 .image = surface.GetImageHandle(), 461 .image = surface.GetImageHandle(),
462 .viewType = VK_IMAGE_VIEW_TYPE_1D,
461 .format = surface.GetImage().GetFormat(), 463 .format = surface.GetImage().GetFormat(),
462 .components = 464 .components =
463 { 465 {
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index a041519b7..73155966f 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -98,12 +98,12 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
98 op_b = GetOperandAbsNegInteger(op_b, false, instr.iadd3.neg_b, true); 98 op_b = GetOperandAbsNegInteger(op_b, false, instr.iadd3.neg_b, true);
99 op_c = GetOperandAbsNegInteger(op_c, false, instr.iadd3.neg_c, true); 99 op_c = GetOperandAbsNegInteger(op_c, false, instr.iadd3.neg_c, true);
100 100
101 const Node value = [&]() { 101 const Node value = [&] {
102 const Node add_ab = Operation(OperationCode::IAdd, NO_PRECISE, op_a, op_b); 102 Node add_ab = Operation(OperationCode::IAdd, NO_PRECISE, op_a, op_b);
103 if (opcode->get().GetId() != OpCode::Id::IADD3_R) { 103 if (opcode->get().GetId() != OpCode::Id::IADD3_R) {
104 return Operation(OperationCode::IAdd, NO_PRECISE, add_ab, op_c); 104 return Operation(OperationCode::IAdd, NO_PRECISE, add_ab, op_c);
105 } 105 }
106 const Node shifted = [&]() { 106 const Node shifted = [&] {
107 switch (instr.iadd3.mode) { 107 switch (instr.iadd3.mode) {
108 case Tegra::Shader::IAdd3Mode::RightShift: 108 case Tegra::Shader::IAdd3Mode::RightShift:
109 // TODO(tech4me): According to 109 // TODO(tech4me): According to
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index c0a8f233f..29a7cfbfe 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -75,8 +75,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
75 const Node value = [this, instr] { 75 const Node value = [this, instr] {
76 switch (instr.sys20) { 76 switch (instr.sys20) {
77 case SystemVariable::LaneId: 77 case SystemVariable::LaneId:
78 LOG_WARNING(HW_GPU, "S2R instruction with LaneId is incomplete"); 78 return Operation(OperationCode::ThreadId);
79 return Immediate(0U);
80 case SystemVariable::InvocationId: 79 case SystemVariable::InvocationId:
81 return Operation(OperationCode::InvocationId); 80 return Operation(OperationCode::InvocationId);
82 case SystemVariable::Ydirection: 81 case SystemVariable::Ydirection:
diff --git a/src/video_core/shader/decode/video.cpp b/src/video_core/shader/decode/video.cpp
index 64ba60ea2..1c0957277 100644
--- a/src/video_core/shader/decode/video.cpp
+++ b/src/video_core/shader/decode/video.cpp
@@ -91,29 +91,28 @@ u32 ShaderIR::DecodeVideo(NodeBlock& bb, u32 pc) {
91 return pc; 91 return pc;
92} 92}
93 93
94Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed, 94Node ShaderIR::GetVideoOperand(Node op, bool is_chunk, bool is_signed, VideoType type,
95 Tegra::Shader::VideoType type, u64 byte_height) { 95 u64 byte_height) {
96 if (!is_chunk) { 96 if (!is_chunk) {
97 return BitfieldExtract(op, static_cast<u32>(byte_height * 8), 8); 97 return BitfieldExtract(op, static_cast<u32>(byte_height * 8), 8);
98 } 98 }
99 const Node zero = Immediate(0);
100 99
101 switch (type) { 100 switch (type) {
102 case Tegra::Shader::VideoType::Size16_Low: 101 case VideoType::Size16_Low:
103 return BitfieldExtract(op, 0, 16); 102 return BitfieldExtract(op, 0, 16);
104 case Tegra::Shader::VideoType::Size16_High: 103 case VideoType::Size16_High:
105 return BitfieldExtract(op, 16, 16); 104 return BitfieldExtract(op, 16, 16);
106 case Tegra::Shader::VideoType::Size32: 105 case VideoType::Size32:
107 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when this type is used 106 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when this type is used
108 // (1 * 1 + 0 == 0x5b800000). Until a better explanation is found: abort. 107 // (1 * 1 + 0 == 0x5b800000). Until a better explanation is found: abort.
109 UNIMPLEMENTED(); 108 UNIMPLEMENTED();
110 return zero; 109 return Immediate(0);
111 case Tegra::Shader::VideoType::Invalid: 110 case VideoType::Invalid:
112 UNREACHABLE_MSG("Invalid instruction encoding"); 111 UNREACHABLE_MSG("Invalid instruction encoding");
113 return zero; 112 return Immediate(0);
114 default: 113 default:
115 UNREACHABLE(); 114 UNREACHABLE();
116 return zero; 115 return Immediate(0);
117 } 116 }
118} 117}
119 118
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp
index c83dc6615..233b8fa42 100644
--- a/src/video_core/shader/decode/xmad.cpp
+++ b/src/video_core/shader/decode/xmad.cpp
@@ -81,20 +81,21 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
81 SetTemporary(bb, 0, product); 81 SetTemporary(bb, 0, product);
82 product = GetTemporary(0); 82 product = GetTemporary(0);
83 83
84 const Node original_c = op_c; 84 Node original_c = op_c;
85 const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error 85 const Tegra::Shader::XmadMode set_mode = mode; // Workaround to clang compile error
86 op_c = [&]() { 86 op_c = [&] {
87 switch (set_mode) { 87 switch (set_mode) {
88 case Tegra::Shader::XmadMode::None: 88 case Tegra::Shader::XmadMode::None:
89 return original_c; 89 return original_c;
90 case Tegra::Shader::XmadMode::CLo: 90 case Tegra::Shader::XmadMode::CLo:
91 return BitfieldExtract(original_c, 0, 16); 91 return BitfieldExtract(std::move(original_c), 0, 16);
92 case Tegra::Shader::XmadMode::CHi: 92 case Tegra::Shader::XmadMode::CHi:
93 return BitfieldExtract(original_c, 16, 16); 93 return BitfieldExtract(std::move(original_c), 16, 16);
94 case Tegra::Shader::XmadMode::CBcc: { 94 case Tegra::Shader::XmadMode::CBcc: {
95 const Node shifted_b = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_b, 95 Node shifted_b = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_b,
96 original_b, Immediate(16)); 96 original_b, Immediate(16));
97 return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b); 97 return SignedOperation(OperationCode::IAdd, is_signed_c, std::move(original_c),
98 std::move(shifted_b));
98 } 99 }
99 case Tegra::Shader::XmadMode::CSfu: { 100 case Tegra::Shader::XmadMode::CSfu: {
100 const Node comp_a = 101 const Node comp_a =
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index e322c3402..29d794b34 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -112,9 +112,9 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
112} 112}
113 113
114Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const { 114Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const {
115 const Node node = MakeNode<InternalFlagNode>(flag); 115 Node node = MakeNode<InternalFlagNode>(flag);
116 if (negated) { 116 if (negated) {
117 return Operation(OperationCode::LogicalNegate, node); 117 return Operation(OperationCode::LogicalNegate, std::move(node));
118 } 118 }
119 return node; 119 return node;
120} 120}
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index a1cc4756d..7d5a75648 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -19,8 +19,6 @@ constexpr auto SNORM = ComponentType::SNORM;
19constexpr auto UNORM = ComponentType::UNORM; 19constexpr auto UNORM = ComponentType::UNORM;
20constexpr auto SINT = ComponentType::SINT; 20constexpr auto SINT = ComponentType::SINT;
21constexpr auto UINT = ComponentType::UINT; 21constexpr auto UINT = ComponentType::UINT;
22constexpr auto SNORM_FORCE_FP16 = ComponentType::SNORM_FORCE_FP16;
23constexpr auto UNORM_FORCE_FP16 = ComponentType::UNORM_FORCE_FP16;
24constexpr auto FLOAT = ComponentType::FLOAT; 22constexpr auto FLOAT = ComponentType::FLOAT;
25constexpr bool C = false; // Normal color 23constexpr bool C = false; // Normal color
26constexpr bool S = true; // Srgb 24constexpr bool S = true; // Srgb
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 59a193edd..94b96afb4 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -666,8 +666,6 @@ void Config::ReadRendererValues() {
666 QStringLiteral("use_asynchronous_shaders"), false); 666 QStringLiteral("use_asynchronous_shaders"), false);
667 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), 667 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
668 true); 668 true);
669 ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
670
671 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); 669 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
672 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); 670 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
673 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); 671 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
@@ -1153,9 +1151,6 @@ void Config::SaveRendererValues() {
1153 Settings::values.use_asynchronous_shaders, false); 1151 Settings::values.use_asynchronous_shaders, false);
1154 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, 1152 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
1155 true); 1153 true);
1156 WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
1157 false);
1158
1159 // Cast to double because Qt's written float values are not human-readable 1154 // Cast to double because Qt's written float values are not human-readable
1160 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); 1155 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
1161 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); 1156 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index bb47c3933..f9becab6e 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -4,17 +4,20 @@
4 4
5#include <QCheckBox> 5#include <QCheckBox>
6#include <QComboBox> 6#include <QComboBox>
7#include <QObject>
8#include <QString>
7#include "core/settings.h" 9#include "core/settings.h"
8#include "yuzu/configuration/configuration_shared.h" 10#include "yuzu/configuration/configuration_shared.h"
9#include "yuzu/configuration/configure_per_game.h" 11#include "yuzu/configuration/configure_per_game.h"
10 12
11void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, 13void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
12 const QCheckBox* checkbox) { 14 const QCheckBox* checkbox,
13 if (checkbox->checkState() == Qt::PartiallyChecked) { 15 const CheckState& tracker) {
16 if (tracker == CheckState::Global) {
14 setting->SetGlobal(true); 17 setting->SetGlobal(true);
15 } else { 18 } else {
16 setting->SetGlobal(false); 19 setting->SetGlobal(false);
17 setting->SetValue(checkbox->checkState() == Qt::Checked); 20 setting->SetValue(checkbox->checkState());
18 } 21 }
19} 22}
20 23
@@ -69,8 +72,69 @@ void ConfigurationShared::SetPerGameSetting(
69 ConfigurationShared::USE_GLOBAL_OFFSET); 72 ConfigurationShared::USE_GLOBAL_OFFSET);
70} 73}
71 74
72void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) { 75void ConfigurationShared::SetHighlight(QWidget* widget, const std::string& name, bool highlighted) {
73 const QString use_global_text = ConfigurePerGame::tr("Use global configuration"); 76 if (highlighted) {
77 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
78 .arg(QString::fromStdString(name)));
79 } else {
80 widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
81 .arg(QString::fromStdString(name)));
82 }
83 widget->show();
84}
85
86void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
87 const Settings::Setting<bool>& setting,
88 CheckState& tracker) {
89 if (setting.UsingGlobal()) {
90 tracker = CheckState::Global;
91 } else {
92 tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
93 }
94 SetHighlight(checkbox, name, tracker != CheckState::Global);
95 QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
96 [checkbox, name, setting, &tracker]() {
97 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
98 static_cast<int>(CheckState::Count));
99 if (tracker == CheckState::Global) {
100 checkbox->setChecked(setting.GetValue(true));
101 }
102 SetHighlight(checkbox, name, tracker != CheckState::Global);
103 });
104}
105
106void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, const std::string& name,
107 bool global, bool state, bool global_state,
108 CheckState& tracker) {
109 if (global) {
110 tracker = CheckState::Global;
111 } else {
112 tracker = (state == global_state) ? CheckState::On : CheckState::Off;
113 }
114 SetHighlight(checkbox, name, tracker != CheckState::Global);
115 QObject::connect(checkbox, &QCheckBox::clicked, checkbox,
116 [checkbox, name, global_state, &tracker]() {
117 tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
118 static_cast<int>(CheckState::Count));
119 if (tracker == CheckState::Global) {
120 checkbox->setChecked(global_state);
121 }
122 SetHighlight(checkbox, name, tracker != CheckState::Global);
123 });
124}
125
126void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target,
127 const std::string& target_name, int global) {
128 InsertGlobalItem(combobox, global);
129 QObject::connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), target,
130 [target, target_name](int index) {
131 ConfigurationShared::SetHighlight(target, target_name, index != 0);
132 });
133}
134
135void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
136 const QString use_global_text =
137 ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
74 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); 138 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
75 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); 139 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
76} 140}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index b11b1b950..003148c68 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -15,9 +15,17 @@ constexpr int USE_GLOBAL_INDEX = 0;
15constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; 15constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
16constexpr int USE_GLOBAL_OFFSET = 2; 16constexpr int USE_GLOBAL_OFFSET = 2;
17 17
18enum class CheckState {
19 Off,
20 On,
21 Global,
22 Count,
23};
24
18// Global-aware apply and set functions 25// Global-aware apply and set functions
19 26
20void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox); 27void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox,
28 const CheckState& tracker);
21void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); 29void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
22void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, 30void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
23 const QComboBox* combobox); 31 const QComboBox* combobox);
@@ -31,6 +39,14 @@ void SetPerGameSetting(QComboBox* combobox,
31void SetPerGameSetting(QComboBox* combobox, 39void SetPerGameSetting(QComboBox* combobox,
32 const Settings::Setting<Settings::GPUAccuracy>* setting); 40 const Settings::Setting<Settings::GPUAccuracy>* setting);
33 41
34void InsertGlobalItem(QComboBox* combobox); 42void SetHighlight(QWidget* widget, const std::string& name, bool highlighted);
43void SetColoredTristate(QCheckBox* checkbox, const std::string& name,
44 const Settings::Setting<bool>& setting, CheckState& tracker);
45void SetColoredTristate(QCheckBox* checkbox, const std::string& name, bool global, bool state,
46 bool global_state, CheckState& tracker);
47void SetColoredComboBox(QComboBox* combobox, QWidget* target, const std::string& target_name,
48 int global);
49
50void InsertGlobalItem(QComboBox* combobox, int global_index);
35 51
36} // namespace ConfigurationShared 52} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index cc021beec..fea632531 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -49,12 +49,9 @@ void ConfigureAudio::SetConfiguration() {
49 49
50 ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum()); 50 ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
51 51
52 if (Settings::configuring_global) { 52 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
53 ui->toggle_audio_stretching->setChecked( 53
54 Settings::values.enable_audio_stretching.GetValue()); 54 if (!Settings::configuring_global) {
55 } else {
56 ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
57 &Settings::values.enable_audio_stretching);
58 if (Settings::values.volume.UsingGlobal()) { 55 if (Settings::values.volume.UsingGlobal()) {
59 ui->volume_combo_box->setCurrentIndex(0); 56 ui->volume_combo_box->setCurrentIndex(0);
60 ui->volume_slider->setEnabled(false); 57 ui->volume_slider->setEnabled(false);
@@ -62,6 +59,8 @@ void ConfigureAudio::SetConfiguration() {
62 ui->volume_combo_box->setCurrentIndex(1); 59 ui->volume_combo_box->setCurrentIndex(1);
63 ui->volume_slider->setEnabled(true); 60 ui->volume_slider->setEnabled(true);
64 } 61 }
62 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout",
63 !Settings::values.volume.UsingGlobal());
65 } 64 }
66 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 65 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
67} 66}
@@ -120,7 +119,8 @@ void ConfigureAudio::ApplyConfiguration() {
120 } 119 }
121 } else { 120 } else {
122 ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, 121 ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
123 ui->toggle_audio_stretching); 122 ui->toggle_audio_stretching,
123 enable_audio_stretching);
124 if (ui->volume_combo_box->currentIndex() == 0) { 124 if (ui->volume_combo_box->currentIndex() == 0) {
125 Settings::values.volume.SetGlobal(true); 125 Settings::values.volume.SetGlobal(true);
126 } else { 126 } else {
@@ -173,9 +173,14 @@ void ConfigureAudio::SetupPerGameUI() {
173 return; 173 return;
174 } 174 }
175 175
176 ui->toggle_audio_stretching->setTristate(true); 176 ConfigurationShared::SetColoredTristate(ui->toggle_audio_stretching, "toggle_audio_stretching",
177 Settings::values.enable_audio_stretching,
178 enable_audio_stretching);
177 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), 179 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
178 this, [this](int index) { ui->volume_slider->setEnabled(index == 1); }); 180 this, [this](int index) {
181 ui->volume_slider->setEnabled(index == 1);
182 ConfigurationShared::SetHighlight(ui->volume_layout, "volume_layout", index == 1);
183 });
179 184
180 ui->output_sink_combo_box->setVisible(false); 185 ui->output_sink_combo_box->setVisible(false);
181 ui->output_sink_label->setVisible(false); 186 ui->output_sink_label->setVisible(false);
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index d84f4a682..9dbd3d93e 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -7,6 +7,10 @@
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9 9
10namespace ConfigurationShared {
11enum class CheckState;
12}
13
10namespace Ui { 14namespace Ui {
11class ConfigureAudio; 15class ConfigureAudio;
12} 16}
@@ -37,4 +41,6 @@ private:
37 void SetupPerGameUI(); 41 void SetupPerGameUI();
38 42
39 std::unique_ptr<Ui::ConfigureAudio> ui; 43 std::unique_ptr<Ui::ConfigureAudio> ui;
44
45 ConfigurationShared::CheckState enable_audio_stretching;
40}; 46};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index 862ccb988..9bd0cca96 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -56,80 +56,91 @@
56 </layout> 56 </layout>
57 </item> 57 </item>
58 <item> 58 <item>
59 <layout class="QHBoxLayout" name="horizontalLayout_2"> 59 <widget class="QWidget" name="volume_layout" native="true">
60 <property name="topMargin"> 60 <layout class="QHBoxLayout" name="horizontalLayout_2">
61 <number>0</number> 61 <property name="leftMargin">
62 </property> 62 <number>0</number>
63 <item> 63 </property>
64 <widget class="QComboBox" name="volume_combo_box"> 64 <property name="topMargin">
65 <item> 65 <number>0</number>
66 </property>
67 <property name="rightMargin">
68 <number>0</number>
69 </property>
70 <property name="bottomMargin">
71 <number>0</number>
72 </property>
73 <item>
74 <widget class="QComboBox" name="volume_combo_box">
75 <item>
76 <property name="text">
77 <string>Use global volume</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>Set volume:</string>
83 </property>
84 </item>
85 </widget>
86 </item>
87 <item>
88 <widget class="QLabel" name="volume_label">
66 <property name="text"> 89 <property name="text">
67 <string>Use global volume</string> 90 <string>Volume:</string>
91 </property>
92 </widget>
93 </item>
94 <item>
95 <spacer name="horizontalSpacer">
96 <property name="orientation">
97 <enum>Qt::Horizontal</enum>
98 </property>
99 <property name="sizeHint" stdset="0">
100 <size>
101 <width>30</width>
102 <height>20</height>
103 </size>
104 </property>
105 </spacer>
106 </item>
107 <item>
108 <widget class="QSlider" name="volume_slider">
109 <property name="sizePolicy">
110 <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
111 <horstretch>0</horstretch>
112 <verstretch>0</verstretch>
113 </sizepolicy>
114 </property>
115 <property name="maximum">
116 <number>100</number>
117 </property>
118 <property name="pageStep">
119 <number>10</number>
120 </property>
121 <property name="orientation">
122 <enum>Qt::Horizontal</enum>
123 </property>
124 </widget>
125 </item>
126 <item>
127 <widget class="QLabel" name="volume_indicator">
128 <property name="minimumSize">
129 <size>
130 <width>32</width>
131 <height>0</height>
132 </size>
68 </property> 133 </property>
69 </item>
70 <item>
71 <property name="text"> 134 <property name="text">
72 <string>Set volume:</string> 135 <string>0 %</string>
73 </property> 136 </property>
74 </item> 137 <property name="alignment">
75 </widget> 138 <set>Qt::AlignCenter</set>
76 </item> 139 </property>
77 <item> 140 </widget>
78 <widget class="QLabel" name="volume_label"> 141 </item>
79 <property name="text"> 142 </layout>
80 <string>Volume:</string> 143 </widget>
81 </property>
82 </widget>
83 </item>
84 <item>
85 <spacer name="horizontalSpacer">
86 <property name="orientation">
87 <enum>Qt::Horizontal</enum>
88 </property>
89 <property name="sizeHint" stdset="0">
90 <size>
91 <width>30</width>
92 <height>20</height>
93 </size>
94 </property>
95 </spacer>
96 </item>
97 <item>
98 <widget class="QSlider" name="volume_slider">
99 <property name="sizePolicy">
100 <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
101 <horstretch>0</horstretch>
102 <verstretch>0</verstretch>
103 </sizepolicy>
104 </property>
105 <property name="maximum">
106 <number>100</number>
107 </property>
108 <property name="pageStep">
109 <number>10</number>
110 </property>
111 <property name="orientation">
112 <enum>Qt::Horizontal</enum>
113 </property>
114 </widget>
115 </item>
116 <item>
117 <widget class="QLabel" name="volume_indicator">
118 <property name="minimumSize">
119 <size>
120 <width>32</width>
121 <height>0</height>
122 </size>
123 </property>
124 <property name="text">
125 <string>0 %</string>
126 </property>
127 <property name="alignment">
128 <set>Qt::AlignCenter</set>
129 </property>
130 </widget>
131 </item>
132 </layout>
133 </item> 144 </item>
134 </layout> 145 </layout>
135 </widget> 146 </widget>
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 272bdd6b8..9d6feb9f7 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -171,26 +171,6 @@
171 </property> 171 </property>
172 <layout class="QVBoxLayout" name="verticalLayout_6"> 172 <layout class="QVBoxLayout" name="verticalLayout_6">
173 <item> 173 <item>
174 <widget class="QCheckBox" name="dump_decompressed_nso">
175 <property name="whatsThis">
176 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
177 </property>
178 <property name="text">
179 <string>Dump Decompressed NSOs</string>
180 </property>
181 </widget>
182 </item>
183 <item>
184 <widget class="QCheckBox" name="dump_exefs">
185 <property name="whatsThis">
186 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
187 </property>
188 <property name="text">
189 <string>Dump ExeFS</string>
190 </property>
191 </widget>
192 </item>
193 <item>
194 <widget class="QCheckBox" name="reporting_services"> 174 <widget class="QCheckBox" name="reporting_services">
195 <property name="text"> 175 <property name="text">
196 <string>Enable Verbose Reporting Services</string> 176 <string>Enable Verbose Reporting Services</string>
@@ -257,8 +237,6 @@
257 <tabstop>open_log_button</tabstop> 237 <tabstop>open_log_button</tabstop>
258 <tabstop>homebrew_args_edit</tabstop> 238 <tabstop>homebrew_args_edit</tabstop>
259 <tabstop>enable_graphics_debugging</tabstop> 239 <tabstop>enable_graphics_debugging</tabstop>
260 <tabstop>dump_decompressed_nso</tabstop>
261 <tabstop>dump_exefs</tabstop>
262 <tabstop>reporting_services</tabstop> 240 <tabstop>reporting_services</tabstop>
263 <tabstop>quest_flag</tabstop> 241 <tabstop>quest_flag</tabstop>
264 </tabstops> 242 </tabstops>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 20316c9cc..c0dbd9855 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -19,9 +19,10 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
19 19
20 SetConfiguration(); 20 SetConfiguration();
21 21
22 connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() { 22 if (Settings::configuring_global) {
23 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked); 23 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
24 }); 24 [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
25 }
25} 26}
26 27
27ConfigureGeneral::~ConfigureGeneral() = default; 28ConfigureGeneral::~ConfigureGeneral() = default;
@@ -40,17 +41,12 @@ void ConfigureGeneral::SetConfiguration() {
40 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); 41 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
41 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); 42 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
42 43
43 if (!Settings::configuring_global) { 44 if (Settings::configuring_global) {
44 if (Settings::values.use_multi_core.UsingGlobal()) { 45 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
45 ui->use_multi_core->setCheckState(Qt::PartiallyChecked); 46 } else {
46 } 47 ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
47 if (Settings::values.use_frame_limit.UsingGlobal()) { 48 use_frame_limit != ConfigurationShared::CheckState::Global);
48 ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
49 }
50 } 49 }
51
52 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
53 ui->toggle_frame_limit->isEnabled());
54} 50}
55 51
56void ConfigureGeneral::ApplyConfiguration() { 52void ConfigureGeneral::ApplyConfiguration() {
@@ -71,9 +67,9 @@ void ConfigureGeneral::ApplyConfiguration() {
71 } 67 }
72 } else { 68 } else {
73 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, 69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
74 ui->use_multi_core); 70 ui->use_multi_core, use_multi_core);
75 71
76 bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked; 72 bool global_frame_limit = use_frame_limit == ConfigurationShared::CheckState::Global;
77 Settings::values.use_frame_limit.SetGlobal(global_frame_limit); 73 Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
78 Settings::values.frame_limit.SetGlobal(global_frame_limit); 74 Settings::values.frame_limit.SetGlobal(global_frame_limit);
79 if (!global_frame_limit) { 75 if (!global_frame_limit) {
@@ -109,6 +105,13 @@ void ConfigureGeneral::SetupPerGameUI() {
109 ui->toggle_background_pause->setVisible(false); 105 ui->toggle_background_pause->setVisible(false);
110 ui->toggle_hide_mouse->setVisible(false); 106 ui->toggle_hide_mouse->setVisible(false);
111 107
112 ui->toggle_frame_limit->setTristate(true); 108 ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, "toggle_frame_limit",
113 ui->use_multi_core->setTristate(true); 109 Settings::values.use_frame_limit, use_frame_limit);
110 ConfigurationShared::SetColoredTristate(ui->use_multi_core, "use_multi_core",
111 Settings::values.use_multi_core, use_multi_core);
112
113 connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() {
114 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() &&
115 (use_frame_limit != ConfigurationShared::CheckState::Global));
116 });
114} 117}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 9c785c22e..323ffbd8f 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -7,6 +7,10 @@
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9 9
10namespace ConfigurationShared {
11enum class CheckState;
12}
13
10class HotkeyRegistry; 14class HotkeyRegistry;
11 15
12namespace Ui { 16namespace Ui {
@@ -31,4 +35,7 @@ private:
31 void SetupPerGameUI(); 35 void SetupPerGameUI();
32 36
33 std::unique_ptr<Ui::ConfigureGeneral> ui; 37 std::unique_ptr<Ui::ConfigureGeneral> ui;
38
39 ConfigurationShared::CheckState use_frame_limit;
40 ConfigurationShared::CheckState use_multi_core;
34}; 41};
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cb4706bd6..3e42531c3 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -31,8 +31,14 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
31 31
32 SetConfiguration(); 32 SetConfiguration();
33 33
34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
35 [this] { UpdateDeviceComboBox(); }); 35 UpdateDeviceComboBox();
36 if (!Settings::configuring_global) {
37 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
38 ui->api->currentIndex() !=
39 ConfigurationShared::USE_GLOBAL_INDEX);
40 }
41 });
36 connect(ui->device, qOverload<int>(&QComboBox::activated), this, 42 connect(ui->device, qOverload<int>(&QComboBox::activated), this,
37 [this](int device) { UpdateDeviceSelection(device); }); 43 [this](int device) { UpdateDeviceSelection(device); });
38 44
@@ -65,25 +71,26 @@ void ConfigureGraphics::SetConfiguration() {
65 ui->api->setEnabled(runtime_lock); 71 ui->api->setEnabled(runtime_lock);
66 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 72 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
67 ui->use_disk_shader_cache->setEnabled(runtime_lock); 73 ui->use_disk_shader_cache->setEnabled(runtime_lock);
74 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
75 ui->use_asynchronous_gpu_emulation->setChecked(
76 Settings::values.use_asynchronous_gpu_emulation.GetValue());
68 77
69 if (Settings::configuring_global) { 78 if (Settings::configuring_global) {
70 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 79 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
71 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 80 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
72 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
73 ui->use_asynchronous_gpu_emulation->setChecked(
74 Settings::values.use_asynchronous_gpu_emulation.GetValue());
75 } else { 81 } else {
76 ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
77 &Settings::values.use_disk_shader_cache);
78 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
79 &Settings::values.use_asynchronous_gpu_emulation);
80
81 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 82 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
83 ConfigurationShared::SetHighlight(ui->api_layout, "api_layout",
84 !Settings::values.renderer_backend.UsingGlobal());
82 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, 85 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
83 &Settings::values.aspect_ratio); 86 &Settings::values.aspect_ratio);
84 87
85 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); 88 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
86 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); 89 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
90 ConfigurationShared::SetHighlight(ui->ar_label, "ar_label",
91 !Settings::values.aspect_ratio.UsingGlobal());
92 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout",
93 !Settings::values.bg_red.UsingGlobal());
87 } 94 }
88 95
89 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(), 96 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
@@ -135,9 +142,10 @@ void ConfigureGraphics::ApplyConfiguration() {
135 ui->aspect_ratio_combobox); 142 ui->aspect_ratio_combobox);
136 143
137 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, 144 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
138 ui->use_disk_shader_cache); 145 ui->use_disk_shader_cache, use_disk_shader_cache);
139 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, 146 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
140 ui->use_asynchronous_gpu_emulation); 147 ui->use_asynchronous_gpu_emulation,
148 use_asynchronous_gpu_emulation);
141 149
142 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { 150 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
143 Settings::values.bg_red.SetGlobal(true); 151 Settings::values.bg_red.SetGlobal(true);
@@ -241,10 +249,20 @@ void ConfigureGraphics::SetupPerGameUI() {
241 } 249 }
242 250
243 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, 251 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
244 [this](int index) { ui->bg_button->setEnabled(index == 1); }); 252 [this](int index) {
245 253 ui->bg_button->setEnabled(index == 1);
246 ui->use_disk_shader_cache->setTristate(true); 254 ConfigurationShared::SetHighlight(ui->bg_layout, "bg_layout", index == 1);
247 ui->use_asynchronous_gpu_emulation->setTristate(true); 255 });
248 ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox); 256
249 ConfigurationShared::InsertGlobalItem(ui->api); 257 ConfigurationShared::SetColoredTristate(ui->use_disk_shader_cache, "use_disk_shader_cache",
258 Settings::values.use_disk_shader_cache,
259 use_disk_shader_cache);
260 ConfigurationShared::SetColoredTristate(
261 ui->use_asynchronous_gpu_emulation, "use_asynchronous_gpu_emulation",
262 Settings::values.use_asynchronous_gpu_emulation, use_asynchronous_gpu_emulation);
263
264 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, "ar_label",
265 Settings::values.aspect_ratio.GetValue(true));
266 ConfigurationShared::InsertGlobalItem(
267 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
250} 268}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 24f01c739..b4961f719 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -10,6 +10,10 @@
10#include <QWidget> 10#include <QWidget>
11#include "core/settings.h" 11#include "core/settings.h"
12 12
13namespace ConfigurationShared {
14enum class CheckState;
15}
16
13namespace Ui { 17namespace Ui {
14class ConfigureGraphics; 18class ConfigureGraphics;
15} 19}
@@ -42,6 +46,9 @@ private:
42 std::unique_ptr<Ui::ConfigureGraphics> ui; 46 std::unique_ptr<Ui::ConfigureGraphics> ui;
43 QColor bg_color; 47 QColor bg_color;
44 48
49 ConfigurationShared::CheckState use_disk_shader_cache;
50 ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
51
45 std::vector<QString> vulkan_devices; 52 std::vector<QString> vulkan_devices;
46 u32 vulkan_device{}; 53 u32 vulkan_device{};
47}; 54};
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 62418fc14..62aa337e7 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>437</width>
10 <height>321</height> 10 <height>321</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -23,43 +23,56 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_4"> 26 <widget class="QWidget" name="api_layout" native="true">
27 <item> 27 <layout class="QGridLayout" name="gridLayout">
28 <widget class="QLabel" name="label_2"> 28 <property name="leftMargin">
29 <property name="text"> 29 <number>0</number>
30 <string>API:</string> 30 </property>
31 </property> 31 <property name="topMargin">
32 </widget> 32 <number>0</number>
33 </item> 33 </property>
34 <item> 34 <property name="rightMargin">
35 <widget class="QComboBox" name="api"> 35 <number>0</number>
36 <item> 36 </property>
37 <property name="bottomMargin">
38 <number>0</number>
39 </property>
40 <property name="horizontalSpacing">
41 <number>6</number>
42 </property>
43 <item row="0" column="0">
44 <widget class="QLabel" name="api_label">
37 <property name="text"> 45 <property name="text">
38 <string notr="true">OpenGL</string> 46 <string>API:</string>
39 </property> 47 </property>
40 </item> 48 </widget>
41 <item> 49 </item>
50 <item row="0" column="1">
51 <widget class="QComboBox" name="api">
52 <item>
53 <property name="text">
54 <string notr="true">OpenGL</string>
55 </property>
56 </item>
57 <item>
58 <property name="text">
59 <string notr="true">Vulkan</string>
60 </property>
61 </item>
62 </widget>
63 </item>
64 <item row="1" column="0">
65 <widget class="QLabel" name="device_label">
42 <property name="text"> 66 <property name="text">
43 <string notr="true">Vulkan</string> 67 <string>Device:</string>
44 </property> 68 </property>
45 </item> 69 </widget>
46 </widget> 70 </item>
47 </item> 71 <item row="1" column="1">
48 </layout> 72 <widget class="QComboBox" name="device"/>
49 </item> 73 </item>
50 <item> 74 </layout>
51 <layout class="QHBoxLayout" name="horizontalLayout_5"> 75 </widget>
52 <item>
53 <widget class="QLabel" name="label_3">
54 <property name="text">
55 <string>Device:</string>
56 </property>
57 </widget>
58 </item>
59 <item>
60 <widget class="QComboBox" name="device"/>
61 </item>
62 </layout>
63 </item> 76 </item>
64 </layout> 77 </layout>
65 </widget> 78 </widget>
@@ -85,96 +98,133 @@
85 </widget> 98 </widget>
86 </item> 99 </item>
87 <item> 100 <item>
88 <layout class="QHBoxLayout" name="horizontalLayout_6"> 101 <widget class="QWidget" name="aspect_ratio_layout" native="true">
89 <item> 102 <layout class="QHBoxLayout" name="horizontalLayout_6">
90 <widget class="QLabel" name="ar_label"> 103 <property name="leftMargin">
91 <property name="text"> 104 <number>0</number>
92 <string>Aspect Ratio:</string> 105 </property>
93 </property> 106 <property name="topMargin">
94 </widget> 107 <number>0</number>
95 </item> 108 </property>
96 <item> 109 <property name="rightMargin">
97 <widget class="QComboBox" name="aspect_ratio_combobox"> 110 <number>0</number>
98 <item> 111 </property>
99 <property name="text"> 112 <property name="bottomMargin">
100 <string>Default (16:9)</string> 113 <number>0</number>
101 </property> 114 </property>
102 </item> 115 <item>
103 <item> 116 <widget class="QLabel" name="ar_label">
104 <property name="text">
105 <string>Force 4:3</string>
106 </property>
107 </item>
108 <item>
109 <property name="text">
110 <string>Force 21:9</string>
111 </property>
112 </item>
113 <item>
114 <property name="text"> 117 <property name="text">
115 <string>Stretch to Window</string> 118 <string>Aspect Ratio:</string>
116 </property> 119 </property>
117 </item> 120 </widget>
118 </widget> 121 </item>
119 </item> 122 <item>
120 </layout> 123 <widget class="QComboBox" name="aspect_ratio_combobox">
124 <item>
125 <property name="text">
126 <string>Default (16:9)</string>
127 </property>
128 </item>
129 <item>
130 <property name="text">
131 <string>Force 4:3</string>
132 </property>
133 </item>
134 <item>
135 <property name="text">
136 <string>Force 21:9</string>
137 </property>
138 </item>
139 <item>
140 <property name="text">
141 <string>Stretch to Window</string>
142 </property>
143 </item>
144 </widget>
145 </item>
146 </layout>
147 </widget>
121 </item> 148 </item>
122 <item> 149 <item>
123 <layout class="QHBoxLayout" name="horizontalLayout_3"> 150 <widget class="QWidget" name="bg_layout" native="true">
124 <item> 151 <property name="sizePolicy">
125 <widget class="QComboBox" name="bg_combobox"> 152 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
126 <property name="currentText"> 153 <horstretch>0</horstretch>
127 <string>Use global background color</string> 154 <verstretch>0</verstretch>
128 </property> 155 </sizepolicy>
129 <property name="currentIndex"> 156 </property>
130 <number>0</number> 157 <layout class="QHBoxLayout" name="horizontalLayout_3">
131 </property> 158 <property name="spacing">
132 <property name="maxVisibleItems"> 159 <number>6</number>
133 <number>10</number> 160 </property>
134 </property> 161 <property name="leftMargin">
135 <item> 162 <number>0</number>
136 <property name="text"> 163 </property>
164 <property name="topMargin">
165 <number>0</number>
166 </property>
167 <property name="rightMargin">
168 <number>0</number>
169 </property>
170 <property name="bottomMargin">
171 <number>0</number>
172 </property>
173 <item>
174 <widget class="QComboBox" name="bg_combobox">
175 <property name="currentText">
137 <string>Use global background color</string> 176 <string>Use global background color</string>
138 </property> 177 </property>
139 </item> 178 <property name="currentIndex">
140 <item> 179 <number>0</number>
180 </property>
181 <property name="maxVisibleItems">
182 <number>10</number>
183 </property>
184 <item>
185 <property name="text">
186 <string>Use global background color</string>
187 </property>
188 </item>
189 <item>
190 <property name="text">
191 <string>Set background color:</string>
192 </property>
193 </item>
194 </widget>
195 </item>
196 <item>
197 <widget class="QLabel" name="bg_label">
141 <property name="text"> 198 <property name="text">
142 <string>Set background color:</string> 199 <string>Background Color:</string>
143 </property> 200 </property>
144 </item> 201 </widget>
145 </widget> 202 </item>
146 </item> 203 <item>
147 <item> 204 <spacer name="horizontalSpacer">
148 <widget class="QLabel" name="bg_label"> 205 <property name="orientation">
149 <property name="text"> 206 <enum>Qt::Horizontal</enum>
150 <string>Background Color:</string> 207 </property>
151 </property> 208 <property name="sizeHint" stdset="0">
152 </widget> 209 <size>
153 </item> 210 <width>40</width>
154 <item> 211 <height>20</height>
155 <spacer name="horizontalSpacer"> 212 </size>
156 <property name="orientation"> 213 </property>
157 <enum>Qt::Horizontal</enum> 214 </spacer>
158 </property> 215 </item>
159 <property name="sizeHint" stdset="0"> 216 <item>
160 <size> 217 <widget class="QPushButton" name="bg_button">
161 <width>40</width> 218 <property name="maximumSize">
162 <height>20</height> 219 <size>
163 </size> 220 <width>40</width>
164 </property> 221 <height>16777215</height>
165 </spacer> 222 </size>
166 </item> 223 </property>
167 <item> 224 </widget>
168 <widget class="QPushButton" name="bg_button"> 225 </item>
169 <property name="maximumSize"> 226 </layout>
170 <size> 227 </widget>
171 <width>40</width>
172 <height>16777215</height>
173 </size>
174 </property>
175 </widget>
176 </item>
177 </layout>
178 </item> 228 </item>
179 </layout> 229 </layout>
180 </widget> 230 </widget>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index ce30188cd..c5d1a778c 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -25,35 +25,26 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
25 ui->use_vsync->setEnabled(runtime_lock); 25 ui->use_vsync->setEnabled(runtime_lock);
26 ui->use_assembly_shaders->setEnabled(runtime_lock); 26 ui->use_assembly_shaders->setEnabled(runtime_lock);
27 ui->use_asynchronous_shaders->setEnabled(runtime_lock); 27 ui->use_asynchronous_shaders->setEnabled(runtime_lock);
28 ui->force_30fps_mode->setEnabled(runtime_lock);
29 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
30 29
30 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
31 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
32 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
33 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
34
31 if (Settings::configuring_global) { 35 if (Settings::configuring_global) {
32 ui->gpu_accuracy->setCurrentIndex( 36 ui->gpu_accuracy->setCurrentIndex(
33 static_cast<int>(Settings::values.gpu_accuracy.GetValue())); 37 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
34 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
35 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
36 ui->use_asynchronous_shaders->setChecked(
37 Settings::values.use_asynchronous_shaders.GetValue());
38 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
39 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
40 ui->anisotropic_filtering_combobox->setCurrentIndex( 38 ui->anisotropic_filtering_combobox->setCurrentIndex(
41 Settings::values.max_anisotropy.GetValue()); 39 Settings::values.max_anisotropy.GetValue());
42 } else { 40 } else {
43 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); 41 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
44 ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
45 ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
46 &Settings::values.use_assembly_shaders);
47 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
48 &Settings::values.use_asynchronous_shaders);
49 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
50 &Settings::values.use_asynchronous_shaders);
51 ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
52 &Settings::values.use_fast_gpu_time);
53 ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
54 &Settings::values.force_30fps_mode);
55 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, 42 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
56 &Settings::values.max_anisotropy); 43 &Settings::values.max_anisotropy);
44 ConfigurationShared::SetHighlight(ui->label_gpu_accuracy, "label_gpu_accuracy",
45 !Settings::values.gpu_accuracy.UsingGlobal());
46 ConfigurationShared::SetHighlight(ui->af_label, "af_label",
47 !Settings::values.max_anisotropy.UsingGlobal());
57 } 48 }
58} 49}
59 50
@@ -85,9 +76,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
85 if (Settings::values.use_fast_gpu_time.UsingGlobal()) { 76 if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
86 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); 77 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
87 } 78 }
88 if (Settings::values.force_30fps_mode.UsingGlobal()) {
89 Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
90 }
91 if (Settings::values.max_anisotropy.UsingGlobal()) { 79 if (Settings::values.max_anisotropy.UsingGlobal()) {
92 Settings::values.max_anisotropy.SetValue( 80 Settings::values.max_anisotropy.SetValue(
93 ui->anisotropic_filtering_combobox->currentIndex()); 81 ui->anisotropic_filtering_combobox->currentIndex());
@@ -95,17 +83,15 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
95 } else { 83 } else {
96 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
97 ui->anisotropic_filtering_combobox); 85 ui->anisotropic_filtering_combobox);
98 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync); 86 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync,
87 use_vsync);
99 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, 88 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
100 ui->use_assembly_shaders); 89 ui->use_assembly_shaders, use_assembly_shaders);
101 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
102 ui->use_asynchronous_shaders);
103 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, 90 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
104 ui->use_asynchronous_shaders); 91 ui->use_asynchronous_shaders,
92 use_asynchronous_shaders);
105 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 93 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
106 ui->use_fast_gpu_time); 94 ui->use_fast_gpu_time, use_fast_gpu_time);
107 ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
108 ui->force_30fps_mode);
109 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 95 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
110 ui->anisotropic_filtering_combobox); 96 ui->anisotropic_filtering_combobox);
111 97
@@ -139,18 +125,26 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
139 ui->use_asynchronous_shaders->setEnabled( 125 ui->use_asynchronous_shaders->setEnabled(
140 Settings::values.use_asynchronous_shaders.UsingGlobal()); 126 Settings::values.use_asynchronous_shaders.UsingGlobal());
141 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 127 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
142 ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
143 ui->anisotropic_filtering_combobox->setEnabled( 128 ui->anisotropic_filtering_combobox->setEnabled(
144 Settings::values.max_anisotropy.UsingGlobal()); 129 Settings::values.max_anisotropy.UsingGlobal());
145 130
146 return; 131 return;
147 } 132 }
148 133
149 ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy); 134 ConfigurationShared::SetColoredTristate(ui->use_vsync, "use_vsync", Settings::values.use_vsync,
150 ui->use_vsync->setTristate(true); 135 use_vsync);
151 ui->use_assembly_shaders->setTristate(true); 136 ConfigurationShared::SetColoredTristate(ui->use_assembly_shaders, "use_assembly_shaders",
152 ui->use_asynchronous_shaders->setTristate(true); 137 Settings::values.use_assembly_shaders,
153 ui->use_fast_gpu_time->setTristate(true); 138 use_assembly_shaders);
154 ui->force_30fps_mode->setTristate(true); 139 ConfigurationShared::SetColoredTristate(
155 ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox); 140 ui->use_asynchronous_shaders, "use_asynchronous_shaders",
141 Settings::values.use_asynchronous_shaders, use_asynchronous_shaders);
142 ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time",
143 Settings::values.use_fast_gpu_time, use_fast_gpu_time);
144 ConfigurationShared::SetColoredComboBox(
145 ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy",
146 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
147 ConfigurationShared::SetColoredComboBox(
148 ui->anisotropic_filtering_combobox, ui->af_label, "af_label",
149 static_cast<int>(Settings::values.max_anisotropy.GetValue(true)));
156} 150}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index c043588ff..e61b571c7 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -7,6 +7,10 @@
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9 9
10namespace ConfigurationShared {
11enum class CheckState;
12}
13
10namespace Ui { 14namespace Ui {
11class ConfigureGraphicsAdvanced; 15class ConfigureGraphicsAdvanced;
12} 16}
@@ -29,4 +33,9 @@ private:
29 void SetupPerGameUI(); 33 void SetupPerGameUI();
30 34
31 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 35 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
36
37 ConfigurationShared::CheckState use_vsync;
38 ConfigurationShared::CheckState use_assembly_shaders;
39 ConfigurationShared::CheckState use_asynchronous_shaders;
40 ConfigurationShared::CheckState use_fast_gpu_time;
32}; 41};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 71e7dfe5e..a793c803d 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>404</width>
10 <height>321</height> 10 <height>321</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -23,34 +23,48 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_2"> 26 <widget class="QWidget" name="gpu_accuracy_layout" native="true">
27 <item> 27 <layout class="QHBoxLayout" name="horizontalLayout_2">
28 <widget class="QLabel" name="label_gpu_accuracy"> 28 <property name="leftMargin">
29 <property name="text"> 29 <number>0</number>
30 <string>Accuracy Level:</string> 30 </property>
31 </property> 31 <property name="topMargin">
32 </widget> 32 <number>0</number>
33 </item> 33 </property>
34 <item> 34 <property name="rightMargin">
35 <widget class="QComboBox" name="gpu_accuracy"> 35 <number>0</number>
36 <item> 36 </property>
37 <property name="bottomMargin">
38 <number>0</number>
39 </property>
40 <item>
41 <widget class="QLabel" name="label_gpu_accuracy">
37 <property name="text"> 42 <property name="text">
38 <string notr="true">Normal</string> 43 <string>Accuracy Level:</string>
39 </property> 44 </property>
40 </item> 45 </widget>
41 <item> 46 </item>
42 <property name="text"> 47 <item>
43 <string notr="true">High</string> 48 <widget class="QComboBox" name="gpu_accuracy">
44 </property> 49 <item>
45 </item> 50 <property name="text">
46 <item> 51 <string notr="true">Normal</string>
47 <property name="text"> 52 </property>
48 <string notr="true">Extreme(very slow)</string> 53 </item>
49 </property> 54 <item>
50 </item> 55 <property name="text">
51 </widget> 56 <string notr="true">High</string>
52 </item> 57 </property>
53 </layout> 58 </item>
59 <item>
60 <property name="text">
61 <string notr="true">Extreme(very slow)</string>
62 </property>
63 </item>
64 </widget>
65 </item>
66 </layout>
67 </widget>
54 </item> 68 </item>
55 <item> 69 <item>
56 <widget class="QCheckBox" name="use_vsync"> 70 <widget class="QCheckBox" name="use_vsync">
@@ -83,13 +97,6 @@
83 </widget> 97 </widget>
84 </item> 98 </item>
85 <item> 99 <item>
86 <widget class="QCheckBox" name="force_30fps_mode">
87 <property name="text">
88 <string>Force 30 FPS mode</string>
89 </property>
90 </widget>
91 </item>
92 <item>
93 <widget class="QCheckBox" name="use_fast_gpu_time"> 100 <widget class="QCheckBox" name="use_fast_gpu_time">
94 <property name="text"> 101 <property name="text">
95 <string>Use Fast GPU Time</string> 102 <string>Use Fast GPU Time</string>
@@ -97,44 +104,58 @@
97 </widget> 104 </widget>
98 </item> 105 </item>
99 <item> 106 <item>
100 <layout class="QHBoxLayout" name="horizontalLayout_1"> 107 <widget class="QWidget" name="af_layout" native="true">
101 <item> 108 <layout class="QHBoxLayout" name="horizontalLayout_1">
102 <widget class="QLabel" name="af_label"> 109 <property name="leftMargin">
103 <property name="text"> 110 <number>0</number>
104 <string>Anisotropic Filtering:</string> 111 </property>
105 </property> 112 <property name="topMargin">
106 </widget> 113 <number>0</number>
107 </item> 114 </property>
108 <item> 115 <property name="rightMargin">
109 <widget class="QComboBox" name="anisotropic_filtering_combobox"> 116 <number>0</number>
110 <item> 117 </property>
118 <property name="bottomMargin">
119 <number>0</number>
120 </property>
121 <item>
122 <widget class="QLabel" name="af_label">
111 <property name="text"> 123 <property name="text">
112 <string>Default</string> 124 <string>Anisotropic Filtering:</string>
113 </property> 125 </property>
114 </item> 126 </widget>
115 <item> 127 </item>
116 <property name="text"> 128 <item>
117 <string>2x</string> 129 <widget class="QComboBox" name="anisotropic_filtering_combobox">
118 </property> 130 <item>
119 </item> 131 <property name="text">
120 <item> 132 <string>Default</string>
121 <property name="text"> 133 </property>
122 <string>4x</string> 134 </item>
123 </property> 135 <item>
124 </item> 136 <property name="text">
125 <item> 137 <string>2x</string>
126 <property name="text"> 138 </property>
127 <string>8x</string> 139 </item>
128 </property> 140 <item>
129 </item> 141 <property name="text">
130 <item> 142 <string>4x</string>
131 <property name="text"> 143 </property>
132 <string>16x</string> 144 </item>
133 </property> 145 <item>
134 </item> 146 <property name="text">
135 </widget> 147 <string>8x</string>
136 </item> 148 </property>
137 </layout> 149 </item>
150 <item>
151 <property name="text">
152 <string>16x</string>
153 </property>
154 </item>
155 </widget>
156 </item>
157 </layout>
158 </widget>
138 </item> 159 </item>
139 </layout> 160 </layout>
140 </widget> 161 </widget>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 68e02738b..0c4daf147 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -67,21 +67,21 @@ void ConfigureSystem::SetConfiguration() {
67 const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or( 67 const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or(
68 std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); 68 std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
69 69
70 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
71 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
72 Settings::values.rng_seed.UsingGlobal());
73 ui->rng_seed_edit->setText(rng_seed);
74
75 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value());
76 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() &&
77 Settings::values.rng_seed.UsingGlobal());
78 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
79
70 if (Settings::configuring_global) { 80 if (Settings::configuring_global) {
71 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); 81 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
72 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); 82 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
73 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); 83 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
74 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue()); 84 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
75
76 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
77 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
78 Settings::values.rng_seed.UsingGlobal());
79 ui->rng_seed_edit->setText(rng_seed);
80
81 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value());
82 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() &&
83 Settings::values.rng_seed.UsingGlobal());
84 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
85 } else { 85 } else {
86 ConfigurationShared::SetPerGameSetting(ui->combo_language, 86 ConfigurationShared::SetPerGameSetting(ui->combo_language,
87 &Settings::values.language_index); 87 &Settings::values.language_index);
@@ -90,27 +90,14 @@ void ConfigureSystem::SetConfiguration() {
90 &Settings::values.time_zone_index); 90 &Settings::values.time_zone_index);
91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); 91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
92 92
93 if (Settings::values.rng_seed.UsingGlobal()) { 93 ConfigurationShared::SetHighlight(ui->label_language, "label_language",
94 ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked); 94 !Settings::values.language_index.UsingGlobal());
95 } else { 95 ConfigurationShared::SetHighlight(ui->label_region, "label_region",
96 ui->rng_seed_checkbox->setCheckState( 96 !Settings::values.region_index.UsingGlobal());
97 Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked); 97 ConfigurationShared::SetHighlight(ui->label_timezone, "label_timezone",
98 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value()); 98 !Settings::values.time_zone_index.UsingGlobal());
99 if (Settings::values.rng_seed.GetValue().has_value()) { 99 ConfigurationShared::SetHighlight(ui->label_sound, "label_sound",
100 ui->rng_seed_edit->setText(rng_seed); 100 !Settings::values.sound_index.UsingGlobal());
101 }
102 }
103
104 if (Settings::values.custom_rtc.UsingGlobal()) {
105 ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked);
106 } else {
107 ui->custom_rtc_checkbox->setCheckState(
108 Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
109 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value());
110 if (Settings::values.custom_rtc.GetValue().has_value()) {
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
112 }
113 }
114 } 101 }
115} 102}
116 103
@@ -161,37 +148,44 @@ void ConfigureSystem::ApplyConfiguration() {
161 ui->combo_time_zone); 148 ui->combo_time_zone);
162 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); 149 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound);
163 150
164 switch (ui->rng_seed_checkbox->checkState()) { 151 switch (use_rng_seed) {
165 case Qt::Checked: 152 case ConfigurationShared::CheckState::On:
166 Settings::values.rng_seed.SetGlobal(false); 153 case ConfigurationShared::CheckState::Off:
167 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16));
168 break;
169 case Qt::Unchecked:
170 Settings::values.rng_seed.SetGlobal(false); 154 Settings::values.rng_seed.SetGlobal(false);
171 Settings::values.rng_seed.SetValue(std::nullopt); 155 if (ui->rng_seed_checkbox->isChecked()) {
156 Settings::values.rng_seed.SetValue(
157 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
158 } else {
159 Settings::values.rng_seed.SetValue(std::nullopt);
160 }
172 break; 161 break;
173 case Qt::PartiallyChecked: 162 case ConfigurationShared::CheckState::Global:
174 Settings::values.rng_seed.SetGlobal(false); 163 Settings::values.rng_seed.SetGlobal(false);
175 Settings::values.rng_seed.SetValue(std::nullopt); 164 Settings::values.rng_seed.SetValue(std::nullopt);
176 Settings::values.rng_seed.SetGlobal(true); 165 Settings::values.rng_seed.SetGlobal(true);
177 break; 166 break;
167 case ConfigurationShared::CheckState::Count:
168 break;
178 } 169 }
179 170
180 switch (ui->custom_rtc_checkbox->checkState()) { 171 switch (use_custom_rtc) {
181 case Qt::Checked: 172 case ConfigurationShared::CheckState::On:
182 Settings::values.custom_rtc.SetGlobal(false); 173 case ConfigurationShared::CheckState::Off:
183 Settings::values.custom_rtc.SetValue(
184 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
185 break;
186 case Qt::Unchecked:
187 Settings::values.custom_rtc.SetGlobal(false); 174 Settings::values.custom_rtc.SetGlobal(false);
188 Settings::values.custom_rtc.SetValue(std::nullopt); 175 if (ui->custom_rtc_checkbox->isChecked()) {
176 Settings::values.custom_rtc.SetValue(
177 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
178 } else {
179 Settings::values.custom_rtc.SetValue(std::nullopt);
180 }
189 break; 181 break;
190 case Qt::PartiallyChecked: 182 case ConfigurationShared::CheckState::Global:
191 Settings::values.custom_rtc.SetGlobal(false); 183 Settings::values.custom_rtc.SetGlobal(false);
192 Settings::values.custom_rtc.SetValue(std::nullopt); 184 Settings::values.custom_rtc.SetValue(std::nullopt);
193 Settings::values.custom_rtc.SetGlobal(true); 185 Settings::values.custom_rtc.SetGlobal(true);
194 break; 186 break;
187 case ConfigurationShared::CheckState::Count:
188 break;
195 } 189 }
196 } 190 }
197 191
@@ -229,10 +223,23 @@ void ConfigureSystem::SetupPerGameUI() {
229 return; 223 return;
230 } 224 }
231 225
232 ConfigurationShared::InsertGlobalItem(ui->combo_language); 226 ConfigurationShared::SetColoredComboBox(ui->combo_language, ui->label_language,
233 ConfigurationShared::InsertGlobalItem(ui->combo_region); 227 "label_language",
234 ConfigurationShared::InsertGlobalItem(ui->combo_time_zone); 228 Settings::values.language_index.GetValue(true));
235 ConfigurationShared::InsertGlobalItem(ui->combo_sound); 229 ConfigurationShared::SetColoredComboBox(ui->combo_region, ui->label_region, "label_region",
236 ui->rng_seed_checkbox->setTristate(true); 230 Settings::values.region_index.GetValue(true));
237 ui->custom_rtc_checkbox->setTristate(true); 231 ConfigurationShared::SetColoredComboBox(ui->combo_time_zone, ui->label_timezone,
232 "label_timezone",
233 Settings::values.time_zone_index.GetValue(true));
234 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->label_sound, "label_sound",
235 Settings::values.sound_index.GetValue(true));
236
237 ConfigurationShared::SetColoredTristate(
238 ui->rng_seed_checkbox, "rng_seed_checkbox", Settings::values.rng_seed.UsingGlobal(),
239 Settings::values.rng_seed.GetValue().has_value(),
240 Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
241 ConfigurationShared::SetColoredTristate(
242 ui->custom_rtc_checkbox, "custom_rtc_checkbox", Settings::values.custom_rtc.UsingGlobal(),
243 Settings::values.custom_rtc.GetValue().has_value(),
244 Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc);
238} 245}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index f317ef8b5..fc5cd2945 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -9,6 +9,10 @@
9#include <QList> 9#include <QList>
10#include <QWidget> 10#include <QWidget>
11 11
12namespace ConfigurationShared {
13enum class CheckState;
14}
15
12namespace Ui { 16namespace Ui {
13class ConfigureSystem; 17class ConfigureSystem;
14} 18}
@@ -41,4 +45,7 @@ private:
41 int region_index = 0; 45 int region_index = 0;
42 int time_zone_index = 0; 46 int time_zone_index = 0;
43 int sound_index = 0; 47 int sound_index = 0;
48
49 ConfigurationShared::CheckState use_rng_seed;
50 ConfigurationShared::CheckState use_custom_rtc;
44}; 51};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 9c8cca6dc..53b95658b 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -21,490 +21,494 @@
21 <property name="title"> 21 <property name="title">
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item row="3" column="0"> 25 <item>
26 <widget class="QLabel" name="label_sound"> 26 <layout class="QGridLayout" name="gridLayout_2">
27 <property name="text"> 27 <item row="1" column="0">
28 <string>Sound output mode</string> 28 <widget class="QLabel" name="label_region">
29 </property> 29 <property name="text">
30 </widget> 30 <string>Region:</string>
31 </item> 31 </property>
32 <item row="4" column="0"> 32 </widget>
33 <widget class="QLabel" name="label_console_id"> 33 </item>
34 <property name="text"> 34 <item row="2" column="1">
35 <string>Console ID:</string> 35 <widget class="QComboBox" name="combo_time_zone">
36 </property> 36 <item>
37 </widget> 37 <property name="text">
38 </item> 38 <string>Auto</string>
39 <item row="0" column="1"> 39 </property>
40 <widget class="QComboBox" name="combo_language"> 40 </item>
41 <property name="toolTip"> 41 <item>
42 <string>Note: this can be overridden when region setting is auto-select</string> 42 <property name="text">
43 </property> 43 <string>Default</string>
44 <item> 44 </property>
45 <property name="text"> 45 </item>
46 <string>Japanese (日本語)</string> 46 <item>
47 </property> 47 <property name="text">
48 </item> 48 <string>CET</string>
49 <item> 49 </property>
50 <property name="text"> 50 </item>
51 <string>English</string> 51 <item>
52 </property> 52 <property name="text">
53 </item> 53 <string>CST6CDT</string>
54 <item> 54 </property>
55 <property name="text"> 55 </item>
56 <string>French (français)</string> 56 <item>
57 </property> 57 <property name="text">
58 </item> 58 <string>Cuba</string>
59 <item> 59 </property>
60 <property name="text"> 60 </item>
61 <string>German (Deutsch)</string> 61 <item>
62 </property> 62 <property name="text">
63 </item> 63 <string>EET</string>
64 <item> 64 </property>
65 <property name="text"> 65 </item>
66 <string>Italian (italiano)</string> 66 <item>
67 </property> 67 <property name="text">
68 </item> 68 <string>Egypt</string>
69 <item> 69 </property>
70 <property name="text"> 70 </item>
71 <string>Spanish (español)</string> 71 <item>
72 </property> 72 <property name="text">
73 </item> 73 <string>Eire</string>
74 <item> 74 </property>
75 <property name="text"> 75 </item>
76 <string>Chinese</string> 76 <item>
77 </property> 77 <property name="text">
78 </item> 78 <string>EST</string>
79 <item> 79 </property>
80 <property name="text"> 80 </item>
81 <string>Korean (한국어)</string> 81 <item>
82 </property> 82 <property name="text">
83 </item> 83 <string>EST5EDT</string>
84 <item> 84 </property>
85 <property name="text"> 85 </item>
86 <string>Dutch (Nederlands)</string> 86 <item>
87 </property> 87 <property name="text">
88 </item> 88 <string>GB</string>
89 <item> 89 </property>
90 <property name="text"> 90 </item>
91 <string>Portuguese (português)</string> 91 <item>
92 </property> 92 <property name="text">
93 </item> 93 <string>GB-Eire</string>
94 <item> 94 </property>
95 <property name="text"> 95 </item>
96 <string>Russian (Русский)</string> 96 <item>
97 </property> 97 <property name="text">
98 </item> 98 <string>GMT</string>
99 <item> 99 </property>
100 <property name="text"> 100 </item>
101 <string>Taiwanese</string> 101 <item>
102 </property> 102 <property name="text">
103 </item> 103 <string>GMT+0</string>
104 <item> 104 </property>
105 <property name="text"> 105 </item>
106 <string>British English</string> 106 <item>
107 </property> 107 <property name="text">
108 </item> 108 <string>GMT-0</string>
109 <item> 109 </property>
110 <property name="text"> 110 </item>
111 <string>Canadian French</string> 111 <item>
112 </property> 112 <property name="text">
113 </item> 113 <string>GMT0</string>
114 <item> 114 </property>
115 <property name="text"> 115 </item>
116 <string>Latin American Spanish</string> 116 <item>
117 </property> 117 <property name="text">
118 </item> 118 <string>Greenwich</string>
119 <item> 119 </property>
120 <property name="text"> 120 </item>
121 <string>Simplified Chinese</string> 121 <item>
122 </property> 122 <property name="text">
123 </item> 123 <string>Hongkong</string>
124 <item> 124 </property>
125 <property name="text"> 125 </item>
126 <string>Traditional Chinese (正體中文)</string> 126 <item>
127 </property> 127 <property name="text">
128 </item> 128 <string>HST</string>
129 </widget> 129 </property>
130 </item> 130 </item>
131 <item row="1" column="0"> 131 <item>
132 <widget class="QLabel" name="label_region"> 132 <property name="text">
133 <property name="text"> 133 <string>Iceland</string>
134 <string>Region:</string> 134 </property>
135 </property> 135 </item>
136 </widget> 136 <item>
137 </item> 137 <property name="text">
138 <item row="1" column="1"> 138 <string>Iran</string>
139 <widget class="QComboBox" name="combo_region"> 139 </property>
140 <item> 140 </item>
141 <property name="text"> 141 <item>
142 <string>Japan</string> 142 <property name="text">
143 </property> 143 <string>Israel</string>
144 </item> 144 </property>
145 <item> 145 </item>
146 <property name="text"> 146 <item>
147 <string>USA</string> 147 <property name="text">
148 </property> 148 <string>Jamaica</string>
149 </item> 149 </property>
150 <item> 150 </item>
151 <property name="text"> 151 <item>
152 <string>Europe</string> 152 <property name="text">
153 </property> 153 <string>Japan</string>
154 </item> 154 </property>
155 <item> 155 </item>
156 <property name="text"> 156 <item>
157 <string>Australia</string> 157 <property name="text">
158 </property> 158 <string>Kwajalein</string>
159 </item> 159 </property>
160 <item> 160 </item>
161 <property name="text"> 161 <item>
162 <string>China</string> 162 <property name="text">
163 </property> 163 <string>Libya</string>
164 </item> 164 </property>
165 <item> 165 </item>
166 <property name="text"> 166 <item>
167 <string>Korea</string> 167 <property name="text">
168 </property> 168 <string>MET</string>
169 </item> 169 </property>
170 <item> 170 </item>
171 <property name="text"> 171 <item>
172 <string>Taiwan</string> 172 <property name="text">
173 </property> 173 <string>MST</string>
174 </item> 174 </property>
175 </widget> 175 </item>
176 </item> 176 <item>
177 <item row="2" column="0"> 177 <property name="text">
178 <widget class="QLabel" name="label_timezone"> 178 <string>MST7MDT</string>
179 <property name="text"> 179 </property>
180 <string>Time Zone:</string> 180 </item>
181 </property> 181 <item>
182 </widget> 182 <property name="text">
183 </item> 183 <string>Navajo</string>
184 <item row="2" column="1"> 184 </property>
185 <widget class="QComboBox" name="combo_time_zone"> 185 </item>
186 <item> 186 <item>
187 <property name="text"> 187 <property name="text">
188 <string>Auto</string> 188 <string>NZ</string>
189 </property> 189 </property>
190 </item> 190 </item>
191 <item> 191 <item>
192 <property name="text"> 192 <property name="text">
193 <string>Default</string> 193 <string>NZ-CHAT</string>
194 </property> 194 </property>
195 </item> 195 </item>
196 <item> 196 <item>
197 <property name="text"> 197 <property name="text">
198 <string>CET</string> 198 <string>Poland</string>
199 </property> 199 </property>
200 </item> 200 </item>
201 <item> 201 <item>
202 <property name="text"> 202 <property name="text">
203 <string>CST6CDT</string> 203 <string>Portugal</string>
204 </property> 204 </property>
205 </item> 205 </item>
206 <item> 206 <item>
207 <property name="text"> 207 <property name="text">
208 <string>Cuba</string> 208 <string>PRC</string>
209 </property> 209 </property>
210 </item> 210 </item>
211 <item> 211 <item>
212 <property name="text"> 212 <property name="text">
213 <string>EET</string> 213 <string>PST8PDT</string>
214 </property> 214 </property>
215 </item> 215 </item>
216 <item> 216 <item>
217 <property name="text"> 217 <property name="text">
218 <string>Egypt</string> 218 <string>ROC</string>
219 </property> 219 </property>
220 </item> 220 </item>
221 <item> 221 <item>
222 <property name="text"> 222 <property name="text">
223 <string>Eire</string> 223 <string>ROK</string>
224 </property> 224 </property>
225 </item> 225 </item>
226 <item> 226 <item>
227 <property name="text"> 227 <property name="text">
228 <string>EST</string> 228 <string>Singapore</string>
229 </property> 229 </property>
230 </item> 230 </item>
231 <item> 231 <item>
232 <property name="text"> 232 <property name="text">
233 <string>EST5EDT</string> 233 <string>Turkey</string>
234 </property> 234 </property>
235 </item> 235 </item>
236 <item> 236 <item>
237 <property name="text"> 237 <property name="text">
238 <string>GB</string> 238 <string>UCT</string>
239 </property> 239 </property>
240 </item> 240 </item>
241 <item> 241 <item>
242 <property name="text"> 242 <property name="text">
243 <string>GB-Eire</string> 243 <string>Universal</string>
244 </property> 244 </property>
245 </item> 245 </item>
246 <item> 246 <item>
247 <property name="text"> 247 <property name="text">
248 <string>GMT</string> 248 <string>UTC</string>
249 </property> 249 </property>
250 </item> 250 </item>
251 <item> 251 <item>
252 <property name="text"> 252 <property name="text">
253 <string>GMT+0</string> 253 <string>W-SU</string>
254 </property> 254 </property>
255 </item> 255 </item>
256 <item> 256 <item>
257 <property name="text"> 257 <property name="text">
258 <string>GMT-0</string> 258 <string>WET</string>
259 </property> 259 </property>
260 </item> 260 </item>
261 <item> 261 <item>
262 <property name="text"> 262 <property name="text">
263 <string>GMT0</string> 263 <string>Zulu</string>
264 </property> 264 </property>
265 </item> 265 </item>
266 <item> 266 </widget>
267 <property name="text"> 267 </item>
268 <string>Greenwich</string> 268 <item row="1" column="1">
269 </property> 269 <widget class="QComboBox" name="combo_region">
270 </item> 270 <item>
271 <item> 271 <property name="text">
272 <property name="text"> 272 <string>Japan</string>
273 <string>Hongkong</string> 273 </property>
274 </property> 274 </item>
275 </item> 275 <item>
276 <item> 276 <property name="text">
277 <property name="text"> 277 <string>USA</string>
278 <string>HST</string> 278 </property>
279 </property> 279 </item>
280 </item> 280 <item>
281 <item> 281 <property name="text">
282 <property name="text"> 282 <string>Europe</string>
283 <string>Iceland</string> 283 </property>
284 </property> 284 </item>
285 </item> 285 <item>
286 <item> 286 <property name="text">
287 <property name="text"> 287 <string>Australia</string>
288 <string>Iran</string> 288 </property>
289 </property> 289 </item>
290 </item> 290 <item>
291 <item> 291 <property name="text">
292 <property name="text"> 292 <string>China</string>
293 <string>Israel</string> 293 </property>
294 </property> 294 </item>
295 </item> 295 <item>
296 <item> 296 <property name="text">
297 <property name="text"> 297 <string>Korea</string>
298 <string>Jamaica</string> 298 </property>
299 </property> 299 </item>
300 </item> 300 <item>
301 <item> 301 <property name="text">
302 <property name="text"> 302 <string>Taiwan</string>
303 <string>Japan</string> 303 </property>
304 </property> 304 </item>
305 </item> 305 </widget>
306 <item> 306 </item>
307 <property name="text"> 307 <item row="2" column="0">
308 <string>Kwajalein</string> 308 <widget class="QLabel" name="label_timezone">
309 </property> 309 <property name="text">
310 </item> 310 <string>Time Zone:</string>
311 <item> 311 </property>
312 <property name="text"> 312 </widget>
313 <string>Libya</string> 313 </item>
314 </property> 314 <item row="0" column="1">
315 </item> 315 <widget class="QComboBox" name="combo_language">
316 <item> 316 <property name="toolTip">
317 <property name="text"> 317 <string>Note: this can be overridden when region setting is auto-select</string>
318 <string>MET</string> 318 </property>
319 </property> 319 <item>
320 </item> 320 <property name="text">
321 <item> 321 <string>Japanese (日本語)</string>
322 <property name="text"> 322 </property>
323 <string>MST</string> 323 </item>
324 </property> 324 <item>
325 </item> 325 <property name="text">
326 <item> 326 <string>English</string>
327 <property name="text"> 327 </property>
328 <string>MST7MDT</string> 328 </item>
329 </property> 329 <item>
330 </item> 330 <property name="text">
331 <item> 331 <string>French (français)</string>
332 <property name="text"> 332 </property>
333 <string>Navajo</string> 333 </item>
334 </property> 334 <item>
335 </item> 335 <property name="text">
336 <item> 336 <string>German (Deutsch)</string>
337 <property name="text"> 337 </property>
338 <string>NZ</string> 338 </item>
339 </property> 339 <item>
340 </item> 340 <property name="text">
341 <item> 341 <string>Italian (italiano)</string>
342 <property name="text"> 342 </property>
343 <string>NZ-CHAT</string> 343 </item>
344 </property> 344 <item>
345 </item> 345 <property name="text">
346 <item> 346 <string>Spanish (español)</string>
347 <property name="text"> 347 </property>
348 <string>Poland</string> 348 </item>
349 </property> 349 <item>
350 </item> 350 <property name="text">
351 <item> 351 <string>Chinese</string>
352 <property name="text"> 352 </property>
353 <string>Portugal</string> 353 </item>
354 </property> 354 <item>
355 </item> 355 <property name="text">
356 <item> 356 <string>Korean (한국어)</string>
357 <property name="text"> 357 </property>
358 <string>PRC</string> 358 </item>
359 </property> 359 <item>
360 </item> 360 <property name="text">
361 <item> 361 <string>Dutch (Nederlands)</string>
362 <property name="text"> 362 </property>
363 <string>PST8PDT</string> 363 </item>
364 </property> 364 <item>
365 </item> 365 <property name="text">
366 <item> 366 <string>Portuguese (português)</string>
367 <property name="text"> 367 </property>
368 <string>ROC</string> 368 </item>
369 </property> 369 <item>
370 </item> 370 <property name="text">
371 <item> 371 <string>Russian (Русский)</string>
372 <property name="text"> 372 </property>
373 <string>ROK</string> 373 </item>
374 </property> 374 <item>
375 </item> 375 <property name="text">
376 <item> 376 <string>Taiwanese</string>
377 <property name="text"> 377 </property>
378 <string>Singapore</string> 378 </item>
379 </property> 379 <item>
380 </item> 380 <property name="text">
381 <item> 381 <string>British English</string>
382 <property name="text"> 382 </property>
383 <string>Turkey</string> 383 </item>
384 </property> 384 <item>
385 </item> 385 <property name="text">
386 <item> 386 <string>Canadian French</string>
387 <property name="text"> 387 </property>
388 <string>UCT</string> 388 </item>
389 </property> 389 <item>
390 </item> 390 <property name="text">
391 <item> 391 <string>Latin American Spanish</string>
392 <property name="text"> 392 </property>
393 <string>Universal</string> 393 </item>
394 </property> 394 <item>
395 </item> 395 <property name="text">
396 <item> 396 <string>Simplified Chinese</string>
397 <property name="text"> 397 </property>
398 <string>UTC</string> 398 </item>
399 </property> 399 <item>
400 </item> 400 <property name="text">
401 <item> 401 <string>Traditional Chinese (正體中文)</string>
402 <property name="text"> 402 </property>
403 <string>W-SU</string> 403 </item>
404 </property> 404 </widget>
405 </item> 405 </item>
406 <item> 406 <item row="5" column="0">
407 <property name="text"> 407 <widget class="QCheckBox" name="custom_rtc_checkbox">
408 <string>WET</string> 408 <property name="text">
409 </property> 409 <string>Custom RTC</string>
410 </item> 410 </property>
411 <item> 411 </widget>
412 <property name="text"> 412 </item>
413 <string>Zulu</string> 413 <item row="0" column="0">
414 </property> 414 <widget class="QLabel" name="label_language">
415 </item> 415 <property name="text">
416 </widget> 416 <string>Language</string>
417 </item> 417 </property>
418 <item row="6" column="0"> 418 </widget>
419 <widget class="QCheckBox" name="rng_seed_checkbox"> 419 </item>
420 <property name="text"> 420 <item row="6" column="0">
421 <string>RNG Seed</string> 421 <widget class="QCheckBox" name="rng_seed_checkbox">
422 </property> 422 <property name="text">
423 </widget> 423 <string>RNG Seed</string>
424 </item> 424 </property>
425 <item row="3" column="1"> 425 </widget>
426 <widget class="QComboBox" name="combo_sound"> 426 </item>
427 <item> 427 <item row="3" column="1">
428 <property name="text"> 428 <widget class="QComboBox" name="combo_sound">
429 <string>Mono</string> 429 <item>
430 </property> 430 <property name="text">
431 </item> 431 <string>Mono</string>
432 <item> 432 </property>
433 <property name="text"> 433 </item>
434 <string>Stereo</string> 434 <item>
435 </property> 435 <property name="text">
436 </item> 436 <string>Stereo</string>
437 <item> 437 </property>
438 <property name="text"> 438 </item>
439 <string>Surround</string> 439 <item>
440 </property> 440 <property name="text">
441 </item> 441 <string>Surround</string>
442 </widget> 442 </property>
443 </item> 443 </item>
444 <item row="0" column="0"> 444 </widget>
445 <widget class="QLabel" name="label_language"> 445 </item>
446 <property name="text"> 446 <item row="4" column="0">
447 <string>Language</string> 447 <widget class="QLabel" name="label_console_id">
448 </property> 448 <property name="text">
449 </widget> 449 <string>Console ID:</string>
450 </item> 450 </property>
451 <item row="4" column="1"> 451 </widget>
452 <widget class="QPushButton" name="button_regenerate_console_id"> 452 </item>
453 <property name="sizePolicy"> 453 <item row="3" column="0">
454 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 454 <widget class="QLabel" name="label_sound">
455 <horstretch>0</horstretch> 455 <property name="text">
456 <verstretch>0</verstretch> 456 <string>Sound output mode</string>
457 </sizepolicy> 457 </property>
458 </property> 458 </widget>
459 <property name="layoutDirection"> 459 </item>
460 <enum>Qt::RightToLeft</enum> 460 <item row="5" column="1">
461 </property> 461 <widget class="QDateTimeEdit" name="custom_rtc_edit">
462 <property name="text"> 462 <property name="minimumDate">
463 <string>Regenerate</string> 463 <date>
464 </property> 464 <year>1970</year>
465 </widget> 465 <month>1</month>
466 </item> 466 <day>1</day>
467 <item row="5" column="0"> 467 </date>
468 <widget class="QCheckBox" name="custom_rtc_checkbox"> 468 </property>
469 <property name="text"> 469 <property name="displayFormat">
470 <string>Custom RTC</string> 470 <string>d MMM yyyy h:mm:ss AP</string>
471 </property> 471 </property>
472 </widget> 472 </widget>
473 </item> 473 </item>
474 <item row="5" column="1"> 474 <item row="6" column="1">
475 <widget class="QDateTimeEdit" name="custom_rtc_edit"> 475 <widget class="QLineEdit" name="rng_seed_edit">
476 <property name="minimumDate"> 476 <property name="sizePolicy">
477 <date> 477 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
478 <year>1970</year> 478 <horstretch>0</horstretch>
479 <month>1</month> 479 <verstretch>0</verstretch>
480 <day>1</day> 480 </sizepolicy>
481 </date> 481 </property>
482 </property> 482 <property name="font">
483 <property name="displayFormat"> 483 <font>
484 <string>d MMM yyyy h:mm:ss AP</string> 484 <family>Lucida Console</family>
485 </property> 485 </font>
486 </widget> 486 </property>
487 </item> 487 <property name="inputMask">
488 <item row="6" column="1"> 488 <string notr="true">HHHHHHHH</string>
489 <widget class="QLineEdit" name="rng_seed_edit"> 489 </property>
490 <property name="sizePolicy"> 490 <property name="maxLength">
491 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 491 <number>8</number>
492 <horstretch>0</horstretch> 492 </property>
493 <verstretch>0</verstretch> 493 </widget>
494 </sizepolicy> 494 </item>
495 </property> 495 <item row="4" column="1">
496 <property name="font"> 496 <widget class="QPushButton" name="button_regenerate_console_id">
497 <font> 497 <property name="sizePolicy">
498 <family>Lucida Console</family> 498 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
499 </font> 499 <horstretch>0</horstretch>
500 </property> 500 <verstretch>0</verstretch>
501 <property name="inputMask"> 501 </sizepolicy>
502 <string notr="true">HHHHHHHH</string> 502 </property>
503 </property> 503 <property name="layoutDirection">
504 <property name="maxLength"> 504 <enum>Qt::RightToLeft</enum>
505 <number>8</number> 505 </property>
506 </property> 506 <property name="text">
507 </widget> 507 <string>Regenerate</string>
508 </property>
509 </widget>
510 </item>
511 </layout>
508 </item> 512 </item>
509 </layout> 513 </layout>
510 </widget> 514 </widget>
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f391a41a9..3439cb333 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -38,7 +38,10 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
38 38
39bool IsDarkTheme() { 39bool IsDarkTheme() {
40 const auto& theme = UISettings::values.theme; 40 const auto& theme = UISettings::values.theme;
41 return theme == QStringLiteral("qdarkstyle") || theme == QStringLiteral("colorful_dark"); 41 return theme == QStringLiteral("qdarkstyle") ||
42 theme == QStringLiteral("qdarkstyle_midnight_blue") ||
43 theme == QStringLiteral("colorful_dark") ||
44 theme == QStringLiteral("colorful_midnight_blue");
42} 45}
43 46
44} // namespace 47} // namespace
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index ab7fc7a24..62acc3720 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
474 474
475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { 475void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); 476 QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
477 QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); 477 QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
478 QAction* open_transferable_shader_cache = 478 QAction* open_transferable_shader_cache =
479 context_menu.addAction(tr("Open Transferable Shader Cache")); 479 context_menu.addAction(tr("Open Transferable Shader Cache"));
480 context_menu.addSeparator(); 480 context_menu.addSeparator();
481 QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
482 QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
483 QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
484 QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache"));
485 QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
486 remove_menu->addSeparator();
487 QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
481 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); 488 QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
482 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 489 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
483 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 490 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
484 context_menu.addSeparator(); 491 context_menu.addSeparator();
485 QAction* properties = context_menu.addAction(tr("Properties")); 492 QAction* properties = context_menu.addAction(tr("Properties"));
486 493
487 open_save_location->setEnabled(program_id != 0); 494 open_save_location->setVisible(program_id != 0);
495 open_mod_location->setVisible(program_id != 0);
496 open_transferable_shader_cache->setVisible(program_id != 0);
497 remove_update->setVisible(program_id != 0);
498 remove_dlc->setVisible(program_id != 0);
499 remove_shader_cache->setVisible(program_id != 0);
500 remove_all_content->setVisible(program_id != 0);
488 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 501 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
489 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); 502 navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
490 503
491 connect(open_save_location, &QAction::triggered, [this, program_id, path]() { 504 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
492 emit OpenFolderRequested(GameListOpenTarget::SaveData, path); 505 emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
493 }); 506 });
494 connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() { 507 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
495 emit OpenFolderRequested(GameListOpenTarget::ModData, path); 508 emit OpenFolderRequested(GameListOpenTarget::ModData, path);
496 }); 509 });
497 connect(open_transferable_shader_cache, &QAction::triggered, 510 connect(open_transferable_shader_cache, &QAction::triggered,
498 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); 511 [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
512 connect(remove_all_content, &QAction::triggered, [this, program_id]() {
513 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game);
514 });
515 connect(remove_update, &QAction::triggered, [this, program_id]() {
516 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update);
517 });
518 connect(remove_dlc, &QAction::triggered, [this, program_id]() {
519 emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent);
520 });
521 connect(remove_shader_cache, &QAction::triggered, [this, program_id]() {
522 emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache);
523 });
524 connect(remove_custom_config, &QAction::triggered, [this, program_id]() {
525 emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration);
526 });
499 connect(dump_romfs, &QAction::triggered, 527 connect(dump_romfs, &QAction::triggered,
500 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); 528 [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
501 connect(copy_tid, &QAction::triggered, 529 connect(copy_tid, &QAction::triggered,
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index a38cb2fc3..483835cce 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -39,6 +39,17 @@ enum class GameListOpenTarget {
39 ModData, 39 ModData,
40}; 40};
41 41
42enum class GameListRemoveTarget {
43 ShaderCache,
44 CustomConfiguration,
45};
46
47enum class InstalledEntryType {
48 Game,
49 Update,
50 AddOnContent,
51};
52
42class GameList : public QWidget { 53class GameList : public QWidget {
43 Q_OBJECT 54 Q_OBJECT
44 55
@@ -75,6 +86,8 @@ signals:
75 void ShouldCancelWorker(); 86 void ShouldCancelWorker();
76 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); 87 void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
77 void OpenTransferableShaderCacheRequested(u64 program_id); 88 void OpenTransferableShaderCacheRequested(u64 program_id);
89 void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
90 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
78 void DumpRomFSRequested(u64 program_id, const std::string& game_path); 91 void DumpRomFSRequested(u64 program_id, const std::string& game_path);
79 void CopyTIDRequested(u64 program_id); 92 void CopyTIDRequested(u64 program_id);
80 void NavigateToGamedbEntryRequested(u64 program_id, 93 void NavigateToGamedbEntryRequested(u64 program_id,
@@ -117,8 +130,6 @@ private:
117 friend class GameListSearchField; 130 friend class GameListSearchField;
118}; 131};
119 132
120Q_DECLARE_METATYPE(GameListOpenTarget);
121
122class GameListPlaceholder : public QWidget { 133class GameListPlaceholder : public QWidget {
123 Q_OBJECT 134 Q_OBJECT
124public: 135public:
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 31a635176..276658c9e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() {
847 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 847 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
848 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, 848 connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
849 &GMainWindow::OnTransferableShaderCacheOpenFile); 849 &GMainWindow::OnTransferableShaderCacheOpenFile);
850 connect(game_list, &GameList::RemoveInstalledEntryRequested, this,
851 &GMainWindow::OnGameListRemoveInstalledEntry);
852 connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
850 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); 853 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
851 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 854 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
852 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 855 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
@@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1257 case GameListOpenTarget::SaveData: { 1260 case GameListOpenTarget::SaveData: {
1258 open_target = tr("Save Data"); 1261 open_target = tr("Save Data");
1259 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 1262 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
1260 ASSERT(program_id != 0);
1261 1263
1262 if (has_user_save) { 1264 if (has_user_save) {
1263 // User save data 1265 // User save data
@@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
1322} 1324}
1323 1325
1324void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { 1326void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1325 ASSERT(program_id != 0);
1326
1327 const QString shader_dir = 1327 const QString shader_dir =
1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); 1328 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
1329 const QString tranferable_shader_cache_folder_path = 1329 const QString transferable_shader_cache_folder_path =
1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); 1330 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1331 const QString transferable_shader_cache_file_path = 1331 const QString transferable_shader_cache_file_path =
1332 tranferable_shader_cache_folder_path + QDir::separator() + 1332 transferable_shader_cache_folder_path + QDir::separator() +
1333 QString::fromStdString(fmt::format("{:016X}.bin", program_id)); 1333 QString::fromStdString(fmt::format("{:016X}.bin", program_id));
1334 1334
1335 if (!QFile::exists(transferable_shader_cache_file_path)) { 1335 if (!QFile::exists(transferable_shader_cache_file_path)) {
@@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
1350 param << QDir::toNativeSeparators(transferable_shader_cache_file_path); 1350 param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
1351 QProcess::startDetached(explorer, param); 1351 QProcess::startDetached(explorer, param);
1352#else 1352#else
1353 QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); 1353 QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path));
1354#endif 1354#endif
1355} 1355}
1356 1356
@@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
1394 return true; 1394 return true;
1395} 1395}
1396 1396
1397void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
1398 const QString entry_type = [this, type] {
1399 switch (type) {
1400 case InstalledEntryType::Game:
1401 return tr("Contents");
1402 case InstalledEntryType::Update:
1403 return tr("Update");
1404 case InstalledEntryType::AddOnContent:
1405 return tr("DLC");
1406 default:
1407 return QString{};
1408 }
1409 }();
1410
1411 if (QMessageBox::question(
1412 this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type),
1413 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
1414 return;
1415 }
1416
1417 switch (type) {
1418 case InstalledEntryType::Game:
1419 RemoveBaseContent(program_id, entry_type);
1420 [[fallthrough]];
1421 case InstalledEntryType::Update:
1422 RemoveUpdateContent(program_id, entry_type);
1423 if (type != InstalledEntryType::Game) {
1424 break;
1425 }
1426 [[fallthrough]];
1427 case InstalledEntryType::AddOnContent:
1428 RemoveAddOnContent(program_id, entry_type);
1429 break;
1430 }
1431 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
1432 "game_list");
1433 game_list->PopulateAsync(UISettings::values.game_dirs);
1434}
1435
1436void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
1437 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1438 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
1439 fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
1440
1441 if (res) {
1442 QMessageBox::information(this, tr("Successfully Removed"),
1443 tr("Successfully removed the installed base game."));
1444 } else {
1445 QMessageBox::warning(
1446 this, tr("Error Removing %1").arg(entry_type),
1447 tr("The base game is not installed in the NAND and cannot be removed."));
1448 }
1449}
1450
1451void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) {
1452 const auto update_id = program_id | 0x800;
1453 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1454 const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
1455 fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
1456
1457 if (res) {
1458 QMessageBox::information(this, tr("Successfully Removed"),
1459 tr("Successfully removed the installed update."));
1460 } else {
1461 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
1462 tr("There is no update installed for this title."));
1463 }
1464}
1465
1466void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) {
1467 u32 count{};
1468 const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
1469 const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter(
1470 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1471
1472 for (const auto& entry : dlc_entries) {
1473 if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) {
1474 const auto res =
1475 fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) ||
1476 fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id);
1477 if (res) {
1478 ++count;
1479 }
1480 }
1481 }
1482
1483 if (count == 0) {
1484 QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
1485 tr("There are no DLC installed for this title."));
1486 return;
1487 }
1488
1489 QMessageBox::information(this, tr("Successfully Removed"),
1490 tr("Successfully removed %1 installed DLC.").arg(count));
1491}
1492
1493void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) {
1494 const QString question = [this, target] {
1495 switch (target) {
1496 case GameListRemoveTarget::ShaderCache:
1497 return tr("Delete Transferable Shader Cache?");
1498 case GameListRemoveTarget::CustomConfiguration:
1499 return tr("Remove Custom Game Configuration?");
1500 default:
1501 return QString{};
1502 }
1503 }();
1504
1505 if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
1506 QMessageBox::No) != QMessageBox::Yes) {
1507 return;
1508 }
1509
1510 switch (target) {
1511 case GameListRemoveTarget::ShaderCache:
1512 RemoveTransferableShaderCache(program_id);
1513 break;
1514 case GameListRemoveTarget::CustomConfiguration:
1515 RemoveCustomConfiguration(program_id);
1516 break;
1517 }
1518}
1519
1520void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
1521 const QString shader_dir =
1522 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
1523 const QString transferable_shader_cache_folder_path =
1524 shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
1525 const QString transferable_shader_cache_file_path =
1526 transferable_shader_cache_folder_path + QDir::separator() +
1527 QString::fromStdString(fmt::format("{:016X}.bin", program_id));
1528
1529 if (!QFile::exists(transferable_shader_cache_file_path)) {
1530 QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
1531 tr("A shader cache for this title does not exist."));
1532 return;
1533 }
1534
1535 if (QFile::remove(transferable_shader_cache_file_path)) {
1536 QMessageBox::information(this, tr("Successfully Removed"),
1537 tr("Successfully removed the transferable shader cache."));
1538 } else {
1539 QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
1540 tr("Failed to remove the transferable shader cache."));
1541 }
1542}
1543
1544void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
1545 const QString config_dir =
1546 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir));
1547 const QString custom_config_file_path =
1548 config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
1549
1550 if (!QFile::exists(custom_config_file_path)) {
1551 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
1552 tr("A custom configuration for this title does not exist."));
1553 return;
1554 }
1555
1556 if (QFile::remove(custom_config_file_path)) {
1557 QMessageBox::information(this, tr("Successfully Removed"),
1558 tr("Successfully removed the custom game configuration."));
1559 } else {
1560 QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
1561 tr("Failed to remove the custom game configuration."));
1562 }
1563}
1564
1397void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 1565void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
1398 const auto failed = [this] { 1566 const auto failed = [this] {
1399 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 1567 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
@@ -1714,9 +1882,9 @@ void GMainWindow::OnMenuInstallToNAND() {
1714 : tr("%n file(s) failed to install\n", "", failed_files.size())); 1882 : tr("%n file(s) failed to install\n", "", failed_files.size()));
1715 1883
1716 QMessageBox::information(this, tr("Install Results"), install_results); 1884 QMessageBox::information(this, tr("Install Results"), install_results);
1717 game_list->PopulateAsync(UISettings::values.game_dirs);
1718 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + 1885 FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
1719 "game_list"); 1886 "game_list");
1887 game_list->PopulateAsync(UISettings::values.game_dirs);
1720 ui.action_Install_File_NAND->setEnabled(true); 1888 ui.action_Install_File_NAND->setEnabled(true);
1721} 1889}
1722 1890
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index db573d606..73a44a3bf 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -32,6 +32,8 @@ class QPushButton;
32class QProgressDialog; 32class QProgressDialog;
33class WaitTreeWidget; 33class WaitTreeWidget;
34enum class GameListOpenTarget; 34enum class GameListOpenTarget;
35enum class GameListRemoveTarget;
36enum class InstalledEntryType;
35class GameListPlaceholder; 37class GameListPlaceholder;
36 38
37namespace Core::Frontend { 39namespace Core::Frontend {
@@ -198,6 +200,8 @@ private slots:
198 void OnGameListLoadFile(QString game_path); 200 void OnGameListLoadFile(QString game_path);
199 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 201 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
200 void OnTransferableShaderCacheOpenFile(u64 program_id); 202 void OnTransferableShaderCacheOpenFile(u64 program_id);
203 void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
204 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
201 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); 205 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
202 void OnGameListCopyTID(u64 program_id); 206 void OnGameListCopyTID(u64 program_id);
203 void OnGameListNavigateToGamedbEntry(u64 program_id, 207 void OnGameListNavigateToGamedbEntry(u64 program_id,
@@ -229,6 +233,11 @@ private slots:
229 void OnLanguageChanged(const QString& locale); 233 void OnLanguageChanged(const QString& locale);
230 234
231private: 235private:
236 void RemoveBaseContent(u64 program_id, const QString& entry_type);
237 void RemoveUpdateContent(u64 program_id, const QString& entry_type);
238 void RemoveAddOnContent(u64 program_id, const QString& entry_type);
239 void RemoveTransferableShaderCache(u64 program_id);
240 void RemoveCustomConfiguration(u64 program_id);
232 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 241 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
233 InstallResult InstallNSPXCI(const QString& filename); 242 InstallResult InstallNSPXCI(const QString& filename);
234 InstallResult InstallNCA(const QString& filename); 243 InstallResult InstallNCA(const QString& filename);
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 738c4b2fc..a51175f36 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -11,6 +11,8 @@ const Themes themes{{
11 {"Light Colorful", "colorful"}, 11 {"Light Colorful", "colorful"},
12 {"Dark", "qdarkstyle"}, 12 {"Dark", "qdarkstyle"},
13 {"Dark Colorful", "colorful_dark"}, 13 {"Dark Colorful", "colorful_dark"},
14 {"Midnight Blue", "qdarkstyle_midnight_blue"},
15 {"Midnight Blue Colorful", "colorful_midnight_blue"},
14}}; 16}};
15 17
16Values values = {}; 18Values values = {};
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 6cc65736d..ac7b9aef6 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -24,7 +24,7 @@ struct Shortcut {
24 ContextualShortcut shortcut; 24 ContextualShortcut shortcut;
25}; 25};
26 26
27using Themes = std::array<std::pair<const char*, const char*>, 4>; 27using Themes = std::array<std::pair<const char*, const char*>, 6>;
28extern const Themes themes; 28extern const Themes themes;
29 29
30struct GameDir { 30struct GameDir {