summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.h2
-rw-r--r--src/audio_core/stream.cpp4
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/assert.h5
-rw-r--r--src/common/bit_field.h19
-rw-r--r--src/common/bit_set.h244
-rw-r--r--src/common/logging/backend.cpp11
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/common/math_util.h16
-rw-r--r--src/common/string_util.cpp66
-rw-r--r--src/common/string_util.h41
-rw-r--r--src/common/thread.cpp35
-rw-r--r--src/common/thread.h20
-rw-r--r--src/common/x64/xbyak_abi.h222
-rw-r--r--src/common/x64/xbyak_util.h47
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/core.cpp136
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/cpu_core_manager.cpp142
-rw-r--r--src/core/cpu_core_manager.h59
-rw-r--r--src/core/file_sys/bis_factory.cpp9
-rw-r--r--src/core/file_sys/bis_factory.h4
-rw-r--r--src/core/file_sys/card_image.cpp2
-rw-r--r--src/core/file_sys/card_image.h16
-rw-r--r--src/core/file_sys/content_archive.cpp5
-rw-r--r--src/core/file_sys/content_archive.h3
-rw-r--r--src/core/file_sys/control_metadata.cpp6
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/errors.h25
-rw-r--r--src/core/file_sys/patch_manager.cpp62
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/file_sys/savedata_factory.cpp32
-rw-r--r--src/core/file_sys/savedata_factory.h3
-rw-r--r--src/core/file_sys/submission_package.cpp2
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp29
-rw-r--r--src/core/frontend/applets/software_keyboard.h54
-rw-r--r--src/core/frontend/input.h7
-rw-r--r--src/core/gdbstub/gdbstub.cpp123
-rw-r--r--src/core/hle/kernel/errors.h74
-rw-r--r--src/core/hle/kernel/handle_table.cpp11
-rw-r--r--src/core/hle/kernel/handle_table.h15
-rw-r--r--src/core/hle/kernel/kernel.cpp79
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/process.cpp87
-rw-r--r--src/core/hle/kernel/process.h24
-rw-r--r--src/core/hle/kernel/resource_limit.cpp75
-rw-r--r--src/core/hle/kernel/resource_limit.h98
-rw-r--r--src/core/hle/kernel/shared_memory.cpp22
-rw-r--r--src/core/hle/kernel/shared_memory.h48
-rw-r--r--src/core/hle/kernel/svc.cpp155
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp48
-rw-r--r--src/core/hle/kernel/thread.h2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp82
-rw-r--r--src/core/hle/kernel/vm_manager.h16
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp24
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp25
-rw-r--r--src/core/hle/service/acc/profile_manager.h13
-rw-r--r--src/core/hle/service/am/am.cpp443
-rw-r--r--src/core/hle/service/am/am.h59
-rw-r--r--src/core/hle/service/am/applet_ae.cpp34
-rw-r--r--src/core/hle/service/am/applet_ae.h6
-rw-r--r--src/core/hle/service/am/applet_oe.cpp21
-rw-r--r--src/core/hle/service/am/applet_oe.h6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp113
-rw-r--r--src/core/hle/service/am/applets/applets.h112
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp161
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h74
-rw-r--r--src/core/hle/service/am/applets/stub_applet.cpp70
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
-rw-r--r--src/core/hle/service/audio/audout_u.cpp28
-rw-r--r--src/core/hle/service/audio/audout_u.h3
-rw-r--r--src/core/hle/service/audio/audren_u.cpp31
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp38
-rw-r--r--src/core/hle/service/btm/btm.cpp108
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp57
-rw-r--r--src/core/hle/service/filesystem/filesystem.h5
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp169
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h41
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h7
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h9
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp510
-rw-r--r--src/core/hle/service/hid/controllers/npad.h45
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp22
-rw-r--r--src/core/hle/service/ldr/ldr.cpp380
-rw-r--r--src/core/hle/service/lm/lm.cpp13
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp64
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h4
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/spl/module.cpp10
-rw-r--r--src/core/hle/service/spl/module.h4
-rw-r--r--src/core/hle/service/time/interface.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp147
-rw-r--r--src/core/hle/service/time/time.h20
-rw-r--r--src/core/hle/service/vi/vi.cpp54
-rw-r--r--src/core/loader/nro.cpp12
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/settings.cpp50
-rw-r--r--src/core/settings.h326
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp53
-rw-r--r--src/video_core/engines/maxwell_3d.h212
-rw-r--r--src/video_core/engines/shader_bytecode.h8
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/macro_interpreter.cpp29
-rw-r--r--src/video_core/macro_interpreter.h4
-rw-r--r--src/video_core/memory_manager.cpp75
-rw-r--r--src/video_core/memory_manager.h8
-rw-r--r--src/video_core/rasterizer_cache.cpp7
-rw-r--r--src/video_core/rasterizer_cache.h44
-rw-r--r--src/video_core/renderer_base.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h4
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp338
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h52
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp149
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp186
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h132
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp920
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp318
-rw-r--r--src/video_core/renderer_opengl/gl_state.h85
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp5
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h77
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp8
-rw-r--r--src/video_core/surface.cpp33
-rw-r--r--src/video_core/surface.h118
-rw-r--r--src/video_core/textures/astc.cpp32
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/decoders.cpp103
-rw-r--r--src/video_core/textures/decoders.h11
-rw-r--r--src/video_core/textures/texture.h28
-rw-r--r--src/yuzu/CMakeLists.txt11
-rw-r--r--src/yuzu/applets/software_keyboard.cpp152
-rw-r--r--src/yuzu/applets/software_keyboard.h79
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp403
-rw-r--r--src/yuzu/configuration/config.h14
-rw-r--r--src/yuzu/configuration/configure_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.ui31
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp16
-rw-r--r--src/yuzu/configuration/configure_gamelist.h2
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui223
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.ui9
-rw-r--r--src/yuzu/configuration/configure_graphics.ui94
-rw-r--r--src/yuzu/configuration/configure_input.cpp436
-rw-r--r--src/yuzu/configuration/configure_input.h52
-rw-r--r--src/yuzu/configuration/configure_input.ui1064
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp508
-rw-r--r--src/yuzu/configuration/configure_input_player.h103
-rw-r--r--src/yuzu/configuration/configure_input_player.ui1164
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp213
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h68
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui261
-rw-r--r--src/yuzu/configuration/configure_system.cpp21
-rw-r--r--src/yuzu/configuration/configure_system.ui228
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.cpp42
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h32
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.ui199
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/game_list.cpp33
-rw-r--r--src/yuzu/game_list_worker.cpp24
-rw-r--r--src/yuzu/main.cpp83
-rw-r--r--src/yuzu/main.h12
-rw-r--r--src/yuzu/main.ui7
-rw-r--r--src/yuzu/ui_settings.h3
-rw-r--r--src/yuzu_cmd/config.cpp285
-rw-r--r--src/yuzu_cmd/default_ini.h9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp16
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
195 files changed, 10558 insertions, 4319 deletions
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 046417da3..71ba4be40 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -143,7 +143,7 @@ struct AuxInfo {
143 std::array<u8, 24> output_mix_buffers; 143 std::array<u8, 24> output_mix_buffers;
144 u32_le mix_buffer_count; 144 u32_le mix_buffer_count;
145 u32_le sample_rate; // Stored in the aux buffer currently 145 u32_le sample_rate; // Stored in the aux buffer currently
146 u32_le sampe_count; 146 u32_le sample_count;
147 u64_le send_buffer_info; 147 u64_le send_buffer_info;
148 u64_le send_buffer_base; 148 u64_le send_buffer_base;
149 149
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 742a5e0a0..f35628e45 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -11,7 +11,6 @@
11#include "audio_core/stream.h" 11#include "audio_core/stream.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/microprofile.h"
15#include "core/core_timing.h" 14#include "core/core_timing.h"
16#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
17#include "core/settings.h" 16#include "core/settings.h"
@@ -104,10 +103,7 @@ void Stream::PlayNextBuffer() {
104 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 103 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
105} 104}
106 105
107MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
108
109void Stream::ReleaseActiveBuffer() { 106void Stream::ReleaseActiveBuffer() {
110 MICROPROFILE_SCOPE(AudioOutput);
111 ASSERT(active_buffer); 107 ASSERT(active_buffer);
112 released_buffers.push(std::move(active_buffer)); 108 released_buffers.push(std::move(active_buffer));
113 release_callback(); 109 release_callback();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index eccd8f64a..a5e71d879 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -44,7 +44,6 @@ add_library(common STATIC
44 detached_tasks.cpp 44 detached_tasks.cpp
45 detached_tasks.h 45 detached_tasks.h
46 bit_field.h 46 bit_field.h
47 bit_set.h
48 cityhash.cpp 47 cityhash.cpp
49 cityhash.h 48 cityhash.h
50 color.h 49 color.h
@@ -95,14 +94,9 @@ if(ARCHITECTURE_x86_64)
95 PRIVATE 94 PRIVATE
96 x64/cpu_detect.cpp 95 x64/cpu_detect.cpp
97 x64/cpu_detect.h 96 x64/cpu_detect.h
98 x64/xbyak_abi.h
99 x64/xbyak_util.h
100 ) 97 )
101endif() 98endif()
102 99
103create_target_directory_groups(common) 100create_target_directory_groups(common)
104 101
105target_link_libraries(common PUBLIC Boost::boost fmt microprofile) 102target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
106if (ARCHITECTURE_x86_64)
107 target_link_libraries(common PRIVATE xbyak)
108endif()
diff --git a/src/common/assert.h b/src/common/assert.h
index 0d4eddc19..6002f7ab1 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -52,5 +52,8 @@ __declspec(noinline, noreturn)
52#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) 52#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
53#endif 53#endif
54 54
55#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") 55#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
56#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) 56#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
57
58#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
59#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index bf803da8d..21e07925d 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -117,21 +117,21 @@ private:
117 // We don't delete it because we want BitField to be trivially copyable. 117 // We don't delete it because we want BitField to be trivially copyable.
118 constexpr BitField& operator=(const BitField&) = default; 118 constexpr BitField& operator=(const BitField&) = default;
119 119
120 // StorageType is T for non-enum types and the underlying type of T if 120 // UnderlyingType is T for non-enum types and the underlying type of T if
121 // T is an enumeration. Note that T is wrapped within an enable_if in the 121 // T is an enumeration. Note that T is wrapped within an enable_if in the
122 // former case to workaround compile errors which arise when using 122 // former case to workaround compile errors which arise when using
123 // std::underlying_type<T>::type directly. 123 // std::underlying_type<T>::type directly.
124 using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, 124 using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
125 std::enable_if<true, T>>::type; 125 std::enable_if<true, T>>::type;
126 126
127 // Unsigned version of StorageType 127 // We store the value as the unsigned type to avoid undefined behaviour on value shifting
128 using StorageTypeU = std::make_unsigned_t<StorageType>; 128 using StorageType = std::make_unsigned_t<UnderlyingType>;
129 129
130public: 130public:
131 /// Constants to allow limited introspection of fields if needed 131 /// Constants to allow limited introspection of fields if needed
132 static constexpr std::size_t position = Position; 132 static constexpr std::size_t position = Position;
133 static constexpr std::size_t bits = Bits; 133 static constexpr std::size_t bits = Bits;
134 static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; 134 static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
135 135
136 /** 136 /**
137 * Formats a value by masking and shifting it according to the field parameters. A value 137 * Formats a value by masking and shifting it according to the field parameters. A value
@@ -148,11 +148,12 @@ public:
148 * union in a constexpr context. 148 * union in a constexpr context.
149 */ 149 */
150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) { 150 static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
151 if (std::numeric_limits<T>::is_signed) { 151 if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
152 std::size_t shift = 8 * sizeof(T) - bits; 152 std::size_t shift = 8 * sizeof(T) - bits;
153 return (T)((storage << (shift - position)) >> shift); 153 return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
154 shift);
154 } else { 155 } else {
155 return (T)((storage & mask) >> position); 156 return static_cast<T>((storage & mask) >> position);
156 } 157 }
157 } 158 }
158 159
diff --git a/src/common/bit_set.h b/src/common/bit_set.h
deleted file mode 100644
index 5cd1352b2..000000000
--- a/src/common/bit_set.h
+++ /dev/null
@@ -1,244 +0,0 @@
1// This file is under the public domain.
2
3#pragma once
4
5#include <cstddef>
6#ifdef _WIN32
7#include <intrin.h>
8#endif
9#include <initializer_list>
10#include <new>
11#include <type_traits>
12#include "common/common_types.h"
13
14// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
15namespace Common {
16
17// Helper functions:
18
19#ifdef _MSC_VER
20template <typename T>
21static inline int CountSetBits(T v) {
22 // from https://graphics.stanford.edu/~seander/bithacks.html
23 // GCC has this built in, but MSVC's intrinsic will only emit the actual
24 // POPCNT instruction, which we're not depending on
25 v = v - ((v >> 1) & (T) ~(T)0 / 3);
26 v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
27 v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
28 return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
29}
30static inline int LeastSignificantSetBit(u8 val) {
31 unsigned long index;
32 _BitScanForward(&index, val);
33 return (int)index;
34}
35static inline int LeastSignificantSetBit(u16 val) {
36 unsigned long index;
37 _BitScanForward(&index, val);
38 return (int)index;
39}
40static inline int LeastSignificantSetBit(u32 val) {
41 unsigned long index;
42 _BitScanForward(&index, val);
43 return (int)index;
44}
45static inline int LeastSignificantSetBit(u64 val) {
46 unsigned long index;
47 _BitScanForward64(&index, val);
48 return (int)index;
49}
50#else
51static inline int CountSetBits(u8 val) {
52 return __builtin_popcount(val);
53}
54static inline int CountSetBits(u16 val) {
55 return __builtin_popcount(val);
56}
57static inline int CountSetBits(u32 val) {
58 return __builtin_popcount(val);
59}
60static inline int CountSetBits(u64 val) {
61 return __builtin_popcountll(val);
62}
63static inline int LeastSignificantSetBit(u8 val) {
64 return __builtin_ctz(val);
65}
66static inline int LeastSignificantSetBit(u16 val) {
67 return __builtin_ctz(val);
68}
69static inline int LeastSignificantSetBit(u32 val) {
70 return __builtin_ctz(val);
71}
72static inline int LeastSignificantSetBit(u64 val) {
73 return __builtin_ctzll(val);
74}
75#endif
76
77// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
78// using the set bits of an integer to represent a set of integers. Like that
79// class, it acts like an array of bools:
80// BitSet32 bs;
81// bs[1] = true;
82// but also like the underlying integer ([0] = least significant bit):
83// BitSet32 bs2 = ...;
84// bs = (bs ^ bs2) & BitSet32(0xffff);
85// The following additional functionality is provided:
86// - Construction using an initializer list.
87// BitSet bs { 1, 2, 4, 8 };
88// - Efficiently iterating through the set bits:
89// for (int i : bs)
90// [i is the *index* of a set bit]
91// (This uses the appropriate CPU instruction to find the next set bit in one
92// operation.)
93// - Counting set bits using .Count() - see comment on that method.
94
95// TODO: use constexpr when MSVC gets out of the Dark Ages
96
97template <typename IntTy>
98class BitSet {
99 static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
100
101public:
102 // A reference to a particular bit, returned from operator[].
103 class Ref {
104 public:
105 Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
106 Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
107 operator bool() const {
108 return (m_bs->m_val & m_mask) != 0;
109 }
110 bool operator=(bool set) {
111 m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
112 return set;
113 }
114
115 private:
116 BitSet* m_bs;
117 IntTy m_mask;
118 };
119
120 // A STL-like iterator is required to be able to use range-based for loops.
121 class Iterator {
122 public:
123 Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
124 Iterator(IntTy val) : m_val(val), m_bit(0) {}
125 Iterator& operator=(Iterator other) {
126 new (this) Iterator(other);
127 return *this;
128 }
129 int operator*() {
130 return m_bit + ComputeLsb();
131 }
132 Iterator& operator++() {
133 int lsb = ComputeLsb();
134 m_val >>= lsb + 1;
135 m_bit += lsb + 1;
136 m_has_lsb = false;
137 return *this;
138 }
139 Iterator operator++(int _) {
140 Iterator other(*this);
141 ++*this;
142 return other;
143 }
144 bool operator==(Iterator other) const {
145 return m_val == other.m_val;
146 }
147 bool operator!=(Iterator other) const {
148 return m_val != other.m_val;
149 }
150
151 private:
152 int ComputeLsb() {
153 if (!m_has_lsb) {
154 m_lsb = LeastSignificantSetBit(m_val);
155 m_has_lsb = true;
156 }
157 return m_lsb;
158 }
159 IntTy m_val;
160 int m_bit;
161 int m_lsb = -1;
162 bool m_has_lsb = false;
163 };
164
165 BitSet() : m_val(0) {}
166 explicit BitSet(IntTy val) : m_val(val) {}
167 BitSet(std::initializer_list<int> init) {
168 m_val = 0;
169 for (int bit : init)
170 m_val |= (IntTy)1 << bit;
171 }
172
173 static BitSet AllTrue(std::size_t count) {
174 return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
175 }
176
177 Ref operator[](std::size_t bit) {
178 return Ref(this, (IntTy)1 << bit);
179 }
180 const Ref operator[](std::size_t bit) const {
181 return (*const_cast<BitSet*>(this))[bit];
182 }
183 bool operator==(BitSet other) const {
184 return m_val == other.m_val;
185 }
186 bool operator!=(BitSet other) const {
187 return m_val != other.m_val;
188 }
189 bool operator<(BitSet other) const {
190 return m_val < other.m_val;
191 }
192 bool operator>(BitSet other) const {
193 return m_val > other.m_val;
194 }
195 BitSet operator|(BitSet other) const {
196 return BitSet(m_val | other.m_val);
197 }
198 BitSet operator&(BitSet other) const {
199 return BitSet(m_val & other.m_val);
200 }
201 BitSet operator^(BitSet other) const {
202 return BitSet(m_val ^ other.m_val);
203 }
204 BitSet operator~() const {
205 return BitSet(~m_val);
206 }
207 BitSet& operator|=(BitSet other) {
208 return *this = *this | other;
209 }
210 BitSet& operator&=(BitSet other) {
211 return *this = *this & other;
212 }
213 BitSet& operator^=(BitSet other) {
214 return *this = *this ^ other;
215 }
216 operator u32() = delete;
217 operator bool() {
218 return m_val != 0;
219 }
220
221 // Warning: Even though on modern CPUs this is a single fast instruction,
222 // Dolphin's official builds do not currently assume POPCNT support on x86,
223 // so slower explicit bit twiddling is generated. Still should generally
224 // be faster than a loop.
225 unsigned int Count() const {
226 return CountSetBits(m_val);
227 }
228
229 Iterator begin() const {
230 return Iterator(m_val);
231 }
232 Iterator end() const {
233 return Iterator(0);
234 }
235
236 IntTy m_val;
237};
238
239} // namespace Common
240
241typedef Common::BitSet<u8> BitSet8;
242typedef Common::BitSet<u16> BitSet16;
243typedef Common::BitSet<u32> BitSet32;
244typedef Common::BitSet<u64> BitSet64;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 6d5218465..5753b871a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -12,7 +12,8 @@
12#include <thread> 12#include <thread>
13#include <vector> 13#include <vector>
14#ifdef _WIN32 14#ifdef _WIN32
15#include <share.h> // For _SH_DENYWR 15#include <share.h> // For _SH_DENYWR
16#include <windows.h> // For OutputDebugStringA
16#else 17#else
17#define _SH_DENYWR 0 18#define _SH_DENYWR 0
18#endif 19#endif
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
139 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { 140 if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
140 return; 141 return;
141 } 142 }
142 bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); 143 bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
143 if (entry.log_level >= Level::Error) { 144 if (entry.log_level >= Level::Error) {
144 file.Flush(); 145 file.Flush();
145 } 146 }
146} 147}
147 148
149void DebuggerBackend::Write(const Entry& entry) {
150#ifdef _WIN32
151 ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
152#endif
153}
154
148/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. 155/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
149#define ALL_LOG_CLASSES() \ 156#define ALL_LOG_CLASSES() \
150 CLS(Log) \ 157 CLS(Log) \
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 11edbf1b6..91bb0c309 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -103,6 +103,20 @@ private:
103 std::size_t bytes_written; 103 std::size_t bytes_written;
104}; 104};
105 105
106/**
107 * Backend that writes to Visual Studio's output window
108 */
109class DebuggerBackend : public Backend {
110public:
111 static const char* Name() {
112 return "debugger";
113 }
114 const char* GetName() const override {
115 return Name();
116 }
117 void Write(const Entry& entry) override;
118};
119
106void AddBackend(std::unique_ptr<Backend> backend); 120void AddBackend(std::unique_ptr<Backend> backend);
107 121
108void RemoveBackend(std::string_view backend_name); 122void RemoveBackend(std::string_view backend_name);
diff --git a/src/common/math_util.h b/src/common/math_util.h
index 343cdd902..94b4394c5 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -4,18 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <cstdlib> 7#include <cstdlib>
9#include <type_traits> 8#include <type_traits>
10 9
11namespace MathUtil { 10namespace MathUtil {
12 11
13static constexpr float PI = 3.14159265f; 12constexpr float PI = 3.14159265f;
14
15inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1,
16 unsigned length1) {
17 return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
18}
19 13
20template <class T> 14template <class T>
21struct Rectangle { 15struct Rectangle {
@@ -24,16 +18,16 @@ struct Rectangle {
24 T right{}; 18 T right{};
25 T bottom{}; 19 T bottom{};
26 20
27 Rectangle() = default; 21 constexpr Rectangle() = default;
28 22
29 Rectangle(T left, T top, T right, T bottom) 23 constexpr Rectangle(T left, T top, T right, T bottom)
30 : left(left), top(top), right(right), bottom(bottom) {} 24 : left(left), top(top), right(right), bottom(bottom) {}
31 25
32 T GetWidth() const { 26 T GetWidth() const {
33 return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); 27 return std::abs(static_cast<std::make_signed_t<T>>(right - left));
34 } 28 }
35 T GetHeight() const { 29 T GetHeight() const {
36 return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); 30 return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
37 } 31 }
38 Rectangle<T> TranslateX(const T x) const { 32 Rectangle<T> TranslateX(const T x) const {
39 return Rectangle{left + x, top, right + x, bottom}; 33 return Rectangle{left + x, top, right + x, bottom};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 731d1db34..959f278aa 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -4,11 +4,10 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cctype> 6#include <cctype>
7#include <cerrno>
8#include <codecvt> 7#include <codecvt>
9#include <cstdio>
10#include <cstdlib> 8#include <cstdlib>
11#include <cstring> 9#include <locale>
10#include <sstream>
12#include "common/common_paths.h" 11#include "common/common_paths.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14#include "common/string_util.h" 13#include "common/string_util.h"
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
33 return str; 32 return str;
34} 33}
35 34
36// For Debugging. Read out an u8 array.
37std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
38 std::ostringstream oss;
39 oss << std::setfill('0') << std::hex;
40
41 for (int line = 0; size; ++data, --size) {
42 oss << std::setw(2) << (int)*data;
43
44 if (line_len == ++line) {
45 oss << '\n';
46 line = 0;
47 } else if (spaces)
48 oss << ' ';
49 }
50
51 return oss.str();
52}
53
54std::string StringFromBuffer(const std::vector<u8>& data) { 35std::string StringFromBuffer(const std::vector<u8>& data) {
55 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); 36 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
56} 37}
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
75 return s; 56 return s;
76} 57}
77 58
78bool TryParse(const std::string& str, u32* const output) {
79 char* endptr = nullptr;
80
81 // Reset errno to a value other than ERANGE
82 errno = 0;
83
84 unsigned long value = strtoul(str.c_str(), &endptr, 0);
85
86 if (!endptr || *endptr)
87 return false;
88
89 if (errno == ERANGE)
90 return false;
91
92#if ULONG_MAX > UINT_MAX
93 if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
94 return false;
95#endif
96
97 *output = static_cast<u32>(value);
98 return true;
99}
100
101bool TryParse(const std::string& str, bool* const output) {
102 if ("1" == str || "true" == ToLower(str))
103 *output = true;
104 else if ("0" == str || "false" == ToLower(str))
105 *output = false;
106 else
107 return false;
108
109 return true;
110}
111
112std::string StringFromBool(bool value) { 59std::string StringFromBool(bool value) {
113 return value ? "True" : "False"; 60 return value ? "True" : "False";
114} 61}
@@ -267,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
267 return std::string(buffer, len); 214 return std::string(buffer, len);
268} 215}
269 216
217std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
218 std::size_t max_len) {
219 std::size_t len = 0;
220 while (len < max_len && buffer[len] != '\0')
221 ++len;
222
223 return std::u16string(buffer.begin(), buffer.begin() + len);
224}
225
270const char* TrimSourcePath(const char* path, const char* root) { 226const char* TrimSourcePath(const char* path, const char* root) {
271 const char* p = path; 227 const char* p = path;
272 228
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 32bf6a19c..583fd05e6 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <iomanip>
9#include <sstream>
10#include <string> 8#include <string>
11#include <vector> 9#include <vector>
12#include "common/common_types.h" 10#include "common/common_types.h"
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
19/// Make a string uppercase 17/// Make a string uppercase
20std::string ToUpper(std::string str); 18std::string ToUpper(std::string str);
21 19
22std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
23
24std::string StringFromBuffer(const std::vector<u8>& data); 20std::string StringFromBuffer(const std::vector<u8>& data);
25 21
26std::string StripSpaces(const std::string& s); 22std::string StripSpaces(const std::string& s);
27std::string StripQuotes(const std::string& s); 23std::string StripQuotes(const std::string& s);
28 24
29// Thousand separator. Turns 12345678 into 12,345,678
30template <typename I>
31std::string ThousandSeparate(I value, int spaces = 0) {
32 std::ostringstream oss;
33
34// std::locale("") seems to be broken on many platforms
35#if defined _WIN32 || (defined __linux__ && !defined __clang__)
36 oss.imbue(std::locale(""));
37#endif
38 oss << std::setw(spaces) << value;
39
40 return oss.str();
41}
42
43std::string StringFromBool(bool value); 25std::string StringFromBool(bool value);
44 26
45bool TryParse(const std::string& str, bool* output);
46bool TryParse(const std::string& str, u32* output);
47
48template <typename N>
49static bool TryParse(const std::string& str, N* const output) {
50 std::istringstream iss(str);
51
52 N tmp = 0;
53 if (iss >> tmp) {
54 *output = tmp;
55 return true;
56 } else
57 return false;
58}
59
60std::string TabsToSpaces(int tab_size, std::string in); 27std::string TabsToSpaces(int tab_size, std::string in);
61 28
62void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
@@ -100,6 +67,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
100std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); 67std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
101 68
102/** 69/**
70 * Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
71 * null-terminated, then the string ends at the greatest multiple of two less then or equal to
72 * max_len_bytes.
73 */
74std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
75 std::size_t max_len);
76
77/**
103 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's 78 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
104 * intended to be used to strip a system-specific build directory from the `__FILE__` macro, 79 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
105 * leaving only the path relative to the sources root. 80 * leaving only the path relative to the sources root.
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 9e207118f..5144c0d9f 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -25,23 +25,6 @@
25 25
26namespace Common { 26namespace Common {
27 27
28int CurrentThreadId() {
29#ifdef _MSC_VER
30 return GetCurrentThreadId();
31#elif defined __APPLE__
32 return mach_thread_self();
33#else
34 return 0;
35#endif
36}
37
38#ifdef _WIN32
39// Supporting functions
40void SleepCurrentThread(int ms) {
41 Sleep(ms);
42}
43#endif
44
45#ifdef _MSC_VER 28#ifdef _MSC_VER
46 29
47void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { 30void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
@@ -62,7 +45,7 @@ void SwitchCurrentThread() {
62 45
63// This is implemented much nicer in upcoming msvc++, see: 46// This is implemented much nicer in upcoming msvc++, see:
64// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx 47// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
65void SetCurrentThreadName(const char* szThreadName) { 48void SetCurrentThreadName(const char* name) {
66 static const DWORD MS_VC_EXCEPTION = 0x406D1388; 49 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
67 50
68#pragma pack(push, 8) 51#pragma pack(push, 8)
@@ -75,7 +58,7 @@ void SetCurrentThreadName(const char* szThreadName) {
75#pragma pack(pop) 58#pragma pack(pop)
76 59
77 info.dwType = 0x1000; 60 info.dwType = 0x1000;
78 info.szName = szThreadName; 61 info.szName = name;
79 info.dwThreadID = -1; // dwThreadID; 62 info.dwThreadID = -1; // dwThreadID;
80 info.dwFlags = 0; 63 info.dwFlags = 0;
81 64
@@ -107,10 +90,6 @@ void SetCurrentThreadAffinity(u32 mask) {
107} 90}
108 91
109#ifndef _WIN32 92#ifndef _WIN32
110void SleepCurrentThread(int ms) {
111 usleep(1000 * ms);
112}
113
114void SwitchCurrentThread() { 93void SwitchCurrentThread() {
115 usleep(1000 * 1); 94 usleep(1000 * 1);
116} 95}
@@ -118,15 +97,15 @@ void SwitchCurrentThread() {
118 97
119// MinGW with the POSIX threading model does not support pthread_setname_np 98// MinGW with the POSIX threading model does not support pthread_setname_np
120#if !defined(_WIN32) || defined(_MSC_VER) 99#if !defined(_WIN32) || defined(_MSC_VER)
121void SetCurrentThreadName(const char* szThreadName) { 100void SetCurrentThreadName(const char* name) {
122#ifdef __APPLE__ 101#ifdef __APPLE__
123 pthread_setname_np(szThreadName); 102 pthread_setname_np(name);
124#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) 103#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
125 pthread_set_name_np(pthread_self(), szThreadName); 104 pthread_set_name_np(pthread_self(), name);
126#elif defined(__NetBSD__) 105#elif defined(__NetBSD__)
127 pthread_setname_np(pthread_self(), "%s", (void*)szThreadName); 106 pthread_setname_np(pthread_self(), "%s", (void*)name);
128#else 107#else
129 pthread_setname_np(pthread_self(), szThreadName); 108 pthread_setname_np(pthread_self(), name);
130#endif 109#endif
131} 110}
132#endif 111#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index 6cbdb96a3..2cf74452d 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -13,15 +13,8 @@
13 13
14namespace Common { 14namespace Common {
15 15
16int CurrentThreadId();
17
18void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
19void SetCurrentThreadAffinity(u32 mask);
20
21class Event { 16class Event {
22public: 17public:
23 Event() : is_set(false) {}
24
25 void Set() { 18 void Set() {
26 std::lock_guard<std::mutex> lk(mutex); 19 std::lock_guard<std::mutex> lk(mutex);
27 if (!is_set) { 20 if (!is_set) {
@@ -53,14 +46,14 @@ public:
53 } 46 }
54 47
55private: 48private:
56 bool is_set; 49 bool is_set = false;
57 std::condition_variable condvar; 50 std::condition_variable condvar;
58 std::mutex mutex; 51 std::mutex mutex;
59}; 52};
60 53
61class Barrier { 54class Barrier {
62public: 55public:
63 explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {} 56 explicit Barrier(std::size_t count_) : count(count_) {}
64 57
65 /// Blocks until all "count" threads have called Sync() 58 /// Blocks until all "count" threads have called Sync()
66 void Sync() { 59 void Sync() {
@@ -80,12 +73,13 @@ public:
80private: 73private:
81 std::condition_variable condvar; 74 std::condition_variable condvar;
82 std::mutex mutex; 75 std::mutex mutex;
83 const std::size_t count; 76 std::size_t count;
84 std::size_t waiting; 77 std::size_t waiting = 0;
85 std::size_t generation; // Incremented once each time the barrier is used 78 std::size_t generation = 0; // Incremented once each time the barrier is used
86}; 79};
87 80
88void SleepCurrentThread(int ms); 81void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
82void SetCurrentThreadAffinity(u32 mask);
89void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms 83void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
90void SetCurrentThreadName(const char* name); 84void SetCurrentThreadName(const char* name);
91 85
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
deleted file mode 100644
index 636a5c0f9..000000000
--- a/src/common/x64/xbyak_abi.h
+++ /dev/null
@@ -1,222 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <initializer_list>
8#include <xbyak.h>
9#include "common/assert.h"
10#include "common/bit_set.h"
11
12namespace Common::X64 {
13
14inline int RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers.");
18 ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20}
21
22inline Xbyak::Reg64 IndexToReg64(int reg_index) {
23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(reg_index);
25}
26
27inline Xbyak::Xmm IndexToXmm(int reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(reg_index - 16);
30}
31
32inline Xbyak::Reg IndexToReg(int reg_index) {
33 if (reg_index < 16) {
34 return IndexToReg64(reg_index);
35 } else {
36 return IndexToXmm(reg_index);
37 }
38}
39
40inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
41 BitSet32 bits;
42 for (const Xbyak::Reg& reg : regs) {
43 bits[RegToIndex(reg)] = true;
44 }
45 return bits;
46}
47
48const BitSet32 ABI_ALL_GPRS(0x0000FFFF);
49const BitSet32 ABI_ALL_XMMS(0xFFFF0000);
50
51#ifdef _WIN32
52
53// Microsoft x64 ABI
54const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
55const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
56const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
57const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
58const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
59
60const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
61 // GPRs
62 Xbyak::util::rcx,
63 Xbyak::util::rdx,
64 Xbyak::util::r8,
65 Xbyak::util::r9,
66 Xbyak::util::r10,
67 Xbyak::util::r11,
68 // XMMs
69 Xbyak::util::xmm0,
70 Xbyak::util::xmm1,
71 Xbyak::util::xmm2,
72 Xbyak::util::xmm3,
73 Xbyak::util::xmm4,
74 Xbyak::util::xmm5,
75});
76
77const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
78 // GPRs
79 Xbyak::util::rbx,
80 Xbyak::util::rsi,
81 Xbyak::util::rdi,
82 Xbyak::util::rbp,
83 Xbyak::util::r12,
84 Xbyak::util::r13,
85 Xbyak::util::r14,
86 Xbyak::util::r15,
87 // XMMs
88 Xbyak::util::xmm6,
89 Xbyak::util::xmm7,
90 Xbyak::util::xmm8,
91 Xbyak::util::xmm9,
92 Xbyak::util::xmm10,
93 Xbyak::util::xmm11,
94 Xbyak::util::xmm12,
95 Xbyak::util::xmm13,
96 Xbyak::util::xmm14,
97 Xbyak::util::xmm15,
98});
99
100constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
101
102#else
103
104// System V x86-64 ABI
105const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
106const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
107const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
108const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
109const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
110
111const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
112 // GPRs
113 Xbyak::util::rcx,
114 Xbyak::util::rdx,
115 Xbyak::util::rdi,
116 Xbyak::util::rsi,
117 Xbyak::util::r8,
118 Xbyak::util::r9,
119 Xbyak::util::r10,
120 Xbyak::util::r11,
121 // XMMs
122 Xbyak::util::xmm0,
123 Xbyak::util::xmm1,
124 Xbyak::util::xmm2,
125 Xbyak::util::xmm3,
126 Xbyak::util::xmm4,
127 Xbyak::util::xmm5,
128 Xbyak::util::xmm6,
129 Xbyak::util::xmm7,
130 Xbyak::util::xmm8,
131 Xbyak::util::xmm9,
132 Xbyak::util::xmm10,
133 Xbyak::util::xmm11,
134 Xbyak::util::xmm12,
135 Xbyak::util::xmm13,
136 Xbyak::util::xmm14,
137 Xbyak::util::xmm15,
138});
139
140const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
141 // GPRs
142 Xbyak::util::rbx,
143 Xbyak::util::rbp,
144 Xbyak::util::r12,
145 Xbyak::util::r13,
146 Xbyak::util::r14,
147 Xbyak::util::r15,
148});
149
150constexpr std::size_t ABI_SHADOW_SPACE = 0;
151
152#endif
153
154inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
155 std::size_t needed_frame_size, s32* out_subtraction,
156 s32* out_xmm_offset) {
157 int count = (regs & ABI_ALL_GPRS).Count();
158 rsp_alignment -= count * 8;
159 std::size_t subtraction = 0;
160 int xmm_count = (regs & ABI_ALL_XMMS).Count();
161 if (xmm_count) {
162 // If we have any XMMs to save, we must align the stack here.
163 subtraction = rsp_alignment & 0xF;
164 }
165 subtraction += 0x10 * xmm_count;
166 std::size_t xmm_base_subtraction = subtraction;
167 subtraction += needed_frame_size;
168 subtraction += ABI_SHADOW_SPACE;
169 // Final alignment.
170 rsp_alignment -= subtraction;
171 subtraction += rsp_alignment & 0xF;
172
173 *out_subtraction = (s32)subtraction;
174 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
175}
176
177inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
178 std::size_t rsp_alignment,
179 std::size_t needed_frame_size = 0) {
180 s32 subtraction, xmm_offset;
181 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
182
183 for (int reg_index : (regs & ABI_ALL_GPRS)) {
184 code.push(IndexToReg64(reg_index));
185 }
186
187 if (subtraction != 0) {
188 code.sub(code.rsp, subtraction);
189 }
190
191 for (int reg_index : (regs & ABI_ALL_XMMS)) {
192 code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index));
193 xmm_offset += 0x10;
194 }
195
196 return ABI_SHADOW_SPACE;
197}
198
199inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
200 std::size_t rsp_alignment,
201 std::size_t needed_frame_size = 0) {
202 s32 subtraction, xmm_offset;
203 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
204
205 for (int reg_index : (regs & ABI_ALL_XMMS)) {
206 code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]);
207 xmm_offset += 0x10;
208 }
209
210 if (subtraction != 0) {
211 code.add(code.rsp, subtraction);
212 }
213
214 // GPRs need to be popped in reverse order
215 for (int reg_index = 15; reg_index >= 0; reg_index--) {
216 if (regs[reg_index]) {
217 code.pop(IndexToReg64(reg_index));
218 }
219 }
220}
221
222} // namespace Common::X64
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
deleted file mode 100644
index 5cc8a8c76..000000000
--- a/src/common/x64/xbyak_util.h
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8#include <xbyak.h>
9#include "common/x64/xbyak_abi.h"
10
11namespace Common::X64 {
12
13// Constants for use with cmpps/cmpss
14enum {
15 CMP_EQ = 0,
16 CMP_LT = 1,
17 CMP_LE = 2,
18 CMP_UNORD = 3,
19 CMP_NEQ = 4,
20 CMP_NLT = 5,
21 CMP_NLE = 6,
22 CMP_ORD = 7,
23};
24
25inline bool IsWithin2G(uintptr_t ref, uintptr_t target) {
26 u64 distance = target - (ref + 5);
27 return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
28}
29
30inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
31 return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
32}
33
34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
36 static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
37 std::size_t addr = reinterpret_cast<std::size_t>(f);
38 if (IsWithin2G(code, addr)) {
39 code.call(f);
40 } else {
41 // ABI_RETURN is a safe temp register to use before a call
42 code.mov(ABI_RETURN, addr);
43 code.call(ABI_RETURN);
44 }
45}
46
47} // namespace Common::X64
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 64fdf38cd..e1f21a764 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -12,6 +12,8 @@ add_library(core STATIC
12 core_timing.h 12 core_timing.h
13 core_timing_util.cpp 13 core_timing_util.cpp
14 core_timing_util.h 14 core_timing_util.h
15 cpu_core_manager.cpp
16 cpu_core_manager.h
15 crypto/aes_util.cpp 17 crypto/aes_util.cpp
16 crypto/aes_util.h 18 crypto/aes_util.h
17 crypto/encryption_layer.cpp 19 crypto/encryption_layer.cpp
@@ -77,6 +79,8 @@ add_library(core STATIC
77 file_sys/vfs_vector.h 79 file_sys/vfs_vector.h
78 file_sys/xts_archive.cpp 80 file_sys/xts_archive.cpp
79 file_sys/xts_archive.h 81 file_sys/xts_archive.h
82 frontend/applets/software_keyboard.cpp
83 frontend/applets/software_keyboard.h
80 frontend/emu_window.cpp 84 frontend/emu_window.cpp
81 frontend/emu_window.h 85 frontend/emu_window.h
82 frontend/framebuffer_layout.cpp 86 frontend/framebuffer_layout.cpp
@@ -150,6 +154,12 @@ add_library(core STATIC
150 hle/service/am/applet_ae.h 154 hle/service/am/applet_ae.h
151 hle/service/am/applet_oe.cpp 155 hle/service/am/applet_oe.cpp
152 hle/service/am/applet_oe.h 156 hle/service/am/applet_oe.h
157 hle/service/am/applets/applets.cpp
158 hle/service/am/applets/applets.h
159 hle/service/am/applets/software_keyboard.cpp
160 hle/service/am/applets/software_keyboard.h
161 hle/service/am/applets/stub_applet.cpp
162 hle/service/am/applets/stub_applet.h
153 hle/service/am/idle.cpp 163 hle/service/am/idle.cpp
154 hle/service/am/idle.h 164 hle/service/am/idle.h
155 hle/service/am/omm.cpp 165 hle/service/am/omm.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6d5b5a2d0..795fabc65 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,6 +14,7 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_cpu.h" 15#include "core/core_cpu.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/cpu_core_manager.h"
17#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
18#include "core/file_sys/vfs_concat.h" 19#include "core/file_sys/vfs_concat.h"
19#include "core/file_sys/vfs_real.h" 20#include "core/file_sys/vfs_real.h"
@@ -23,12 +24,13 @@
23#include "core/hle/kernel/process.h" 24#include "core/hle/kernel/process.h"
24#include "core/hle/kernel/scheduler.h" 25#include "core/hle/kernel/scheduler.h"
25#include "core/hle/kernel/thread.h" 26#include "core/hle/kernel/thread.h"
27#include "core/hle/service/am/applets/software_keyboard.h"
26#include "core/hle/service/service.h" 28#include "core/hle/service/service.h"
27#include "core/hle/service/sm/sm.h" 29#include "core/hle/service/sm/sm.h"
28#include "core/loader/loader.h" 30#include "core/loader/loader.h"
29#include "core/perf_stats.h" 31#include "core/perf_stats.h"
30#include "core/settings.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "frontend/applets/software_keyboard.h"
32#include "video_core/debug_utils/debug_utils.h" 34#include "video_core/debug_utils/debug_utils.h"
33#include "video_core/gpu.h" 35#include "video_core/gpu.h"
34#include "video_core/renderer_base.h" 36#include "video_core/renderer_base.h"
@@ -69,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
69 71
70 return vfs->OpenFile(path, FileSys::Mode::Read); 72 return vfs->OpenFile(path, FileSys::Mode::Read);
71} 73}
72
73/// Runs a CPU core while the system is powered on
74void RunCpuCore(Cpu& cpu_state) {
75 while (Core::System::GetInstance().IsPoweredOn()) {
76 cpu_state.RunLoop(true);
77 }
78}
79} // Anonymous namespace 74} // Anonymous namespace
80 75
81struct System::Impl { 76struct System::Impl {
82 Cpu& CurrentCpuCore() { 77 Cpu& CurrentCpuCore() {
83 if (Settings::values.use_multi_core) { 78 return cpu_core_manager.GetCurrentCore();
84 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
85 ASSERT(search != thread_to_cpu.end());
86 ASSERT(search->second);
87 return *search->second;
88 }
89
90 // Otherwise, use single-threaded mode active_core variable
91 return *cpu_cores[active_core];
92 } 79 }
93 80
94 ResultStatus RunLoop(bool tight_loop) { 81 ResultStatus RunLoop(bool tight_loop) {
95 status = ResultStatus::Success; 82 status = ResultStatus::Success;
96 83
97 // Update thread_to_cpu in case Core 0 is run from a different host thread 84 cpu_core_manager.RunLoop(tight_loop);
98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
99
100 if (GDBStub::IsServerEnabled()) {
101 GDBStub::HandlePacket();
102
103 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
104 // execute. Otherwise, get out of the loop function.
105 if (GDBStub::GetCpuHaltFlag()) {
106 if (GDBStub::GetCpuStepFlag()) {
107 tight_loop = false;
108 } else {
109 return ResultStatus::Success;
110 }
111 }
112 }
113
114 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
115 cpu_cores[active_core]->RunLoop(tight_loop);
116 if (Settings::values.use_multi_core) {
117 // Cores 1-3 are run on other threads in this mode
118 break;
119 }
120 }
121
122 if (GDBStub::IsServerEnabled()) {
123 GDBStub::SetCpuStepFlag(false);
124 }
125 85
126 return status; 86 return status;
127 } 87 }
128 88
129 ResultStatus Init(Frontend::EmuWindow& emu_window) { 89 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
130 LOG_DEBUG(HW_Memory, "initialized OK"); 90 LOG_DEBUG(HW_Memory, "initialized OK");
131 91
132 CoreTiming::Init(); 92 CoreTiming::Init();
@@ -136,15 +96,13 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 96 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 97 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 98
99 /// Create default implementations of applets if one is not provided.
100 if (software_keyboard == nullptr)
101 software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
102
139 auto main_process = Kernel::Process::Create(kernel, "main"); 103 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get()); 104 kernel.MakeCurrentProcess(main_process.get());
141 105
142 cpu_barrier = std::make_unique<CpuBarrier>();
143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
146 }
147
148 telemetry_session = std::make_unique<Core::TelemetrySession>(); 106 telemetry_session = std::make_unique<Core::TelemetrySession>();
149 service_manager = std::make_shared<Service::SM::ServiceManager>(); 107 service_manager = std::make_shared<Service::SM::ServiceManager>();
150 108
@@ -158,17 +116,8 @@ struct System::Impl {
158 116
159 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 117 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
160 118
161 // Create threads for CPU cores 1-3, and build thread_to_cpu map 119 cpu_core_manager.Initialize(system);
162 // CPU core 0 is run on the main thread 120 is_powered_on = true;
163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
164 if (Settings::values.use_multi_core) {
165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
166 cpu_core_threads[index] =
167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
169 }
170 }
171
172 LOG_DEBUG(Core, "Initialized OK"); 121 LOG_DEBUG(Core, "Initialized OK");
173 122
174 // Reset counters and set time origin to current frame 123 // Reset counters and set time origin to current frame
@@ -178,7 +127,8 @@ struct System::Impl {
178 return ResultStatus::Success; 127 return ResultStatus::Success;
179 } 128 }
180 129
181 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 130 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
131 const std::string& filepath) {
182 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 132 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
183 133
184 if (!app_loader) { 134 if (!app_loader) {
@@ -195,7 +145,7 @@ struct System::Impl {
195 return ResultStatus::ErrorSystemMode; 145 return ResultStatus::ErrorSystemMode;
196 } 146 }
197 147
198 ResultStatus init_result{Init(emu_window)}; 148 ResultStatus init_result{Init(system, emu_window)};
199 if (init_result != ResultStatus::Success) { 149 if (init_result != ResultStatus::Success) {
200 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 150 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
201 static_cast<int>(init_result)); 151 static_cast<int>(init_result));
@@ -225,6 +175,8 @@ struct System::Impl {
225 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 175 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
226 perf_results.frametime * 1000.0); 176 perf_results.frametime * 1000.0);
227 177
178 is_powered_on = false;
179
228 // Shutdown emulation session 180 // Shutdown emulation session
229 renderer.reset(); 181 renderer.reset();
230 GDBStub::Shutdown(); 182 GDBStub::Shutdown();
@@ -234,19 +186,7 @@ struct System::Impl {
234 gpu_core.reset(); 186 gpu_core.reset();
235 187
236 // Close all CPU/threading state 188 // Close all CPU/threading state
237 cpu_barrier->NotifyEnd(); 189 cpu_core_manager.Shutdown();
238 if (Settings::values.use_multi_core) {
239 for (auto& thread : cpu_core_threads) {
240 thread->join();
241 thread.reset();
242 }
243 }
244 thread_to_cpu.clear();
245 for (auto& cpu_core : cpu_cores) {
246 cpu_core.reset();
247 }
248 cpu_exclusive_monitor.reset();
249 cpu_barrier.reset();
250 190
251 // Shutdown kernel and core timing 191 // Shutdown kernel and core timing
252 kernel.Shutdown(); 192 kernel.Shutdown();
@@ -283,11 +223,11 @@ struct System::Impl {
283 std::unique_ptr<VideoCore::RendererBase> renderer; 223 std::unique_ptr<VideoCore::RendererBase> renderer;
284 std::unique_ptr<Tegra::GPU> gpu_core; 224 std::unique_ptr<Tegra::GPU> gpu_core;
285 std::shared_ptr<Tegra::DebugContext> debug_context; 225 std::shared_ptr<Tegra::DebugContext> debug_context;
286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 226 CpuCoreManager cpu_core_manager;
287 std::unique_ptr<CpuBarrier> cpu_barrier; 227 bool is_powered_on = false;
288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 228
289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 229 /// Frontend applets
290 std::size_t active_core{}; ///< Active core, only used in single thread mode 230 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
291 231
292 /// Service manager 232 /// Service manager
293 std::shared_ptr<Service::SM::ServiceManager> service_manager; 233 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -298,9 +238,6 @@ struct System::Impl {
298 ResultStatus status = ResultStatus::Success; 238 ResultStatus status = ResultStatus::Success;
299 std::string status_details = ""; 239 std::string status_details = "";
300 240
301 /// Map of guest threads to CPU cores
302 std::map<std::thread::id, Cpu*> thread_to_cpu;
303
304 Core::PerfStats perf_stats; 241 Core::PerfStats perf_stats;
305 Core::FrameLimiter frame_limiter; 242 Core::FrameLimiter frame_limiter;
306}; 243};
@@ -325,17 +262,15 @@ System::ResultStatus System::SingleStep() {
325} 262}
326 263
327void System::InvalidateCpuInstructionCaches() { 264void System::InvalidateCpuInstructionCaches() {
328 for (auto& cpu : impl->cpu_cores) { 265 impl->cpu_core_manager.InvalidateAllInstructionCaches();
329 cpu->ArmInterface().ClearInstructionCache();
330 }
331} 266}
332 267
333System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 268System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
334 return impl->Load(emu_window, filepath); 269 return impl->Load(*this, emu_window, filepath);
335} 270}
336 271
337bool System::IsPoweredOn() const { 272bool System::IsPoweredOn() const {
338 return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); 273 return impl->is_powered_on;
339} 274}
340 275
341void System::PrepareReschedule() { 276void System::PrepareReschedule() {
@@ -399,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
399} 334}
400 335
401Cpu& System::CpuCore(std::size_t core_index) { 336Cpu& System::CpuCore(std::size_t core_index) {
402 ASSERT(core_index < NUM_CPU_CORES); 337 return impl->cpu_core_manager.GetCore(core_index);
403 return *impl->cpu_cores[core_index];
404} 338}
405 339
406const Cpu& System::CpuCore(std::size_t core_index) const { 340const Cpu& System::CpuCore(std::size_t core_index) const {
407 ASSERT(core_index < NUM_CPU_CORES); 341 ASSERT(core_index < NUM_CPU_CORES);
408 return *impl->cpu_cores[core_index]; 342 return impl->cpu_core_manager.GetCore(core_index);
409} 343}
410 344
411ExclusiveMonitor& System::Monitor() { 345ExclusiveMonitor& System::Monitor() {
412 return *impl->cpu_exclusive_monitor; 346 return impl->cpu_core_manager.GetExclusiveMonitor();
413} 347}
414 348
415const ExclusiveMonitor& System::Monitor() const { 349const ExclusiveMonitor& System::Monitor() const {
416 return *impl->cpu_exclusive_monitor; 350 return impl->cpu_core_manager.GetExclusiveMonitor();
417} 351}
418 352
419Tegra::GPU& System::GPU() { 353Tegra::GPU& System::GPU() {
@@ -488,8 +422,16 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
488 return impl->virtual_filesystem; 422 return impl->virtual_filesystem;
489} 423}
490 424
425void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
426 impl->software_keyboard = std::move(applet);
427}
428
429const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
430 return *impl->software_keyboard;
431}
432
491System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 433System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
492 return impl->Init(emu_window); 434 return impl->Init(*this, emu_window);
493} 435}
494 436
495void System::Shutdown() { 437void System::Shutdown() {
diff --git a/src/core/core.h b/src/core/core.h
index cfacceb81..be71bd437 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -13,6 +13,7 @@
13 13
14namespace Core::Frontend { 14namespace Core::Frontend {
15class EmuWindow; 15class EmuWindow;
16class SoftwareKeyboardApplet;
16} // namespace Core::Frontend 17} // namespace Core::Frontend
17 18
18namespace FileSys { 19namespace FileSys {
@@ -236,6 +237,10 @@ public:
236 237
237 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 238 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
238 239
240 void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
241
242 const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
243
239private: 244private:
240 System(); 245 System();
241 246
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
new file mode 100644
index 000000000..769a6fefa
--- /dev/null
+++ b/src/core/cpu_core_manager.cpp
@@ -0,0 +1,142 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/arm/exclusive_monitor.h"
7#include "core/core.h"
8#include "core/core_cpu.h"
9#include "core/cpu_core_manager.h"
10#include "core/gdbstub/gdbstub.h"
11#include "core/settings.h"
12
13namespace Core {
14namespace {
15void RunCpuCore(const System& system, Cpu& cpu_state) {
16 while (system.IsPoweredOn()) {
17 cpu_state.RunLoop(true);
18 }
19}
20} // Anonymous namespace
21
22CpuCoreManager::CpuCoreManager() = default;
23CpuCoreManager::~CpuCoreManager() = default;
24
25void CpuCoreManager::Initialize(System& system) {
26 barrier = std::make_unique<CpuBarrier>();
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28
29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
31 }
32
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map
34 // CPU core 0 is run on the main thread
35 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
36 if (!Settings::values.use_multi_core) {
37 return;
38 }
39
40 for (std::size_t index = 0; index < core_threads.size(); ++index) {
41 core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
42 std::ref(*cores[index + 1]));
43 thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
44 }
45}
46
47void CpuCoreManager::Shutdown() {
48 barrier->NotifyEnd();
49 if (Settings::values.use_multi_core) {
50 for (auto& thread : core_threads) {
51 thread->join();
52 thread.reset();
53 }
54 }
55
56 thread_to_cpu.clear();
57 for (auto& cpu_core : cores) {
58 cpu_core.reset();
59 }
60
61 exclusive_monitor.reset();
62 barrier.reset();
63}
64
65Cpu& CpuCoreManager::GetCore(std::size_t index) {
66 return *cores.at(index);
67}
68
69const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
70 return *cores.at(index);
71}
72
73ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
74 return *exclusive_monitor;
75}
76
77const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
78 return *exclusive_monitor;
79}
80
81Cpu& CpuCoreManager::GetCurrentCore() {
82 if (Settings::values.use_multi_core) {
83 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
84 ASSERT(search != thread_to_cpu.end());
85 ASSERT(search->second);
86 return *search->second;
87 }
88
89 // Otherwise, use single-threaded mode active_core variable
90 return *cores[active_core];
91}
92
93const Cpu& CpuCoreManager::GetCurrentCore() const {
94 if (Settings::values.use_multi_core) {
95 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
96 ASSERT(search != thread_to_cpu.end());
97 ASSERT(search->second);
98 return *search->second;
99 }
100
101 // Otherwise, use single-threaded mode active_core variable
102 return *cores[active_core];
103}
104
105void CpuCoreManager::RunLoop(bool tight_loop) {
106 // Update thread_to_cpu in case Core 0 is run from a different host thread
107 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
108
109 if (GDBStub::IsServerEnabled()) {
110 GDBStub::HandlePacket();
111
112 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
113 // execute. Otherwise, get out of the loop function.
114 if (GDBStub::GetCpuHaltFlag()) {
115 if (GDBStub::GetCpuStepFlag()) {
116 tight_loop = false;
117 } else {
118 return;
119 }
120 }
121 }
122
123 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
124 cores[active_core]->RunLoop(tight_loop);
125 if (Settings::values.use_multi_core) {
126 // Cores 1-3 are run on other threads in this mode
127 break;
128 }
129 }
130
131 if (GDBStub::IsServerEnabled()) {
132 GDBStub::SetCpuStepFlag(false);
133 }
134}
135
136void CpuCoreManager::InvalidateAllInstructionCaches() {
137 for (auto& cpu : cores) {
138 cpu->ArmInterface().ClearInstructionCache();
139 }
140}
141
142} // namespace Core
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
new file mode 100644
index 000000000..a4d70ec56
--- /dev/null
+++ b/src/core/cpu_core_manager.h
@@ -0,0 +1,59 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <map>
9#include <memory>
10#include <thread>
11
12namespace Core {
13
14class Cpu;
15class CpuBarrier;
16class ExclusiveMonitor;
17class System;
18
19class CpuCoreManager {
20public:
21 CpuCoreManager();
22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete;
24
25 ~CpuCoreManager();
26
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29
30 void Initialize(System& system);
31 void Shutdown();
32
33 Cpu& GetCore(std::size_t index);
34 const Cpu& GetCore(std::size_t index) const;
35
36 Cpu& GetCurrentCore();
37 const Cpu& GetCurrentCore() const;
38
39 ExclusiveMonitor& GetExclusiveMonitor();
40 const ExclusiveMonitor& GetExclusiveMonitor() const;
41
42 void RunLoop(bool tight_loop);
43
44 void InvalidateAllInstructionCaches();
45
46private:
47 static constexpr std::size_t NUM_CPU_CORES = 4;
48
49 std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
50 std::unique_ptr<CpuBarrier> barrier;
51 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
52 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
53 std::size_t active_core{}; ///< Active core, only used in single thread mode
54
55 /// Map of guest threads to CPU cores
56 std::map<std::thread::id, Cpu*> thread_to_cpu;
57};
58
59} // namespace Core
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 76a2b7e86..e29f70b3a 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -8,8 +8,9 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 dump_root(std::move(dump_root_)),
13 sysnand_cache(std::make_unique<RegisteredCache>( 14 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 15 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_unique<RegisteredCache>( 16 usrnand_cache(std::make_unique<RegisteredCache>(
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
32 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); 33 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
33} 34}
34 35
36VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
37 if (title_id == 0)
38 return nullptr;
39 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
40}
41
35} // namespace FileSys 42} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 364d309bd..453c11ad2 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,17 +17,19 @@ class RegisteredCache;
17/// registered caches. 17/// registered caches.
18class BISFactory { 18class BISFactory {
19public: 19public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 RegisteredCache* GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 RegisteredCache* GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 VirtualDir GetModificationDumpRoot(u64 title_id) const;
27 28
28private: 29private:
29 VirtualDir nand_root; 30 VirtualDir nand_root;
30 VirtualDir load_root; 31 VirtualDir load_root;
32 VirtualDir dump_root;
31 33
32 std::unique_ptr<RegisteredCache> sysnand_cache; 34 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::unique_ptr<RegisteredCache> usrnand_cache; 35 std::unique_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 1ece55731..2c145bd09 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) { 176 for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
177 if (file->GetExtension() != "nca") 177 if (file->GetExtension() != "nca")
178 continue; 178 continue;
179 auto nca = std::make_shared<NCA>(file); 179 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
180 // TODO(DarkLordZach): Add proper Rev1+ Support 180 // TODO(DarkLordZach): Add proper Rev1+ Support
181 if (nca->IsUpdate()) 181 if (nca->IsUpdate())
182 continue; 182 continue;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 8f62571cf..a350496f7 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/crypto/key_manager.h"
12#include "core/file_sys/vfs.h" 13#include "core/file_sys/vfs.h"
13 14
14namespace Loader { 15namespace Loader {
@@ -31,7 +32,18 @@ enum class GamecardSize : u8 {
31}; 32};
32 33
33struct GamecardInfo { 34struct GamecardInfo {
34 std::array<u8, 0x70> data; 35 u64_le firmware_version;
36 u32_le access_control_flags;
37 u32_le read_wait_time1;
38 u32_le read_wait_time2;
39 u32_le write_wait_time1;
40 u32_le write_wait_time2;
41 u32_le firmware_mode;
42 u32_le cup_version;
43 std::array<u8, 4> reserved1;
44 u64_le update_partition_hash;
45 u64_le cup_id;
46 std::array<u8, 0x38> reserved2;
35}; 47};
36static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); 48static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
37 49
@@ -107,5 +119,7 @@ private:
107 std::shared_ptr<NSP> secure_partition; 119 std::shared_ptr<NSP> secure_partition;
108 std::shared_ptr<NCA> program; 120 std::shared_ptr<NCA> program;
109 std::vector<std::shared_ptr<NCA>> ncas; 121 std::vector<std::shared_ptr<NCA>> ncas;
122
123 Core::Crypto::KeyManager keys;
110}; 124};
111} // namespace FileSys 125} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index b46fe893c..19b6f8600 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
102} 102}
103 103
104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) 104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
105 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { 105 Core::Crypto::KeyManager keys_)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
106 if (file == nullptr) { 107 if (file == nullptr) {
107 status = Loader::ResultStatus::ErrorNullFile; 108 status = Loader::ResultStatus::ErrorNullFile;
108 return; 109 return;
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 4bba55607..99294cbb4 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
79class NCA : public ReadOnlyVfsDirectory { 79class NCA : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 81 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
82 u64 bktr_base_ivfc_offset = 0); 82 u64 bktr_base_ivfc_offset = 0,
83 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
83 ~NCA() override; 84 ~NCA() override;
84 85
85 Loader::ResultStatus GetStatus() const; 86 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index a012c2be9..c8fa912bf 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size()); 67 raw->version_string.size());
68} 68}
69
70std::vector<u8> NACP::GetRawBytes() const {
71 std::vector<u8> out(sizeof(RawNACP));
72 std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
73 return out;
74}
69} // namespace FileSys 75} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 141f7e056..bfaad46b4 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -81,6 +81,7 @@ public:
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const; 82 u64 GetDLCBaseTitleId() const;
83 std::string GetVersionString() const; 83 std::string GetVersionString() const;
84 std::vector<u8> GetRawBytes() const;
84 85
85private: 86private:
86 std::unique_ptr<RawNACP> raw; 87 std::unique_ptr<RawNACP> raw;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index fea0593c7..e4a4ee4ab 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -8,25 +8,10 @@
8 8
9namespace FileSys { 9namespace FileSys {
10 10
11namespace ErrCodes { 11constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
12enum { 12constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
13 NotFound = 1, 13constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
14 TitleNotFound = 1002, 14constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
15 SdCardNotFound = 2001, 15constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
16 RomFSNotFound = 2520,
17};
18}
19
20constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
21
22// TODO(bunnei): Replace these with correct errors for Switch OS
23constexpr ResultCode ERROR_INVALID_PATH(-1);
24constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
25constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
26constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
27constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
28constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
29constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
30constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
31 16
32} // namespace FileSys 17} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0c1156989..e8df08724 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -19,12 +19,18 @@
19#include "core/file_sys/vfs_vector.h" 19#include "core/file_sys/vfs_vector.h"
20#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22#include "core/settings.h"
22 23
23namespace FileSys { 24namespace FileSys {
24 25
25constexpr u64 SINGLE_BYTE_MODULUS = 0x100; 26constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
26constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 27constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
27 28
29constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
30 "main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2",
31 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
32};
33
28struct NSOBuildHeader { 34struct NSOBuildHeader {
29 u32_le magic; 35 u32_le magic;
30 INSERT_PADDING_BYTES(0x3C); 36 INSERT_PADDING_BYTES(0x3C);
@@ -56,6 +62,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
56 if (exefs == nullptr) 62 if (exefs == nullptr)
57 return exefs; 63 return exefs;
58 64
65 if (Settings::values.dump_exefs) {
66 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
67 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
68 if (dump_dir != nullptr) {
69 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
70 VfsRawCopyD(exefs, exefs_dir);
71 }
72 }
73
59 const auto installed = Service::FileSystem::GetUnionContents(); 74 const auto installed = Service::FileSystem::GetUnionContents();
60 75
61 // Game Updates 76 // Game Updates
@@ -69,6 +84,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
69 exefs = update->GetExeFS(); 84 exefs = update->GetExeFS();
70 } 85 }
71 86
87 // LayeredExeFS
88 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
89 if (load_dir != nullptr && load_dir->GetSize() > 0) {
90 auto patch_dirs = load_dir->GetSubdirectories();
91 std::sort(
92 patch_dirs.begin(), patch_dirs.end(),
93 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
94
95 std::vector<VirtualDir> layers;
96 layers.reserve(patch_dirs.size() + 1);
97 for (const auto& subdir : patch_dirs) {
98 auto exefs_dir = subdir->GetSubdirectory("exefs");
99 if (exefs_dir != nullptr)
100 layers.push_back(std::move(exefs_dir));
101 }
102 layers.push_back(exefs);
103
104 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
105 if (layered != nullptr) {
106 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
107 exefs = std::move(layered);
108 }
109 }
110
72 return exefs; 111 return exefs;
73} 112}
74 113
@@ -119,6 +158,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
119 const auto build_id_raw = Common::HexArrayToString(header.build_id); 158 const auto build_id_raw = Common::HexArrayToString(header.build_id);
120 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 159 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
121 160
161 if (Settings::values.dump_nso) {
162 LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
163 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
164 if (dump_dir != nullptr) {
165 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
166 const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
167
168 file->Resize(nso.size());
169 file->WriteBytes(nso);
170 }
171 }
172
122 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); 173 LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
123 174
124 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 175 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
@@ -301,18 +352,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
301 if (IsDirValidAndNonEmpty(exefs_dir)) { 352 if (IsDirValidAndNonEmpty(exefs_dir)) {
302 bool ips = false; 353 bool ips = false;
303 bool ipswitch = false; 354 bool ipswitch = false;
355 bool layeredfs = false;
304 356
305 for (const auto& file : exefs_dir->GetFiles()) { 357 for (const auto& file : exefs_dir->GetFiles()) {
306 if (file->GetExtension() == "ips") 358 if (file->GetExtension() == "ips") {
307 ips = true; 359 ips = true;
308 else if (file->GetExtension() == "pchtxt") 360 } else if (file->GetExtension() == "pchtxt") {
309 ipswitch = true; 361 ipswitch = true;
362 } else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
363 file->GetName()) != EXEFS_FILE_NAMES.end()) {
364 layeredfs = true;
365 }
310 } 366 }
311 367
312 if (ips) 368 if (ips)
313 AppendCommaIfNotEmpty(types, "IPS"); 369 AppendCommaIfNotEmpty(types, "IPS");
314 if (ipswitch) 370 if (ipswitch)
315 AppendCommaIfNotEmpty(types, "IPSwitch"); 371 AppendCommaIfNotEmpty(types, "IPSwitch");
372 if (layeredfs)
373 AppendCommaIfNotEmpty(types, "LayeredExeFS");
316 } 374 }
317 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 375 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
318 AppendCommaIfNotEmpty(types, "LayeredFS"); 376 AppendCommaIfNotEmpty(types, "LayeredFS");
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 96302a241..a3f8f2f73 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
106 106
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 108 std::string_view path) const {
109 if (dir->GetFileRelative(path) != nullptr) 109 const auto file = dir->GetFileRelative(path);
110 return dir->GetFileRelative(path); 110 if (file != nullptr)
111 if (dir->GetDirectoryRelative(path) != nullptr) { 111 return file;
112
113 const auto nca_dir = dir->GetDirectoryRelative(path);
114 if (nca_dir != nullptr) {
112 const auto nca_dir = dir->GetDirectoryRelative(path); 115 const auto nca_dir = dir->GetDirectoryRelative(path);
113 VirtualFile file = nullptr; 116 VirtualFile file = nullptr;
114 117
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
225 228
226 if (file == nullptr) 229 if (file == nullptr)
227 continue; 230 continue;
228 const auto nca = std::make_shared<NCA>(parser(file, id)); 231 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
229 if (nca->GetStatus() != Loader::ResultStatus::Success || 232 if (nca->GetStatus() != Loader::ResultStatus::Success ||
230 nca->GetType() != NCAContentType::Meta) { 233 nca->GetType() != NCAContentType::Meta) {
231 continue; 234 continue;
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
315 const auto raw = GetEntryRaw(title_id, type); 318 const auto raw = GetEntryRaw(title_id, type);
316 if (raw == nullptr) 319 if (raw == nullptr)
317 return nullptr; 320 return nullptr;
318 return std::make_unique<NCA>(raw); 321 return std::make_unique<NCA>(raw, nullptr, 0, keys);
319} 322}
320 323
321std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 324std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 6cfb16017..6b89db8de 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13#include <boost/container/flat_map.hpp> 13#include <boost/container/flat_map.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/crypto/key_manager.h"
15#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
16 17
17namespace FileSys { 18namespace FileSys {
@@ -133,6 +134,8 @@ private:
133 134
134 VirtualDir dir; 135 VirtualDir dir;
135 RegisteredCacheParsingFunction parser; 136 RegisteredCacheParsingFunction parser;
137 Core::Crypto::KeyManager keys;
138
136 // maps tid -> NcaID of meta 139 // maps tid -> NcaID of meta
137 boost::container::flat_map<u64, NcaID> meta_id; 140 boost::container::flat_map<u64, NcaID> meta_id;
138 // maps tid -> meta 141 // maps tid -> meta
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index ef1aaebbb..5434f2149 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -83,28 +83,32 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
83 return MakeResult<VirtualDir>(std::move(out)); 83 return MakeResult<VirtualDir>(std::move(out));
84} 84}
85 85
86std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 86VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
87 u128 user_id, u64 save_id) { 87 return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
88 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 88}
89 // be interpreted as the title id of the current process.
90 if (type == SaveDataType::SaveData && title_id == 0)
91 title_id = Core::CurrentProcess()->GetTitleID();
92
93 std::string out;
94 89
90std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
95 switch (space) { 91 switch (space) {
96 case SaveDataSpaceId::NandSystem: 92 case SaveDataSpaceId::NandSystem:
97 out = "/system/"; 93 return "/system/";
98 break;
99 case SaveDataSpaceId::NandUser: 94 case SaveDataSpaceId::NandUser:
100 out = "/user/"; 95 return "/user/";
101 break;
102 case SaveDataSpaceId::TemporaryStorage: 96 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/"; 97 return "/temp/";
104 break;
105 default: 98 default:
106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 99 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
100 return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
107 } 101 }
102}
103
104std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
105 u128 user_id, u64 save_id) {
106 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
107 // be interpreted as the title id of the current process.
108 if (type == SaveDataType::SaveData && title_id == 0)
109 title_id = Core::CurrentProcess()->GetTitleID();
110
111 std::string out = GetSaveDataSpaceIdPath(space);
108 112
109 switch (type) { 113 switch (type) {
110 case SaveDataType::SystemSaveData: 114 case SaveDataType::SystemSaveData:
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index d69ef6741..2a0088040 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -52,6 +52,9 @@ public:
52 52
53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
54 54
55 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
56
57 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
55 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 58 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
56 u128 user_id, u64 save_id); 59 u128 user_id, u64 save_id);
57 60
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 2aaba4179..e1a4210db 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
252 continue; 252 continue;
253 } 253 }
254 254
255 auto next_nca = std::make_shared<NCA>(next_file); 255 auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
256 if (next_nca->GetType() == NCAContentType::Program) 256 if (next_nca->GetType() == NCAContentType::Program)
257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
258 if (next_nca->GetStatus() == Loader::ResultStatus::Success || 258 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 338080b7e..9a28ed5bb 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -70,6 +70,8 @@ private:
70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; 70 std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
71 std::vector<VirtualFile> ticket_files; 71 std::vector<VirtualFile> ticket_files;
72 72
73 Core::Crypto::KeyManager keys;
74
73 VirtualFile romfs; 75 VirtualFile romfs;
74 VirtualDir exefs; 76 VirtualDir exefs;
75}; 77};
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
new file mode 100644
index 000000000..856ed33da
--- /dev/null
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -0,0 +1,29 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/backend.h"
6#include "common/string_util.h"
7#include "core/frontend/applets/software_keyboard.h"
8
9namespace Core::Frontend {
10SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
11
12void DefaultSoftwareKeyboardApplet::RequestText(
13 std::function<void(std::optional<std::u16string>)> out,
14 SoftwareKeyboardParameters parameters) const {
15 if (parameters.initial_text.empty())
16 out(u"yuzu");
17
18 out(parameters.initial_text);
19}
20
21void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
22 std::u16string error_message, std::function<void()> finished_check) const {
23 LOG_WARNING(Service_AM,
24 "(STUBBED) called - Default fallback software keyboard does not support text "
25 "check! (error_message={})",
26 Common::UTF16ToUTF8(error_message));
27 finished_check();
28}
29} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
new file mode 100644
index 000000000..f9b202664
--- /dev/null
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -0,0 +1,54 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <optional>
9#include <string>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Core::Frontend {
14struct SoftwareKeyboardParameters {
15 std::u16string submit_text;
16 std::u16string header_text;
17 std::u16string sub_text;
18 std::u16string guide_text;
19 std::u16string initial_text;
20 std::size_t max_length;
21 bool password;
22 bool cursor_at_beginning;
23
24 union {
25 u8 value;
26
27 BitField<1, 1, u8> disable_space;
28 BitField<2, 1, u8> disable_address;
29 BitField<3, 1, u8> disable_percent;
30 BitField<4, 1, u8> disable_slash;
31 BitField<6, 1, u8> disable_number;
32 BitField<7, 1, u8> disable_download_code;
33 };
34};
35
36class SoftwareKeyboardApplet {
37public:
38 virtual ~SoftwareKeyboardApplet();
39
40 virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
41 SoftwareKeyboardParameters parameters) const = 0;
42 virtual void SendTextCheckDialog(std::u16string error_message,
43 std::function<void()> finished_check) const = 0;
44};
45
46class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
47public:
48 void RequestText(std::function<void(std::optional<std::u16string>)> out,
49 SoftwareKeyboardParameters parameters) const override;
50 void SendTextCheckDialog(std::u16string error_message,
51 std::function<void()> finished_check) const override;
52};
53
54} // namespace Core::Frontend
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 39bdf4e21..16fdcd376 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>
132 */ 132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>; 133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134 134
135/**
136 * A mouse device is an input device that returns a tuple of two floats and four ints.
137 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
138 * The s32s are the mouse wheel.
139 */
140using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
141
135} // namespace Input 142} // namespace Input
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index bdcc889e0..687dea409 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -71,10 +71,6 @@ constexpr u32 PSTATE_REGISTER = 33;
71constexpr u32 UC_ARM64_REG_Q0 = 34; 71constexpr u32 UC_ARM64_REG_Q0 = 34;
72constexpr u32 FPCR_REGISTER = 66; 72constexpr u32 FPCR_REGISTER = 66;
73 73
74// TODO/WiP - Used while working on support for FPU
75constexpr u32 TODO_DUMMY_REG_997 = 997;
76constexpr u32 TODO_DUMMY_REG_998 = 998;
77
78// For sample XML files see the GDB source /gdb/features 74// For sample XML files see the GDB source /gdb/features
79// GDB also wants the l character at the start 75// GDB also wants the l character at the start
80// This XML defines what the registers are for this specific ARM device 76// This XML defines what the registers are for this specific ARM device
@@ -260,6 +256,36 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
260 } 256 }
261} 257}
262 258
259static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
260 if (!thread) {
261 return u128{0};
262 }
263
264 auto& thread_context = thread->GetContext();
265
266 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
267 return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
268 } else if (id == FPCR_REGISTER) {
269 return u128{thread_context.fpcr, 0};
270 } else {
271 return u128{0};
272 }
273}
274
275static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) {
276 if (!thread) {
277 return;
278 }
279
280 auto& thread_context = thread->GetContext();
281
282 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
283 thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
284 } else if (id == FPCR_REGISTER) {
285 thread_context.fpcr = val[0];
286 }
287}
288
263/** 289/**
264 * Turns hex string character into the equivalent byte. 290 * Turns hex string character into the equivalent byte.
265 * 291 *
@@ -409,6 +435,27 @@ static u64 GdbHexToLong(const u8* src) {
409 return output; 435 return output;
410} 436}
411 437
438/**
439 * Convert a gdb-formatted hex string into a u128.
440 *
441 * @param src Pointer to hex string.
442 */
443static u128 GdbHexToU128(const u8* src) {
444 u128 output;
445
446 for (int i = 0; i < 16; i += 2) {
447 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]);
448 output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]);
449 }
450
451 for (int i = 0; i < 16; i += 2) {
452 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]);
453 output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]);
454 }
455
456 return output;
457}
458
412/// Read a byte from the gdb client. 459/// Read a byte from the gdb client.
413static u8 ReadByte() { 460static u8 ReadByte() {
414 u8 c; 461 u8 c;
@@ -599,8 +646,7 @@ static void HandleQuery() {
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 646 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList(); 647 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 648 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 649 val += fmt::format("{:x},", thread->GetThreadID());
603 val += ",";
604 } 650 }
605 } 651 }
606 val.pop_back(); 652 val.pop_back();
@@ -791,11 +837,15 @@ static void ReadRegister() {
791 } else if (id == PSTATE_REGISTER) { 837 } else if (id == PSTATE_REGISTER) {
792 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); 838 IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
793 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 839 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
794 LongToGdbHex(reply, RegRead(id, current_thread)); 840 u128 r = FpuRead(id, current_thread);
841 LongToGdbHex(reply, r[0]);
842 LongToGdbHex(reply + 16, r[1]);
795 } else if (id == FPCR_REGISTER) { 843 } else if (id == FPCR_REGISTER) {
796 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); 844 u128 r = FpuRead(id, current_thread);
797 } else { 845 IntToGdbHex(reply, static_cast<u32>(r[0]));
798 LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); 846 } else if (id == FPCR_REGISTER + 1) {
847 u128 r = FpuRead(id, current_thread);
848 IntToGdbHex(reply, static_cast<u32>(r[0] >> 32));
799 } 849 }
800 850
801 SendReply(reinterpret_cast<char*>(reply)); 851 SendReply(reinterpret_cast<char*>(reply));
@@ -822,13 +872,18 @@ static void ReadRegisters() {
822 872
823 bufptr += 8; 873 bufptr += 8;
824 874
825 for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) { 875 u128 r;
826 LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); 876
877 for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) {
878 r = FpuRead(reg, current_thread);
879 LongToGdbHex(bufptr + reg * 32, r[0]);
880 LongToGdbHex(bufptr + reg * 32 + 16, r[1]);
827 } 881 }
828 882
829 bufptr += 32 * 32; 883 bufptr += 32 * 32;
830 884
831 LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread)); 885 r = FpuRead(FPCR_REGISTER, current_thread);
886 IntToGdbHex(bufptr, static_cast<u32>(r[0]));
832 887
833 bufptr += 8; 888 bufptr += 8;
834 889
@@ -853,14 +908,12 @@ static void WriteRegister() {
853 } else if (id == PSTATE_REGISTER) { 908 } else if (id == PSTATE_REGISTER) {
854 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); 909 RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
855 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 910 } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
856 RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); 911 FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread);
857 } else if (id == FPCR_REGISTER) { 912 } else if (id == FPCR_REGISTER) {
858 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); 913 } else if (id == FPCR_REGISTER + 1) {
859 } else {
860 RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
861 } 914 }
862 915
863 // Update Unicorn context skipping scheduler, no running threads at this point 916 // Update ARM context, skipping scheduler - no running threads at this point
864 Core::System::GetInstance() 917 Core::System::GetInstance()
865 .ArmInterface(current_core) 918 .ArmInterface(current_core)
866 .LoadContext(current_thread->GetContext()); 919 .LoadContext(current_thread->GetContext());
@@ -885,13 +938,13 @@ static void WriteRegisters() {
885 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { 938 } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
886 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); 939 RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
887 } else if (reg == FPCR_REGISTER) { 940 } else if (reg == FPCR_REGISTER) {
888 RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); 941 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
889 } else { 942 } else if (reg == FPCR_REGISTER + 1) {
890 UNIMPLEMENTED(); 943 RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
891 } 944 }
892 } 945 }
893 946
894 // Update Unicorn context skipping scheduler, no running threads at this point 947 // Update ARM context, skipping scheduler - no running threads at this point
895 Core::System::GetInstance() 948 Core::System::GetInstance()
896 .ArmInterface(current_core) 949 .ArmInterface(current_core)
897 .LoadContext(current_thread->GetContext()); 950 .LoadContext(current_thread->GetContext());
@@ -917,12 +970,6 @@ static void ReadMemory() {
917 SendReply("E01"); 970 SendReply("E01");
918 } 971 }
919 972
920 const auto& vm_manager = Core::CurrentProcess()->VMManager();
921 if (addr < vm_manager.GetCodeRegionBaseAddress() ||
922 addr >= vm_manager.GetMapRegionEndAddress()) {
923 return SendReply("E00");
924 }
925
926 if (!Memory::IsValidVirtualAddress(addr)) { 973 if (!Memory::IsValidVirtualAddress(addr)) {
927 return SendReply("E00"); 974 return SendReply("E00");
928 } 975 }
@@ -967,7 +1014,7 @@ void Break(bool is_memory_break) {
967static void Step() { 1014static void Step() {
968 if (command_length > 1) { 1015 if (command_length > 1) {
969 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); 1016 RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
970 // Update Unicorn context skipping scheduler, no running threads at this point 1017 // Update ARM context, skipping scheduler - no running threads at this point
971 Core::System::GetInstance() 1018 Core::System::GetInstance()
972 .ArmInterface(current_core) 1019 .ArmInterface(current_core)
973 .LoadContext(current_thread->GetContext()); 1020 .LoadContext(current_thread->GetContext());
@@ -1010,7 +1057,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
1010 breakpoint.addr = addr; 1057 breakpoint.addr = addr;
1011 breakpoint.len = len; 1058 breakpoint.len = len;
1012 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); 1059 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
1013 static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}}; 1060 static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
1014 Memory::WriteBlock(addr, btrap.data(), btrap.size()); 1061 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1015 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 1062 Core::System::GetInstance().InvalidateCpuInstructionCaches();
1016 p.insert({addr, breakpoint}); 1063 p.insert({addr, breakpoint});
@@ -1321,13 +1368,15 @@ void SetCpuStepFlag(bool is_step) {
1321} 1368}
1322 1369
1323void SendTrap(Kernel::Thread* thread, int trap) { 1370void SendTrap(Kernel::Thread* thread, int trap) {
1324 if (send_trap) { 1371 if (!send_trap) {
1325 if (!halt_loop || current_thread == thread) { 1372 return;
1326 current_thread = thread;
1327 SendSignal(thread, trap);
1328 }
1329 halt_loop = true;
1330 send_trap = false;
1331 } 1373 }
1374
1375 if (!halt_loop || current_thread == thread) {
1376 current_thread = thread;
1377 SendSignal(thread, trap);
1378 }
1379 halt_loop = true;
1380 send_trap = false;
1332} 1381}
1333}; // namespace GDBStub 1382}; // namespace GDBStub
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index ee698c8a7..8b58d701d 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,58 +8,28 @@
8 8
9namespace Kernel { 9namespace Kernel {
10 10
11namespace ErrCodes { 11// Confirmed Switch kernel error codes
12enum {
13 // Confirmed Switch OS error codes
14 MaxConnectionsReached = 7,
15 InvalidSize = 101,
16 InvalidAddress = 102,
17 HandleTableFull = 105,
18 InvalidMemoryState = 106,
19 InvalidMemoryPermissions = 108,
20 InvalidMemoryRange = 110,
21 InvalidThreadPriority = 112,
22 InvalidProcessorId = 113,
23 InvalidHandle = 114,
24 InvalidPointer = 115,
25 InvalidCombination = 116,
26 Timeout = 117,
27 SynchronizationCanceled = 118,
28 TooLarge = 119,
29 InvalidEnumValue = 120,
30 NoSuchEntry = 121,
31 AlreadyRegistered = 122,
32 SessionClosed = 123,
33 InvalidState = 125,
34 ResourceLimitExceeded = 132,
35};
36}
37 12
38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
39// double check that the code matches before re-using the constant. 14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
40 15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); 17constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 18constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 19constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
45 ErrCodes::MaxConnectionsReached); 20constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 21constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 22constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
48 ErrCodes::InvalidCombination); 23constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 24constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 25constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 26constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
52 ErrCodes::InvalidMemoryPermissions); 27constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
53constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange); 28constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
54constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 29constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
55constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 30constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
56constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
57constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); 32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
60 ErrCodes::InvalidThreadPriority);
61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
64 34
65} // namespace Kernel 35} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 5ee5c05e3..1bf79b692 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -12,12 +12,23 @@
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13 13
14namespace Kernel { 14namespace Kernel {
15namespace {
16constexpr u16 GetSlot(Handle handle) {
17 return handle >> 15;
18}
19
20constexpr u16 GetGeneration(Handle handle) {
21 return handle & 0x7FFF;
22}
23} // Anonymous namespace
15 24
16HandleTable::HandleTable() { 25HandleTable::HandleTable() {
17 next_generation = 1; 26 next_generation = 1;
18 Clear(); 27 Clear();
19} 28}
20 29
30HandleTable::~HandleTable() = default;
31
21ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { 32ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
22 DEBUG_ASSERT(obj != nullptr); 33 DEBUG_ASSERT(obj != nullptr);
23 34
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 9e2f33e8a..e3f3e3fb8 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -43,6 +43,7 @@ enum KernelHandle : Handle {
43class HandleTable final : NonCopyable { 43class HandleTable final : NonCopyable {
44public: 44public:
45 HandleTable(); 45 HandleTable();
46 ~HandleTable();
46 47
47 /** 48 /**
48 * Allocates a handle for the given object. 49 * Allocates a handle for the given object.
@@ -89,18 +90,8 @@ public:
89 void Clear(); 90 void Clear();
90 91
91private: 92private:
92 /** 93 /// This is the maximum limit of handles allowed per process in Horizon
93 * This is the maximum limit of handles allowed per process in CTR-OS. It can be further 94 static constexpr std::size_t MAX_COUNT = 1024;
94 * reduced by ExHeader values, but this is not emulated here.
95 */
96 static const std::size_t MAX_COUNT = 4096;
97
98 static u16 GetSlot(Handle handle) {
99 return handle >> 15;
100 }
101 static u16 GetGeneration(Handle handle) {
102 return handle & 0x7FFF;
103 }
104 95
105 /// Stores the Object referenced by the handle or null if the slot is empty. 96 /// Stores the Object referenced by the handle or null if the slot is empty.
106 std::array<SharedPtr<Object>, MAX_COUNT> objects; 97 std::array<SharedPtr<Object>, MAX_COUNT> objects;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fd4ba5d2..e441c5bc6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -105,7 +105,7 @@ struct KernelCore::Impl {
105 void Initialize(KernelCore& kernel) { 105 void Initialize(KernelCore& kernel) {
106 Shutdown(); 106 Shutdown();
107 107
108 InitializeResourceLimits(kernel); 108 InitializeSystemResourceLimit(kernel);
109 InitializeThreads(); 109 InitializeThreads();
110 InitializeTimers(); 110 InitializeTimers();
111 } 111 }
@@ -118,7 +118,7 @@ struct KernelCore::Impl {
118 process_list.clear(); 118 process_list.clear();
119 current_process = nullptr; 119 current_process = nullptr;
120 120
121 resource_limits.fill(nullptr); 121 system_resource_limit = nullptr;
122 122
123 thread_wakeup_callback_handle_table.Clear(); 123 thread_wakeup_callback_handle_table.Clear();
124 thread_wakeup_event_type = nullptr; 124 thread_wakeup_event_type = nullptr;
@@ -129,63 +129,17 @@ struct KernelCore::Impl {
129 named_ports.clear(); 129 named_ports.clear();
130 } 130 }
131 131
132 void InitializeResourceLimits(KernelCore& kernel) { 132 // Creates the default system resource limit
133 // Create the four resource limits that the system uses 133 void InitializeSystemResourceLimit(KernelCore& kernel) {
134 // Create the APPLICATION resource limit 134 system_resource_limit = ResourceLimit::Create(kernel, "System");
135 SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications"); 135
136 resource_limit->max_priority = 0x18; 136 // If setting the default system values fails, then something seriously wrong has occurred.
137 resource_limit->max_commit = 0x4000000; 137 ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
138 resource_limit->max_threads = 0x20; 138 .IsSuccess());
139 resource_limit->max_events = 0x20; 139 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
140 resource_limit->max_mutexes = 0x20; 140 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
141 resource_limit->max_semaphores = 0x8; 141 ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
142 resource_limit->max_timers = 0x8; 142 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
143 resource_limit->max_shared_mems = 0x10;
144 resource_limit->max_address_arbiters = 0x2;
145 resource_limit->max_cpu_time = 0x1E;
146 resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
147
148 // Create the SYS_APPLET resource limit
149 resource_limit = ResourceLimit::Create(kernel, "System Applets");
150 resource_limit->max_priority = 0x4;
151 resource_limit->max_commit = 0x5E00000;
152 resource_limit->max_threads = 0x1D;
153 resource_limit->max_events = 0xB;
154 resource_limit->max_mutexes = 0x8;
155 resource_limit->max_semaphores = 0x4;
156 resource_limit->max_timers = 0x4;
157 resource_limit->max_shared_mems = 0x8;
158 resource_limit->max_address_arbiters = 0x3;
159 resource_limit->max_cpu_time = 0x2710;
160 resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
161
162 // Create the LIB_APPLET resource limit
163 resource_limit = ResourceLimit::Create(kernel, "Library Applets");
164 resource_limit->max_priority = 0x4;
165 resource_limit->max_commit = 0x600000;
166 resource_limit->max_threads = 0xE;
167 resource_limit->max_events = 0x8;
168 resource_limit->max_mutexes = 0x8;
169 resource_limit->max_semaphores = 0x4;
170 resource_limit->max_timers = 0x4;
171 resource_limit->max_shared_mems = 0x8;
172 resource_limit->max_address_arbiters = 0x1;
173 resource_limit->max_cpu_time = 0x2710;
174 resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
175
176 // Create the OTHER resource limit
177 resource_limit = ResourceLimit::Create(kernel, "Others");
178 resource_limit->max_priority = 0x4;
179 resource_limit->max_commit = 0x2180000;
180 resource_limit->max_threads = 0xE1;
181 resource_limit->max_events = 0x108;
182 resource_limit->max_mutexes = 0x25;
183 resource_limit->max_semaphores = 0x43;
184 resource_limit->max_timers = 0x2C;
185 resource_limit->max_shared_mems = 0x1F;
186 resource_limit->max_address_arbiters = 0x2D;
187 resource_limit->max_cpu_time = 0x3E8;
188 resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
189 } 143 }
190 144
191 void InitializeThreads() { 145 void InitializeThreads() {
@@ -208,7 +162,7 @@ struct KernelCore::Impl {
208 std::vector<SharedPtr<Process>> process_list; 162 std::vector<SharedPtr<Process>> process_list;
209 Process* current_process = nullptr; 163 Process* current_process = nullptr;
210 164
211 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 165 SharedPtr<ResourceLimit> system_resource_limit;
212 166
213 /// The event type of the generic timer callback event 167 /// The event type of the generic timer callback event
214 CoreTiming::EventType* timer_callback_event_type = nullptr; 168 CoreTiming::EventType* timer_callback_event_type = nullptr;
@@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
239 impl->Shutdown(); 193 impl->Shutdown();
240} 194}
241 195
242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 196SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
243 ResourceLimitCategory category) const { 197 return impl->system_resource_limit;
244 return impl->resource_limits.at(static_cast<std::size_t>(category));
245} 198}
246 199
247SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { 200SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f822d524..ea00c89f5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -24,8 +24,6 @@ class ResourceLimit;
24class Thread; 24class Thread;
25class Timer; 25class Timer;
26 26
27enum class ResourceLimitCategory : u8;
28
29/// Represents a single instance of the kernel. 27/// Represents a single instance of the kernel.
30class KernelCore { 28class KernelCore {
31private: 29private:
@@ -47,8 +45,8 @@ public:
47 /// Clears all resources in use by the kernel instance. 45 /// Clears all resources in use by the kernel instance.
48 void Shutdown(); 46 void Shutdown();
49 47
50 /// Retrieves a shared pointer to a ResourceLimit identified by the given category. 48 /// Retrieves a shared pointer to the system resource limit instance.
51 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; 49 SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
52 50
53 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. 51 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
54 SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; 52 SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 420218d59..7ca538401 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,12 +4,11 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <random>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/common_funcs.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 11#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 14#include "core/hle/kernel/resource_limit.h"
@@ -17,6 +16,7 @@
17#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 17#include "core/hle/kernel/vm_manager.h"
19#include "core/memory.h" 18#include "core/memory.h"
19#include "core/settings.h"
20 20
21namespace Kernel { 21namespace Kernel {
22 22
@@ -29,12 +29,17 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
29 process->name = std::move(name); 29 process->name = std::move(name);
30 process->flags.raw = 0; 30 process->flags.raw = 0;
31 process->flags.memory_region.Assign(MemoryRegion::APPLICATION); 31 process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
32 process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); 32 process->resource_limit = kernel.GetSystemResourceLimit();
33 process->status = ProcessStatus::Created; 33 process->status = ProcessStatus::Created;
34 process->program_id = 0; 34 process->program_id = 0;
35 process->process_id = kernel.CreateNewProcessID(); 35 process->process_id = kernel.CreateNewProcessID();
36 process->svc_access_mask.set(); 36 process->svc_access_mask.set();
37 37
38 std::mt19937 rng(Settings::values.rng_seed.value_or(0));
39 std::uniform_int_distribution<u64> distribution;
40 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
41 [&] { return distribution(rng); });
42
38 kernel.AppendNewProcess(process); 43 kernel.AppendNewProcess(process);
39 return process; 44 return process;
40} 45}
@@ -241,83 +246,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
241} 246}
242 247
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 248ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
244 if (target < vm_manager.GetHeapRegionBaseAddress() || 249 return vm_manager.HeapAllocate(target, size, perms);
245 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
246 return ERR_INVALID_ADDRESS;
247 }
248
249 if (heap_memory == nullptr) {
250 // Initialize heap
251 heap_memory = std::make_shared<std::vector<u8>>();
252 heap_start = heap_end = target;
253 } else {
254 vm_manager.UnmapRange(heap_start, heap_end - heap_start);
255 }
256
257 // If necessary, expand backing vector to cover new heap extents.
258 if (target < heap_start) {
259 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
260 heap_start = target;
261 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
262 }
263 if (target + size > heap_end) {
264 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
265 heap_end = target + size;
266 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
267 }
268 ASSERT(heap_end - heap_start == heap_memory->size());
269
270 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
271 size, MemoryState::Heap));
272 vm_manager.Reprotect(vma, perms);
273
274 heap_used = size;
275
276 return MakeResult<VAddr>(heap_end - size);
277} 250}
278 251
279ResultCode Process::HeapFree(VAddr target, u32 size) { 252ResultCode Process::HeapFree(VAddr target, u32 size) {
280 if (target < vm_manager.GetHeapRegionBaseAddress() || 253 return vm_manager.HeapFree(target, size);
281 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
282 return ERR_INVALID_ADDRESS;
283 }
284
285 if (size == 0) {
286 return RESULT_SUCCESS;
287 }
288
289 ResultCode result = vm_manager.UnmapRange(target, size);
290 if (result.IsError())
291 return result;
292
293 heap_used -= size;
294
295 return RESULT_SUCCESS;
296} 254}
297 255
298ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 256ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
299 auto vma = vm_manager.FindVMA(src_addr); 257 return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
300
301 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
302 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
303
304 // The returned VMA might be a bigger one encompassing the desired address.
305 auto vma_offset = src_addr - vma->first;
306 ASSERT_MSG(vma_offset + size <= vma->second.size,
307 "Shared memory exceeds bounds of mapped block");
308
309 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
310 std::size_t backing_block_offset = vma->second.offset + vma_offset;
311
312 CASCADE_RESULT(auto new_vma,
313 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
314 MemoryState::Mapped));
315 // Protect mirror with permissions from old region
316 vm_manager.Reprotect(new_vma, vma->second.permissions);
317 // Remove permissions from old region
318 vm_manager.Reprotect(vma, VMAPermission::None);
319
320 return RESULT_SUCCESS;
321} 258}
322 259
323ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 260ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 8d2616c79..ada845c7f 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -119,6 +119,8 @@ struct CodeSet final {
119 119
120class Process final : public Object { 120class Process final : public Object {
121public: 121public:
122 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
123
122 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); 124 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
123 125
124 std::string GetTypeName() const override { 126 std::string GetTypeName() const override {
@@ -212,6 +214,11 @@ public:
212 total_process_running_time_ticks += ticks; 214 total_process_running_time_ticks += ticks;
213 } 215 }
214 216
217 /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
218 u64 GetRandomEntropy(std::size_t index) const {
219 return random_entropy.at(index);
220 }
221
215 /** 222 /**
216 * Loads process-specifics configuration info with metadata provided 223 * Loads process-specifics configuration info with metadata provided
217 * by an executable. 224 * by an executable.
@@ -251,7 +258,8 @@ public:
251 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 258 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
252 ResultCode HeapFree(VAddr target, u32 size); 259 ResultCode HeapFree(VAddr target, u32 size);
253 260
254 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 261 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
262 MemoryState state = MemoryState::Mapped);
255 263
256 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 264 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
257 265
@@ -292,17 +300,6 @@ private:
292 u32 allowed_thread_priority_mask = 0xFFFFFFFF; 300 u32 allowed_thread_priority_mask = 0xFFFFFFFF;
293 u32 is_virtual_address_memory_enabled = 0; 301 u32 is_virtual_address_memory_enabled = 0;
294 302
295 // Memory used to back the allocations in the regular heap. A single vector is used to cover
296 // the entire virtual address space extents that bound the allocations, including any holes.
297 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
298 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
299 std::shared_ptr<std::vector<u8>> heap_memory;
300
301 // The left/right bounds of the address space covered by heap_memory.
302 VAddr heap_start = 0;
303 VAddr heap_end = 0;
304 u64 heap_used = 0;
305
306 /// The Thread Local Storage area is allocated as processes create threads, 303 /// The Thread Local Storage area is allocated as processes create threads,
307 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part 304 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
308 /// holds the TLS for a specific thread. This vector contains which parts are in use for each 305 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
@@ -321,6 +318,9 @@ private:
321 /// Per-process handle table for storing created object handles in. 318 /// Per-process handle table for storing created object handles in.
322 HandleTable handle_table; 319 HandleTable handle_table;
323 320
321 /// Random values for svcGetInfo RandomEntropy
322 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
323
324 std::string name; 324 std::string name;
325}; 325};
326 326
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index b253a680f..2f9695005 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -2,12 +2,16 @@
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 <cstring> 5#include "core/hle/kernel/errors.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/kernel/resource_limit.h" 6#include "core/hle/kernel/resource_limit.h"
7#include "core/hle/result.h"
9 8
10namespace Kernel { 9namespace Kernel {
10namespace {
11constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
12 return static_cast<std::size_t>(type);
13}
14} // Anonymous namespace
11 15
12ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} 16ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
13ResourceLimit::~ResourceLimit() = default; 17ResourceLimit::~ResourceLimit() = default;
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
19 return resource_limit; 23 return resource_limit;
20} 24}
21 25
22s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { 26s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
23 switch (resource) { 27 return values.at(ResourceTypeToIndex(resource));
24 case ResourceType::Commit: 28}
25 return current_commit; 29
26 case ResourceType::Thread: 30s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
27 return current_threads; 31 return limits.at(ResourceTypeToIndex(resource));
28 case ResourceType::Event:
29 return current_events;
30 case ResourceType::Mutex:
31 return current_mutexes;
32 case ResourceType::Semaphore:
33 return current_semaphores;
34 case ResourceType::Timer:
35 return current_timers;
36 case ResourceType::SharedMemory:
37 return current_shared_mems;
38 case ResourceType::AddressArbiter:
39 return current_address_arbiters;
40 case ResourceType::CPUTime:
41 return current_cpu_time;
42 default:
43 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
44 UNIMPLEMENTED();
45 return 0;
46 }
47} 32}
48 33
49u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { 34ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
50 switch (resource) { 35 const auto index = ResourceTypeToIndex(resource);
51 case ResourceType::Priority: 36
52 return max_priority; 37 if (value < values[index]) {
53 case ResourceType::Commit: 38 return ERR_INVALID_STATE;
54 return max_commit;
55 case ResourceType::Thread:
56 return max_threads;
57 case ResourceType::Event:
58 return max_events;
59 case ResourceType::Mutex:
60 return max_mutexes;
61 case ResourceType::Semaphore:
62 return max_semaphores;
63 case ResourceType::Timer:
64 return max_timers;
65 case ResourceType::SharedMemory:
66 return max_shared_mems;
67 case ResourceType::AddressArbiter:
68 return max_address_arbiters;
69 case ResourceType::CPUTime:
70 return max_cpu_time;
71 default:
72 LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
73 UNIMPLEMENTED();
74 return 0;
75 } 39 }
40
41 values[index] = value;
42 return RESULT_SUCCESS;
76} 43}
77} // namespace Kernel 44} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 219e49562..bec065543 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -4,31 +4,25 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/hle/kernel/object.h" 9#include "core/hle/kernel/object.h"
9 10
11union ResultCode;
12
10namespace Kernel { 13namespace Kernel {
11 14
12class KernelCore; 15class KernelCore;
13 16
14enum class ResourceLimitCategory : u8 {
15 APPLICATION = 0,
16 SYS_APPLET = 1,
17 LIB_APPLET = 2,
18 OTHER = 3
19};
20
21enum class ResourceType { 17enum class ResourceType {
22 Priority = 0, 18 PhysicalMemory,
23 Commit = 1, 19 Threads,
24 Thread = 2, 20 Events,
25 Event = 3, 21 TransferMemory,
26 Mutex = 4, 22 Sessions,
27 Semaphore = 5, 23
28 Timer = 6, 24 // Used as a count, not an actual type.
29 SharedMemory = 7, 25 ResourceTypeCount
30 AddressArbiter = 8,
31 CPUTime = 9,
32}; 26};
33 27
34class ResourceLimit final : public Object { 28class ResourceLimit final : public Object {
@@ -55,61 +49,51 @@ public:
55 * @param resource Requested resource type 49 * @param resource Requested resource type
56 * @returns The current value of the resource type 50 * @returns The current value of the resource type
57 */ 51 */
58 s32 GetCurrentResourceValue(ResourceType resource) const; 52 s64 GetCurrentResourceValue(ResourceType resource) const;
59 53
60 /** 54 /**
61 * Gets the max value for the specified resource. 55 * Gets the max value for the specified resource.
62 * @param resource Requested resource type 56 * @param resource Requested resource type
63 * @returns The max value of the resource type 57 * @returns The max value of the resource type
64 */ 58 */
65 u32 GetMaxResourceValue(ResourceType resource) const; 59 s64 GetMaxResourceValue(ResourceType resource) const;
66
67 /// Name of resource limit object.
68 std::string name;
69
70 /// Max thread priority that a process in this category can create
71 s32 max_priority = 0;
72
73 /// Max memory that processes in this category can use
74 s32 max_commit = 0;
75 60
76 ///< Max number of objects that can be collectively created by the processes in this category 61 /**
77 s32 max_threads = 0; 62 * Sets the limit value for a given resource type.
78 s32 max_events = 0; 63 *
79 s32 max_mutexes = 0; 64 * @param resource The resource type to apply the limit to.
80 s32 max_semaphores = 0; 65 * @param value The limit to apply to the given resource type.
81 s32 max_timers = 0; 66 *
82 s32 max_shared_mems = 0; 67 * @return A result code indicating if setting the limit value
83 s32 max_address_arbiters = 0; 68 * was successful or not.
69 *
70 * @note The supplied limit value *must* be greater than or equal to
71 * the current resource value for the given resource type,
72 * otherwise ERR_INVALID_STATE will be returned.
73 */
74 ResultCode SetLimitValue(ResourceType resource, s64 value);
84 75
85 /// Max CPU time that the processes in this category can utilize 76private:
86 s32 max_cpu_time = 0; 77 explicit ResourceLimit(KernelCore& kernel);
78 ~ResourceLimit() override;
87 79
88 // TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind 80 // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
89 // that APPLICATION resource limits should not be affected by the objects created by service 81 // functions
90 // modules. 82 //
91 // Currently we have no way of distinguishing if a Create was called by the running application, 83 // Currently we have no way of distinguishing if a Create was called by the running application,
92 // or by a service module. Approach this once we have separated the service modules into their 84 // or by a service module. Approach this once we have separated the service modules into their
93 // own processes 85 // own processes
94 86
95 /// Current memory that the processes in this category are using 87 using ResourceArray =
96 s32 current_commit = 0; 88 std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
97 89
98 ///< Current number of objects among all processes in this category 90 /// Maximum values a resource type may reach.
99 s32 current_threads = 0; 91 ResourceArray limits{};
100 s32 current_events = 0; 92 /// Current resource limit values.
101 s32 current_mutexes = 0; 93 ResourceArray values{};
102 s32 current_semaphores = 0;
103 s32 current_timers = 0;
104 s32 current_shared_mems = 0;
105 s32 current_address_arbiters = 0;
106 94
107 /// Current CPU time that the processes in this category are utilizing 95 /// Name of resource limit object.
108 s32 current_cpu_time = 0; 96 std::string name;
109
110private:
111 explicit ResourceLimit(KernelCore& kernel);
112 ~ResourceLimit() override;
113}; 97};
114 98
115} // namespace Kernel 99} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a016a86b6..0494581f5 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
61} 61}
62 62
63SharedPtr<SharedMemory> SharedMemory::CreateForApplet( 63SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
64 KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size, 64 KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
65 MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { 65 MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
66 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); 66 SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
67 67
@@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
78 return shared_memory; 78 return shared_memory;
79} 79}
80 80
81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, 81ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
82 MemoryPermission other_permissions) { 82 MemoryPermission other_permissions) {
83 const MemoryPermission own_other_permissions = 83 const MemoryPermission own_other_permissions =
84 target_process == owner_process ? this->permissions : this->other_permissions; 84 &target_process == owner_process ? this->permissions : this->other_permissions;
85 85
86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
@@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
106 VAddr target_address = address; 106 VAddr target_address = address;
107 107
108 // Map the memory block into the target process 108 // Map the memory block into the target process
109 auto result = target_process->VMManager().MapMemoryBlock( 109 auto result = target_process.VMManager().MapMemoryBlock(
110 target_address, backing_block, backing_block_offset, size, MemoryState::Shared); 110 target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
111 if (result.Failed()) { 111 if (result.Failed()) {
112 LOG_ERROR( 112 LOG_ERROR(
@@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
116 return result.Code(); 116 return result.Code();
117 } 117 }
118 118
119 return target_process->VMManager().ReprotectRange(target_address, size, 119 return target_process.VMManager().ReprotectRange(target_address, size,
120 ConvertPermissions(permissions)); 120 ConvertPermissions(permissions));
121} 121}
122 122
123ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { 123ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
124 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not 124 // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
125 // mapped to a SharedMemory. 125 // mapped to a SharedMemory.
126 return target_process->VMManager().UnmapRange(address, size); 126 return target_process.VMManager().UnmapRange(address, size);
127} 127}
128 128
129VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { 129VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
@@ -132,7 +132,11 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
132 return static_cast<VMAPermission>(masked_permissions); 132 return static_cast<VMAPermission>(masked_permissions);
133} 133}
134 134
135u8* SharedMemory::GetPointer(u32 offset) { 135u8* SharedMemory::GetPointer(std::size_t offset) {
136 return backing_block->data() + backing_block_offset + offset;
137}
138
139const u8* SharedMemory::GetPointer(std::size_t offset) const {
136 return backing_block->data() + backing_block_offset + offset; 140 return backing_block->data() + backing_block_offset + offset;
137} 141}
138 142
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 2c06bb7ce..0b48db699 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -64,7 +64,7 @@ public:
64 */ 64 */
65 static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel, 65 static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
66 std::shared_ptr<std::vector<u8>> heap_block, 66 std::shared_ptr<std::vector<u8>> heap_block,
67 u32 offset, u32 size, 67 std::size_t offset, u64 size,
68 MemoryPermission permissions, 68 MemoryPermission permissions,
69 MemoryPermission other_permissions, 69 MemoryPermission other_permissions,
70 std::string name = "Unknown Applet"); 70 std::string name = "Unknown Applet");
@@ -81,6 +81,11 @@ public:
81 return HANDLE_TYPE; 81 return HANDLE_TYPE;
82 } 82 }
83 83
84 /// Gets the size of the underlying memory block in bytes.
85 u64 GetSize() const {
86 return size;
87 }
88
84 /** 89 /**
85 * Converts the specified MemoryPermission into the equivalent VMAPermission. 90 * Converts the specified MemoryPermission into the equivalent VMAPermission.
86 * @param permission The MemoryPermission to convert. 91 * @param permission The MemoryPermission to convert.
@@ -94,44 +99,51 @@ public:
94 * @param permissions Memory block map permissions (specified by SVC field) 99 * @param permissions Memory block map permissions (specified by SVC field)
95 * @param other_permissions Memory block map other permissions (specified by SVC field) 100 * @param other_permissions Memory block map other permissions (specified by SVC field)
96 */ 101 */
97 ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions, 102 ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
98 MemoryPermission other_permissions); 103 MemoryPermission other_permissions);
99 104
100 /** 105 /**
101 * Unmaps a shared memory block from the specified address in system memory 106 * Unmaps a shared memory block from the specified address in system memory
102 * @param target_process Process from which to umap the memory block. 107 * @param target_process Process from which to unmap the memory block.
103 * @param address Address in system memory where the shared memory block is mapped 108 * @param address Address in system memory where the shared memory block is mapped
104 * @return Result code of the unmap operation 109 * @return Result code of the unmap operation
105 */ 110 */
106 ResultCode Unmap(Process* target_process, VAddr address); 111 ResultCode Unmap(Process& target_process, VAddr address);
107 112
108 /** 113 /**
109 * Gets a pointer to the shared memory block 114 * Gets a pointer to the shared memory block
110 * @param offset Offset from the start of the shared memory block to get pointer 115 * @param offset Offset from the start of the shared memory block to get pointer
111 * @return Pointer to the shared memory block from the specified offset 116 * @return A pointer to the shared memory block from the specified offset
112 */ 117 */
113 u8* GetPointer(u32 offset = 0); 118 u8* GetPointer(std::size_t offset = 0);
119
120 /**
121 * Gets a constant pointer to the shared memory block
122 * @param offset Offset from the start of the shared memory block to get pointer
123 * @return A constant pointer to the shared memory block from the specified offset
124 */
125 const u8* GetPointer(std::size_t offset = 0) const;
126
127private:
128 explicit SharedMemory(KernelCore& kernel);
129 ~SharedMemory() override;
114 130
115 /// Process that created this shared memory block.
116 SharedPtr<Process> owner_process;
117 /// Address of shared memory block in the owner process if specified.
118 VAddr base_address;
119 /// Backing memory for this shared memory block. 131 /// Backing memory for this shared memory block.
120 std::shared_ptr<std::vector<u8>> backing_block; 132 std::shared_ptr<std::vector<u8>> backing_block;
121 /// Offset into the backing block for this shared memory. 133 /// Offset into the backing block for this shared memory.
122 std::size_t backing_block_offset; 134 std::size_t backing_block_offset = 0;
123 /// Size of the memory block. Page-aligned. 135 /// Size of the memory block. Page-aligned.
124 u64 size; 136 u64 size = 0;
125 /// Permission restrictions applied to the process which created the block. 137 /// Permission restrictions applied to the process which created the block.
126 MemoryPermission permissions; 138 MemoryPermission permissions{};
127 /// Permission restrictions applied to other processes mapping the block. 139 /// Permission restrictions applied to other processes mapping the block.
128 MemoryPermission other_permissions; 140 MemoryPermission other_permissions{};
141 /// Process that created this shared memory block.
142 SharedPtr<Process> owner_process;
143 /// Address of shared memory block in the owner process if specified.
144 VAddr base_address = 0;
129 /// Name of shared memory object. 145 /// Name of shared memory object.
130 std::string name; 146 std::string name;
131
132private:
133 explicit SharedMemory(KernelCore& kernel);
134 ~SharedMemory() override;
135}; 147};
136 148
137} // namespace Kernel 149} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c7c579aaf..b8b6b4d49 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -34,6 +34,7 @@
34#include "core/hle/lock.h" 34#include "core/hle/lock.h"
35#include "core/hle/result.h" 35#include "core/hle/result.h"
36#include "core/hle/service/service.h" 36#include "core/hle/service/service.h"
37#include "core/settings.h"
37 38
38namespace Kernel { 39namespace Kernel {
39namespace { 40namespace {
@@ -122,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
122 return RESULT_SUCCESS; 123 return RESULT_SUCCESS;
123} 124}
124 125
126static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
127 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
128
129 if (!Common::Is4KBAligned(addr)) {
130 return ERR_INVALID_ADDRESS;
131 }
132
133 if (size == 0 || !Common::Is4KBAligned(size)) {
134 return ERR_INVALID_SIZE;
135 }
136
137 if (!IsValidAddressRange(addr, size)) {
138 return ERR_INVALID_ADDRESS_STATE;
139 }
140
141 const auto permission = static_cast<MemoryPermission>(prot);
142 if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
143 permission != MemoryPermission::ReadWrite) {
144 return ERR_INVALID_MEMORY_PERMISSIONS;
145 }
146
147 auto* const current_process = Core::CurrentProcess();
148 auto& vm_manager = current_process->VMManager();
149
150 if (!IsInsideAddressSpace(vm_manager, addr, size)) {
151 return ERR_INVALID_ADDRESS_STATE;
152 }
153
154 const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
155 if (iter == vm_manager.vma_map.end()) {
156 return ERR_INVALID_ADDRESS_STATE;
157 }
158
159 LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
160 // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
161 // make sense to allow changing permissions on kernel memory itself, etc).
162
163 const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
164
165 return vm_manager.ReprotectRange(addr, size, converted_permissions);
166}
167
125static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) { 168static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
126 LOG_WARNING(Kernel_SVC, 169 LOG_WARNING(Kernel_SVC,
127 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr, 170 "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
@@ -171,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
171 // Read 1 char beyond the max allowed port name to detect names that are too long. 214 // Read 1 char beyond the max allowed port name to detect names that are too long.
172 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); 215 std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
173 if (port_name.size() > PortNameMaxLength) { 216 if (port_name.size() > PortNameMaxLength) {
174 return ERR_PORT_NAME_TOO_LONG; 217 return ERR_OUT_OF_RANGE;
175 } 218 }
176 219
177 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 220 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -267,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
267 310
268 static constexpr u64 MaxHandles = 0x40; 311 static constexpr u64 MaxHandles = 0x40;
269 312
270 if (handle_count > MaxHandles) 313 if (handle_count > MaxHandles) {
271 return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); 314 return ERR_OUT_OF_RANGE;
315 }
272 316
273 auto* const thread = GetCurrentThread(); 317 auto* const thread = GetCurrentThread();
274 318
@@ -333,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
333 } 377 }
334 378
335 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); 379 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
336 thread->SetWaitSynchronizationResult( 380 thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
337 ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
338 thread->ResumeFromWait(); 381 thread->ResumeFromWait();
339 return RESULT_SUCCESS; 382 return RESULT_SUCCESS;
340} 383}
@@ -395,16 +438,42 @@ struct BreakReason {
395/// Break program execution 438/// Break program execution
396static void Break(u32 reason, u64 info1, u64 info2) { 439static void Break(u32 reason, u64 info1, u64 info2) {
397 BreakReason break_reason{reason}; 440 BreakReason break_reason{reason};
441 bool has_dumped_buffer{};
442
443 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
444 if (sz == 0 || addr == 0 || has_dumped_buffer) {
445 return;
446 }
398 447
448 // This typically is an error code so we're going to assume this is the case
449 if (sz == sizeof(u32)) {
450 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
451 } else {
452 // We don't know what's in here so we'll hexdump it
453 std::vector<u8> debug_buffer(sz);
454 Memory::ReadBlock(addr, debug_buffer.data(), sz);
455 std::string hexdump;
456 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
457 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
458 if (i != 0 && i % 16 == 0) {
459 hexdump += '\n';
460 }
461 }
462 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
463 }
464 has_dumped_buffer = true;
465 };
399 switch (break_reason.break_type) { 466 switch (break_reason.break_type) {
400 case BreakType::Panic: 467 case BreakType::Panic:
401 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", 468 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
402 info1, info2); 469 info1, info2);
470 handle_debug_buffer(info1, info2);
403 break; 471 break;
404 case BreakType::AssertionFailed: 472 case BreakType::AssertionFailed:
405 LOG_CRITICAL(Debug_Emulated, 473 LOG_CRITICAL(Debug_Emulated,
406 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", 474 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
407 info1, info2); 475 info1, info2);
476 handle_debug_buffer(info1, info2);
408 break; 477 break;
409 case BreakType::PreNROLoad: 478 case BreakType::PreNROLoad:
410 LOG_WARNING( 479 LOG_WARNING(
@@ -433,6 +502,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
433 Debug_Emulated, 502 Debug_Emulated,
434 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", 503 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
435 static_cast<u32>(break_reason.break_type.Value()), info1, info2); 504 static_cast<u32>(break_reason.break_type.Value()), info1, info2);
505 handle_debug_buffer(info1, info2);
436 break; 506 break;
437 } 507 }
438 508
@@ -441,6 +511,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
441 Debug_Emulated, 511 Debug_Emulated,
442 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 512 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
443 reason, info1, info2); 513 reason, info1, info2);
514 handle_debug_buffer(info1, info2);
444 ASSERT(false); 515 ASSERT(false);
445 516
446 Core::CurrentProcess()->PrepareForTermination(); 517 Core::CurrentProcess()->PrepareForTermination();
@@ -530,7 +601,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
530 *result = 0; 601 *result = 0;
531 break; 602 break;
532 case GetInfoType::RandomEntropy: 603 case GetInfoType::RandomEntropy:
533 *result = 0; 604 if (handle != 0) {
605 return ERR_INVALID_HANDLE;
606 }
607
608 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
609 return ERR_INVALID_COMBINATION;
610 }
611
612 *result = current_process->GetRandomEntropy(info_sub_id);
613 return RESULT_SUCCESS;
534 break; 614 break;
535 case GetInfoType::ASLRRegionBaseAddr: 615 case GetInfoType::ASLRRegionBaseAddr:
536 *result = vm_manager.GetASLRRegionBaseAddress(); 616 *result = vm_manager.GetASLRRegionBaseAddress();
@@ -563,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
563 case GetInfoType::ThreadTickCount: { 643 case GetInfoType::ThreadTickCount: {
564 constexpr u64 num_cpus = 4; 644 constexpr u64 num_cpus = 4;
565 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { 645 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
566 return ERR_INVALID_COMBINATION_KERNEL; 646 return ERR_INVALID_COMBINATION;
567 } 647 }
568 648
569 const auto thread = 649 const auto thread =
@@ -656,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
656 736
657 const auto* const current_process = Core::CurrentProcess(); 737 const auto* const current_process = Core::CurrentProcess();
658 738
659 // Note: The kernel uses the current process's resource limit instead of
660 // the one from the thread owner's resource limit.
661 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
662 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
663 return ERR_INVALID_THREAD_PRIORITY;
664 }
665
666 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); 739 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
667 if (!thread) { 740 if (!thread) {
668 return ERR_INVALID_HANDLE; 741 return ERR_INVALID_HANDLE;
@@ -716,7 +789,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
716 return ERR_INVALID_MEMORY_RANGE; 789 return ERR_INVALID_MEMORY_RANGE;
717 } 790 }
718 791
719 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare); 792 return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
720} 793}
721 794
722static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 795static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
@@ -746,7 +819,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
746 return ERR_INVALID_MEMORY_RANGE; 819 return ERR_INVALID_MEMORY_RANGE;
747 } 820 }
748 821
749 return shared_memory->Unmap(current_process, addr); 822 return shared_memory->Unmap(*current_process, addr);
750} 823}
751 824
752/// Query process memory 825/// Query process memory
@@ -805,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
805 } 878 }
806 879
807 auto* const current_process = Core::CurrentProcess(); 880 auto* const current_process = Core::CurrentProcess();
808 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
809 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
810 return ERR_INVALID_THREAD_PRIORITY;
811 }
812 881
813 if (processor_id == THREADPROCESSORID_DEFAULT) { 882 if (processor_id == THREADPROCESSORID_DEFAULT) {
814 // Set the target CPU to the one specified in the process' exheader. 883 // Set the target CPU to the one specified in the process' exheader.
@@ -1101,7 +1170,7 @@ static ResultCode CloseHandle(Handle handle) {
1101 1170
1102/// Reset an event 1171/// Reset an event
1103static ResultCode ResetSignal(Handle handle) { 1172static ResultCode ResetSignal(Handle handle) {
1104 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1173 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1105 1174
1106 const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); 1175 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1107 auto event = handle_table.Get<Event>(handle); 1176 auto event = handle_table.Get<Event>(handle);
@@ -1114,9 +1183,39 @@ static ResultCode ResetSignal(Handle handle) {
1114 1183
1115/// Creates a TransferMemory object 1184/// Creates a TransferMemory object
1116static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { 1185static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
1117 LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, 1186 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
1118 permissions); 1187 permissions);
1119 *handle = 0; 1188
1189 if (!Common::Is4KBAligned(addr)) {
1190 LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
1191 return ERR_INVALID_ADDRESS;
1192 }
1193
1194 if (!Common::Is4KBAligned(size) || size == 0) {
1195 LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
1196 return ERR_INVALID_ADDRESS;
1197 }
1198
1199 if (!IsValidAddressRange(addr, size)) {
1200 LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
1201 addr, size);
1202 return ERR_INVALID_ADDRESS_STATE;
1203 }
1204
1205 const auto perms = static_cast<MemoryPermission>(permissions);
1206 if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
1207 perms != MemoryPermission::ReadWrite) {
1208 LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
1209 permissions);
1210 return ERR_INVALID_MEMORY_PERMISSIONS;
1211 }
1212
1213 auto& kernel = Core::System::GetInstance().Kernel();
1214 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1215 const auto shared_mem_handle = SharedMemory::Create(
1216 kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
1217
1218 CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
1120 return RESULT_SUCCESS; 1219 return RESULT_SUCCESS;
1121} 1220}
1122 1221
@@ -1156,7 +1255,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1156 } 1255 }
1157 1256
1158 if (mask == 0) { 1257 if (mask == 0) {
1159 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1258 return ERR_INVALID_COMBINATION;
1160 } 1259 }
1161 1260
1162 /// This value is used to only change the affinity mask without changing the current ideal core. 1261 /// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1165,12 +1264,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1165 if (core == OnlyChangeMask) { 1264 if (core == OnlyChangeMask) {
1166 core = thread->GetIdealCore(); 1265 core = thread->GetIdealCore();
1167 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { 1266 } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
1168 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 1267 return ERR_INVALID_PROCESSOR_ID;
1169 } 1268 }
1170 1269
1171 // Error out if the input core isn't enabled in the input mask. 1270 // Error out if the input core isn't enabled in the input mask.
1172 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) { 1271 if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
1173 return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination); 1272 return ERR_INVALID_COMBINATION;
1174 } 1273 }
1175 1274
1176 thread->ChangeCore(core, mask); 1275 thread->ChangeCore(core, mask);
@@ -1259,7 +1358,7 @@ struct FunctionDef {
1259static const FunctionDef SVC_Table[] = { 1358static const FunctionDef SVC_Table[] = {
1260 {0x00, nullptr, "Unknown"}, 1359 {0x00, nullptr, "Unknown"},
1261 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, 1360 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
1262 {0x02, nullptr, "SetMemoryPermission"}, 1361 {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
1263 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, 1362 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
1264 {0x04, SvcWrap<MapMemory>, "MapMemory"}, 1363 {0x04, SvcWrap<MapMemory>, "MapMemory"},
1265 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, 1364 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b09753c80..233a99fb0 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -121,6 +121,11 @@ void SvcWrap() {
121 FuncReturn(func(Param(0), Param(1), Param(2)).raw); 121 FuncReturn(func(Param(0), Param(1), Param(2)).raw);
122} 122}
123 123
124template <ResultCode func(u64, u64, u32)>
125void SvcWrap() {
126 FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
127}
128
124template <ResultCode func(u32, u64, u64, u32)> 129template <ResultCode func(u32, u64, u64, u32)>
125void SvcWrap() { 130void SvcWrap() {
126 FuncReturn( 131 FuncReturn(
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index dd5cd9ced..4ffb76818 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
142 142
143 status = ThreadStatus::Ready; 143 status = ThreadStatus::Ready;
144 144
145 std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); 145 ChangeScheduler();
146 if (!new_processor_id) {
147 new_processor_id = processor_id;
148 }
149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core;
152 }
153
154 ASSERT(*new_processor_id < 4);
155
156 // Add thread to new core's scheduler
157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158
159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler
161 scheduler->RemoveThread(this);
162 next_scheduler->AddThread(this, current_priority);
163 }
164
165 processor_id = *new_processor_id;
166
167 // If the thread was ready, unschedule from the previous core and schedule on the new core
168 scheduler->UnscheduleThread(this, current_priority);
169 next_scheduler->ScheduleThread(this, current_priority);
170
171 // Change thread's scheduler
172 scheduler = next_scheduler;
173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 146}
176 147
177/** 148/**
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
364void Thread::ChangeCore(u32 core, u64 mask) { 335void Thread::ChangeCore(u32 core, u64 mask) {
365 ideal_core = core; 336 ideal_core = core;
366 affinity_mask = mask; 337 affinity_mask = mask;
338 ChangeScheduler();
339}
367 340
341void Thread::ChangeScheduler() {
368 if (status != ThreadStatus::Ready) { 342 if (status != ThreadStatus::Ready) {
369 return; 343 return;
370 } 344 }
371 345
346 auto& system = Core::System::GetInstance();
372 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; 347 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
373 348
374 if (!new_processor_id) { 349 if (!new_processor_id) {
375 new_processor_id = processor_id; 350 new_processor_id = processor_id;
376 } 351 }
377 if (ideal_core != -1 && 352 if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
379 new_processor_id = ideal_core; 353 new_processor_id = ideal_core;
380 } 354 }
381 355
382 ASSERT(*new_processor_id < 4); 356 ASSERT(*new_processor_id < 4);
383 357
384 // Add thread to new core's scheduler 358 // Add thread to new core's scheduler
385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); 359 auto& next_scheduler = system.Scheduler(*new_processor_id);
386 360
387 if (*new_processor_id != processor_id) { 361 if (*new_processor_id != processor_id) {
388 // Remove thread from previous core's scheduler 362 // Remove thread from previous core's scheduler
389 scheduler->RemoveThread(this); 363 scheduler->RemoveThread(this);
390 next_scheduler->AddThread(this, current_priority); 364 next_scheduler.AddThread(this, current_priority);
391 } 365 }
392 366
393 processor_id = *new_processor_id; 367 processor_id = *new_processor_id;
394 368
395 // If the thread was ready, unschedule from the previous core and schedule on the new core 369 // If the thread was ready, unschedule from the previous core and schedule on the new core
396 scheduler->UnscheduleThread(this, current_priority); 370 scheduler->UnscheduleThread(this, current_priority);
397 next_scheduler->ScheduleThread(this, current_priority); 371 next_scheduler.ScheduleThread(this, current_priority);
398 372
399 // Change thread's scheduler 373 // Change thread's scheduler
400 scheduler = next_scheduler; 374 scheduler = &next_scheduler;
401 375
402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 376 system.CpuCore(processor_id).PrepareReschedule();
403} 377}
404 378
405bool Thread::AllWaitObjectsReady() { 379bool Thread::AllWaitObjectsReady() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 4a6e11239..d384d50db 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -374,6 +374,8 @@ private:
374 explicit Thread(KernelCore& kernel); 374 explicit Thread(KernelCore& kernel);
375 ~Thread() override; 375 ~Thread() override;
376 376
377 void ChangeScheduler();
378
377 Core::ARM_Interface::ThreadContext context{}; 379 Core::ARM_Interface::ThreadContext context{};
378 380
379 u32 thread_id = 0; 381 u32 thread_id = 0;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 1a92c8f70..100f8f6bf 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -243,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
243 return RESULT_SUCCESS; 243 return RESULT_SUCCESS;
244} 244}
245 245
246ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
247 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
248 target + size < target) {
249 return ERR_INVALID_ADDRESS;
250 }
251
252 if (heap_memory == nullptr) {
253 // Initialize heap
254 heap_memory = std::make_shared<std::vector<u8>>();
255 heap_start = heap_end = target;
256 } else {
257 UnmapRange(heap_start, heap_end - heap_start);
258 }
259
260 // If necessary, expand backing vector to cover new heap extents.
261 if (target < heap_start) {
262 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
263 heap_start = target;
264 RefreshMemoryBlockMappings(heap_memory.get());
265 }
266 if (target + size > heap_end) {
267 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
268 heap_end = target + size;
269 RefreshMemoryBlockMappings(heap_memory.get());
270 }
271 ASSERT(heap_end - heap_start == heap_memory->size());
272
273 CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
274 MemoryState::Heap));
275 Reprotect(vma, perms);
276
277 heap_used = size;
278
279 return MakeResult<VAddr>(heap_end - size);
280}
281
282ResultCode VMManager::HeapFree(VAddr target, u64 size) {
283 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
284 target + size < target) {
285 return ERR_INVALID_ADDRESS;
286 }
287
288 if (size == 0) {
289 return RESULT_SUCCESS;
290 }
291
292 const ResultCode result = UnmapRange(target, size);
293 if (result.IsError()) {
294 return result;
295 }
296
297 heap_used -= size;
298 return RESULT_SUCCESS;
299}
300
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
302 const auto vma = FindVMA(src_addr);
303
304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
305 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
306
307 // The returned VMA might be a bigger one encompassing the desired address.
308 const auto vma_offset = src_addr - vma->first;
309 ASSERT_MSG(vma_offset + size <= vma->second.size,
310 "Shared memory exceeds bounds of mapped block");
311
312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
313 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
314
315 CASCADE_RESULT(auto new_vma,
316 MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
317 // Protect mirror with permissions from old region
318 Reprotect(new_vma, vma->second.permissions);
319 // Remove permissions from old region
320 Reprotect(vma, VMAPermission::None);
321
322 return RESULT_SUCCESS;
323}
324
246void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { 325void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
247 // If this ever proves to have a noticeable performance impact, allow users of the function to 326 // If this ever proves to have a noticeable performance impact, allow users of the function to
248 // specify a specific range of addresses to limit the scan to. 327 // specify a specific range of addresses to limit the scan to.
@@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
495} 574}
496 575
497u64 VMManager::GetTotalHeapUsage() const { 576u64 VMManager::GetTotalHeapUsage() const {
498 LOG_WARNING(Kernel, "(STUBBED) called"); 577 return heap_used;
499 return 0x0;
500} 578}
501 579
502VAddr VMManager::GetAddressSpaceBaseAddress() const { 580VAddr VMManager::GetAddressSpaceBaseAddress() const {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 2447cbb8f..d522404fe 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -186,6 +186,12 @@ public:
186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary. 186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); 187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
188 188
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size);
191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
193 MemoryState state = MemoryState::Mapped);
194
189 /** 195 /**
190 * Scans all VMAs and updates the page table range of any that use the given vector as backing 196 * Scans all VMAs and updates the page table range of any that use the given vector as backing
191 * memory. This should be called after any operation that causes reallocation of the vector. 197 * memory. This should be called after any operation that causes reallocation of the vector.
@@ -343,5 +349,15 @@ private:
343 349
344 VAddr tls_io_region_base = 0; 350 VAddr tls_io_region_base = 0;
345 VAddr tls_io_region_end = 0; 351 VAddr tls_io_region_end = 0;
352
353 // Memory used to back the allocations in the regular heap. A single vector is used to cover
354 // the entire virtual address space extents that bound the allocations, including any holes.
355 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
356 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
357 std::shared_ptr<std::vector<u8>> heap_memory;
358 // The left/right bounds of the address space covered by heap_memory.
359 VAddr heap_start = 0;
360 VAddr heap_end = 0;
361 u64 heap_used = 0;
346}; 362};
347} // namespace Kernel 363} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index c6b18cfba..bfb77cc31 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -19,8 +19,6 @@
19enum class ErrorDescription : u32 { 19enum class ErrorDescription : u32 {
20 Success = 0, 20 Success = 0,
21 RemoteProcessDead = 301, 21 RemoteProcessDead = 301,
22 InvalidOffset = 6061,
23 InvalidLength = 6062,
24}; 22};
25 23
26/** 24/**
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index c6437a671..c629f9357 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -242,6 +242,30 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
242 LOG_DEBUG(Service_ACC, "called"); 242 LOG_DEBUG(Service_ACC, "called");
243} 243}
244 244
245void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
246 LOG_DEBUG(Service_ACC, "called");
247 // A u8 is passed into this function which we can safely ignore. It's to determine if we have
248 // access to use the network or not by the looks of it
249 IPC::ResponseBuilder rb{ctx, 6};
250 if (profile_manager->GetUserCount() != 1) {
251 rb.Push(RESULT_SUCCESS);
252 rb.PushRaw<u128>(INVALID_UUID);
253 return;
254 }
255
256 const auto user_list = profile_manager->GetAllUsers();
257 if (std::all_of(user_list.begin(), user_list.end(),
258 [](const auto& user) { return user.uuid == INVALID_UUID; })) {
259 rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
260 rb.PushRaw<u128>(INVALID_UUID);
261 return;
262 }
263
264 // Select the first user we have
265 rb.Push(RESULT_SUCCESS);
266 rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
267}
268
245Module::Interface::Interface(std::shared_ptr<Module> module, 269Module::Interface::Interface(std::shared_ptr<Module> module,
246 std::shared_ptr<ProfileManager> profile_manager, const char* name) 270 std::shared_ptr<ProfileManager> profile_manager, const char* name)
247 : ServiceFramework(name), module(std::move(module)), 271 : ServiceFramework(name), module(std::move(module)),
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index c7ed74351..89b2104fa 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -27,6 +27,7 @@ public:
27 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); 27 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
28 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); 28 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
29 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); 29 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
30 void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
30 31
31 protected: 32 protected:
32 std::shared_ptr<Module> module; 33 std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index ad455c3a7..5e2030355 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_SU::GetProfile, "GetProfile"}, 17 {5, &ACC_SU::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 23 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 72d4adf35..a4d705b45 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_U0::GetProfile, "GetProfile"}, 17 {5, &ACC_U0::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 22 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 23 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index d480f08e5..8fffc93b5 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {5, &ACC_U1::GetProfile, "GetProfile"}, 17 {5, &ACC_U1::GetProfile, "GetProfile"},
18 {6, nullptr, "GetProfileDigest"}, 18 {6, nullptr, "GetProfileDigest"},
19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 19 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
20 {51, nullptr, "TrySelectUserWithoutInteraction"}, 20 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
21 {60, nullptr, "ListOpenContextStoredUsers"}, 21 {60, nullptr, "ListOpenContextStoredUsers"},
22 {100, nullptr, "GetUserRegistrationNotifier"}, 22 {100, nullptr, "GetUserRegistrationNotifier"},
23 {101, nullptr, "GetUserStateChangeNotifier"}, 23 {101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index c08394e4c..968263846 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -2,8 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
5#include <random> 6#include <random>
6 7
8#include <fmt/format.h>
9
7#include "common/file_util.h" 10#include "common/file_util.h"
8#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
9#include "core/settings.h" 12#include "core/settings.h"
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
39 return UUID{distribution(gen), distribution(gen)}; 42 return UUID{distribution(gen), distribution(gen)};
40} 43}
41 44
45std::string UUID::Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47}
48
49std::string UUID::FormatSwitch() const {
50 std::array<u8, 16> s{};
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56}
57
42ProfileManager::ProfileManager() { 58ProfileManager::ProfileManager() {
43 ParseUserSaveFile(); 59 ParseUserSaveFile();
44 60
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
325 return; 341 return;
326 } 342 }
327 343
328 for (std::size_t i = 0; i < MAX_USERS; ++i) { 344 for (const auto& user : data.users) {
329 const auto& user = data.users[i]; 345 if (user.uuid == UUID(INVALID_UUID)) {
346 continue;
347 }
330 348
331 if (user.uuid != UUID(INVALID_UUID)) 349 AddUser({user.uuid, user.username, user.timestamp, {}, false});
332 AddUser({user.uuid, user.username, user.timestamp, {}, false});
333 } 350 }
334 351
335 std::stable_partition(profiles.begin(), profiles.end(), 352 std::stable_partition(profiles.begin(), profiles.end(),
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 747c46c20..d2d8e6c6b 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -42,18 +42,9 @@ struct UUID {
42 void Invalidate() { 42 void Invalidate() {
43 uuid = INVALID_UUID; 43 uuid = INVALID_UUID;
44 } 44 }
45 std::string Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47 }
48 45
49 std::string FormatSwitch() const { 46 std::string Format() const;
50 std::array<u8, 16> s{}; 47 std::string FormatSwitch() const;
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56 }
57}; 48};
58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
59 50
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ac3ff9f20..4f17b52f9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,14 +6,19 @@
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include <stack> 8#include <stack>
9#include "audio_core/audio_renderer.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h" 12#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/am/am.h" 16#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applet_ae.h" 17#include "core/hle/service/am/applet_ae.h"
16#include "core/hle/service/am/applet_oe.h" 18#include "core/hle/service/am/applet_oe.h"
19#include "core/hle/service/am/applets/applets.h"
20#include "core/hle/service/am/applets/software_keyboard.h"
21#include "core/hle/service/am/applets/stub_applet.h"
17#include "core/hle/service/am/idle.h" 22#include "core/hle/service/am/idle.h"
18#include "core/hle/service/am/omm.h" 23#include "core/hle/service/am/omm.h"
19#include "core/hle/service/am/spsm.h" 24#include "core/hle/service/am/spsm.h"
@@ -28,6 +33,13 @@
28 33
29namespace Service::AM { 34namespace Service::AM {
30 35
36constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
37constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
38
39enum class AppletId : u32 {
40 SoftwareKeyboard = 0x11,
41};
42
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 43constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32 44
33struct LaunchParameters { 45struct LaunchParameters {
@@ -203,8 +215,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
203ISelfController::~ISelfController() = default; 215ISelfController::~ISelfController() = default;
204 216
205void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { 217void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
206 // Takes 3 input u8s with each field located immediately after the previous u8, these are 218 // Takes 3 input u8s with each field located immediately after the previous
207 // bool flags. No output. 219 // u8, these are bool flags. No output.
208 220
209 IPC::RequestParser rp{ctx}; 221 IPC::RequestParser rp{ctx};
210 222
@@ -258,8 +270,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
258} 270}
259 271
260void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { 272void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
261 // Takes 3 input u8s with each field located immediately after the previous u8, these are 273 // Takes 3 input u8s with each field located immediately after the previous
262 // bool flags. No output. 274 // u8, these are bool flags. No output.
263 IPC::RequestParser rp{ctx}; 275 IPC::RequestParser rp{ctx};
264 276
265 bool enabled = rp.Pop<bool>(); 277 bool enabled = rp.Pop<bool>();
@@ -302,8 +314,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
302} 314}
303 315
304void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { 316void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
305 // TODO(Subv): Find out how AM determines the display to use, for now just create the layer 317 // TODO(Subv): Find out how AM determines the display to use, for now just
306 // in the Default display. 318 // create the layer in the Default display.
307 u64 display_id = nvflinger->OpenDisplay("Default"); 319 u64 display_id = nvflinger->OpenDisplay("Default");
308 u64 layer_id = nvflinger->CreateLayer(display_id); 320 u64 layer_id = nvflinger->CreateLayer(display_id);
309 321
@@ -338,7 +350,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
338 LOG_WARNING(Service_AM, "(STUBBED) called"); 350 LOG_WARNING(Service_AM, "(STUBBED) called");
339} 351}
340 352
341ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 353AppletMessageQueue::AppletMessageQueue() {
354 auto& kernel = Core::System::GetInstance().Kernel();
355 on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
356 "AMMessageQueue:OnMessageRecieved");
357 on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
358 "AMMessageQueue:OperationModeChanged");
359}
360
361AppletMessageQueue::~AppletMessageQueue() = default;
362
363const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
364 return on_new_message;
365}
366
367const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
368 return on_operation_mode_changed;
369}
370
371void AppletMessageQueue::PushMessage(AppletMessage msg) {
372 messages.push(msg);
373 on_new_message->Signal();
374}
375
376AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
377 if (messages.empty()) {
378 on_new_message->Clear();
379 return AppletMessage::NoMessage;
380 }
381 auto msg = messages.front();
382 messages.pop();
383 if (messages.empty()) {
384 on_new_message->Clear();
385 }
386 return msg;
387}
388
389std::size_t AppletMessageQueue::GetMessageCount() const {
390 return messages.size();
391}
392
393void AppletMessageQueue::OperationModeChanged() {
394 PushMessage(AppletMessage::OperationModeChanged);
395 PushMessage(AppletMessage::PerformanceModeChanged);
396 on_operation_mode_changed->Signal();
397}
398
399ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
400 : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
342 // clang-format off 401 // clang-format off
343 static const FunctionInfo functions[] = { 402 static const FunctionInfo functions[] = {
344 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 403 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -388,21 +447,19 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
388} 447}
389 448
390void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { 449void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
391 event->Signal();
392
393 IPC::ResponseBuilder rb{ctx, 2, 1}; 450 IPC::ResponseBuilder rb{ctx, 2, 1};
394 rb.Push(RESULT_SUCCESS); 451 rb.Push(RESULT_SUCCESS);
395 rb.PushCopyObjects(event); 452 rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
396 453
397 LOG_WARNING(Service_AM, "(STUBBED) called"); 454 LOG_DEBUG(Service_AM, "called");
398} 455}
399 456
400void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 457void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
401 IPC::ResponseBuilder rb{ctx, 3}; 458 IPC::ResponseBuilder rb{ctx, 3};
402 rb.Push(RESULT_SUCCESS); 459 rb.Push(RESULT_SUCCESS);
403 rb.Push<u32>(15); 460 rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
404 461
405 LOG_WARNING(Service_AM, "(STUBBED) called"); 462 LOG_DEBUG(Service_AM, "called");
406} 463}
407 464
408void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { 465void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -414,13 +471,11 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
414} 471}
415 472
416void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { 473void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
417 event->Signal();
418
419 IPC::ResponseBuilder rb{ctx, 2, 1}; 474 IPC::ResponseBuilder rb{ctx, 2, 1};
420 rb.Push(RESULT_SUCCESS); 475 rb.Push(RESULT_SUCCESS);
421 rb.PushCopyObjects(event); 476 rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
422 477
423 LOG_WARNING(Service_AM, "(STUBBED) called"); 478 LOG_DEBUG(Service_AM, "called");
424} 479}
425 480
426void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { 481void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
@@ -428,23 +483,45 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
428 rb.Push(RESULT_SUCCESS); 483 rb.Push(RESULT_SUCCESS);
429 484
430 if (Settings::values.use_docked_mode) { 485 if (Settings::values.use_docked_mode) {
431 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 486 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
432 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 487 static_cast<u32>(Settings::values.resolution_factor));
488 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
489 static_cast<u32>(Settings::values.resolution_factor));
433 } else { 490 } else {
434 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 491 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
435 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); 492 static_cast<u32>(Settings::values.resolution_factor));
493 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
494 static_cast<u32>(Settings::values.resolution_factor));
436 } 495 }
437 496
438 LOG_DEBUG(Service_AM, "called"); 497 LOG_DEBUG(Service_AM, "called");
439} 498}
440 499
500IStorage::IStorage(std::vector<u8> buffer)
501 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
502 // clang-format off
503 static const FunctionInfo functions[] = {
504 {0, &IStorage::Open, "Open"},
505 {1, nullptr, "OpenTransferStorage"},
506 };
507 // clang-format on
508
509 RegisterHandlers(functions);
510}
511
512IStorage::~IStorage() = default;
513
514const std::vector<u8>& IStorage::GetData() const {
515 return buffer;
516}
517
441void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 518void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
442 const bool use_docked_mode{Settings::values.use_docked_mode}; 519 const bool use_docked_mode{Settings::values.use_docked_mode};
443 IPC::ResponseBuilder rb{ctx, 3}; 520 IPC::ResponseBuilder rb{ctx, 3};
444 rb.Push(RESULT_SUCCESS); 521 rb.Push(RESULT_SUCCESS);
445 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); 522 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
446 523
447 LOG_WARNING(Service_AM, "(STUBBED) called"); 524 LOG_DEBUG(Service_AM, "called");
448} 525}
449 526
450void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { 527void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
@@ -454,18 +531,33 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
454 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked 531 rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
455 : APM::PerformanceMode::Handheld)); 532 : APM::PerformanceMode::Handheld));
456 533
457 LOG_WARNING(Service_AM, "(STUBBED) called"); 534 LOG_DEBUG(Service_AM, "called");
458} 535}
459 536
460class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { 537class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
461public: 538public:
462 explicit IStorageAccessor(std::vector<u8> buffer) 539 explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
463 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 540 : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
464 // clang-format off 541 // clang-format off
465 static const FunctionInfo functions[] = { 542 static const FunctionInfo functions[] = {
466 {0, &IStorageAccessor::GetSize, "GetSize"}, 543 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
467 {10, &IStorageAccessor::Write, "Write"}, 544 {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
468 {11, &IStorageAccessor::Read, "Read"}, 545 {10, &ILibraryAppletAccessor::Start, "Start"},
546 {20, nullptr, "RequestExit"},
547 {25, nullptr, "Terminate"},
548 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
549 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
550 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
551 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
552 {102, nullptr, "PushExtraStorage"},
553 {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
554 {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
555 {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
556 {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
557 {110, nullptr, "NeedsToExitProcess"},
558 {120, nullptr, "GetLibraryAppletInfo"},
559 {150, nullptr, "RequestForAppletToGetForeground"},
560 {160, nullptr, "GetIndirectLayerConsumerHandle"},
469 }; 561 };
470 // clang-format on 562 // clang-format on
471 563
@@ -473,158 +565,187 @@ public:
473 } 565 }
474 566
475private: 567private:
476 std::vector<u8> buffer; 568 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
569 const auto event = applet->GetBroker().GetStateChangedEvent();
570 event->Signal();
477 571
478 void GetSize(Kernel::HLERequestContext& ctx) { 572 IPC::ResponseBuilder rb{ctx, 2, 1};
479 IPC::ResponseBuilder rb{ctx, 4}; 573 rb.Push(RESULT_SUCCESS);
574 rb.PushCopyObjects(event);
480 575
576 LOG_DEBUG(Service_AM, "called");
577 }
578
579 void IsCompleted(Kernel::HLERequestContext& ctx) {
580 IPC::ResponseBuilder rb{ctx, 3};
481 rb.Push(RESULT_SUCCESS); 581 rb.Push(RESULT_SUCCESS);
482 rb.Push(static_cast<u64>(buffer.size())); 582 rb.Push<u32>(applet->TransactionComplete());
483 583
484 LOG_DEBUG(Service_AM, "called"); 584 LOG_DEBUG(Service_AM, "called");
485 } 585 }
486 586
487 void Write(Kernel::HLERequestContext& ctx) { 587 void GetResult(Kernel::HLERequestContext& ctx) {
488 IPC::RequestParser rp{ctx}; 588 IPC::ResponseBuilder rb{ctx, 2};
589 rb.Push(applet->GetStatus());
489 590
490 const u64 offset{rp.Pop<u64>()}; 591 LOG_DEBUG(Service_AM, "called");
491 const std::vector<u8> data{ctx.ReadBuffer()}; 592 }
492 593
493 ASSERT(offset + data.size() <= buffer.size()); 594 void Start(Kernel::HLERequestContext& ctx) {
595 ASSERT(applet != nullptr);
494 596
495 std::memcpy(&buffer[offset], data.data(), data.size()); 597 applet->Initialize();
598 applet->Execute();
496 599
497 IPC::ResponseBuilder rb{ctx, 2}; 600 IPC::ResponseBuilder rb{ctx, 2};
498 rb.Push(RESULT_SUCCESS); 601 rb.Push(RESULT_SUCCESS);
499 602
500 LOG_DEBUG(Service_AM, "called, offset={}", offset); 603 LOG_DEBUG(Service_AM, "called");
501 } 604 }
502 605
503 void Read(Kernel::HLERequestContext& ctx) { 606 void PushInData(Kernel::HLERequestContext& ctx) {
504 IPC::RequestParser rp{ctx}; 607 IPC::RequestParser rp{ctx};
608 applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
505 609
506 const u64 offset{rp.Pop<u64>()}; 610 IPC::ResponseBuilder rb{ctx, 2};
507 const std::size_t size{ctx.GetWriteBufferSize()}; 611 rb.Push(RESULT_SUCCESS);
508 612
509 ASSERT(offset + size <= buffer.size()); 613 LOG_DEBUG(Service_AM, "called");
614 }
510 615
511 ctx.WriteBuffer(buffer.data() + offset, size); 616 void PopOutData(Kernel::HLERequestContext& ctx) {
617 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
618
619 const auto storage = applet->GetBroker().PopNormalDataToGame();
620 if (storage == nullptr) {
621 rb.Push(ERR_NO_DATA_IN_CHANNEL);
622 return;
623 }
624
625 rb.Push(RESULT_SUCCESS);
626 rb.PushIpcInterface<IStorage>(std::move(*storage));
627
628 LOG_DEBUG(Service_AM, "called");
629 }
630
631 void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
632 IPC::RequestParser rp{ctx};
633 applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
634
635 ASSERT(applet->IsInitialized());
636 applet->ExecuteInteractive();
637 applet->Execute();
512 638
513 IPC::ResponseBuilder rb{ctx, 2}; 639 IPC::ResponseBuilder rb{ctx, 2};
514 rb.Push(RESULT_SUCCESS); 640 rb.Push(RESULT_SUCCESS);
515 641
516 LOG_DEBUG(Service_AM, "called, offset={}", offset); 642 LOG_DEBUG(Service_AM, "called");
517 } 643 }
518};
519 644
520class IStorage final : public ServiceFramework<IStorage> { 645 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
521public: 646 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
522 explicit IStorage(std::vector<u8> buffer)
523 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
524 // clang-format off
525 static const FunctionInfo functions[] = {
526 {0, &IStorage::Open, "Open"},
527 {1, nullptr, "OpenTransferStorage"},
528 };
529 // clang-format on
530 647
531 RegisterHandlers(functions); 648 const auto storage = applet->GetBroker().PopInteractiveDataToGame();
649 if (storage == nullptr) {
650 rb.Push(ERR_NO_DATA_IN_CHANNEL);
651 return;
652 }
653
654 rb.Push(RESULT_SUCCESS);
655 rb.PushIpcInterface<IStorage>(std::move(*storage));
656
657 LOG_DEBUG(Service_AM, "called");
532 } 658 }
533 659
534private: 660 void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
535 std::vector<u8> buffer; 661 IPC::ResponseBuilder rb{ctx, 2, 1};
662 rb.Push(RESULT_SUCCESS);
663 rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
536 664
537 void Open(Kernel::HLERequestContext& ctx) { 665 LOG_DEBUG(Service_AM, "called");
538 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 666 }
539 667
668 void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
669 IPC::ResponseBuilder rb{ctx, 2, 1};
540 rb.Push(RESULT_SUCCESS); 670 rb.Push(RESULT_SUCCESS);
541 rb.PushIpcInterface<AM::IStorageAccessor>(buffer); 671 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
542 672
543 LOG_DEBUG(Service_AM, "called"); 673 LOG_DEBUG(Service_AM, "called");
544 } 674 }
675
676 std::shared_ptr<Applets::Applet> applet;
545}; 677};
546 678
547class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 679void IStorage::Open(Kernel::HLERequestContext& ctx) {
548public: 680 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
549 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 681
550 // clang-format off 682 rb.Push(RESULT_SUCCESS);
683 rb.PushIpcInterface<IStorageAccessor>(*this);
684
685 LOG_DEBUG(Service_AM, "called");
686}
687
688IStorageAccessor::IStorageAccessor(IStorage& storage)
689 : ServiceFramework("IStorageAccessor"), backing(storage) {
690 // clang-format off
551 static const FunctionInfo functions[] = { 691 static const FunctionInfo functions[] = {
552 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 692 {0, &IStorageAccessor::GetSize, "GetSize"},
553 {1, nullptr, "IsCompleted"}, 693 {10, &IStorageAccessor::Write, "Write"},
554 {10, &ILibraryAppletAccessor::Start, "Start"}, 694 {11, &IStorageAccessor::Read, "Read"},
555 {20, nullptr, "RequestExit"},
556 {25, nullptr, "Terminate"},
557 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
558 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
559 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
560 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
561 {102, nullptr, "PushExtraStorage"},
562 {103, nullptr, "PushInteractiveInData"},
563 {104, nullptr, "PopInteractiveOutData"},
564 {105, nullptr, "GetPopOutDataEvent"},
565 {106, nullptr, "GetPopInteractiveOutDataEvent"},
566 {110, nullptr, "NeedsToExitProcess"},
567 {120, nullptr, "GetLibraryAppletInfo"},
568 {150, nullptr, "RequestForAppletToGetForeground"},
569 {160, nullptr, "GetIndirectLayerConsumerHandle"},
570 }; 695 };
571 // clang-format on 696 // clang-format on
572 697
573 RegisterHandlers(functions); 698 RegisterHandlers(functions);
699}
574 700
575 auto& kernel = Core::System::GetInstance().Kernel(); 701IStorageAccessor::~IStorageAccessor() = default;
576 state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
577 "ILibraryAppletAccessor:StateChangedEvent");
578 }
579 702
580private: 703void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
581 void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { 704 IPC::ResponseBuilder rb{ctx, 4};
582 state_changed_event->Signal();
583 705
584 IPC::ResponseBuilder rb{ctx, 2, 1}; 706 rb.Push(RESULT_SUCCESS);
585 rb.Push(RESULT_SUCCESS); 707 rb.Push(static_cast<u64>(backing.buffer.size()));
586 rb.PushCopyObjects(state_changed_event);
587 708
588 LOG_WARNING(Service_AM, "(STUBBED) called"); 709 LOG_DEBUG(Service_AM, "called");
589 } 710}
590 711
591 void GetResult(Kernel::HLERequestContext& ctx) { 712void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
592 IPC::ResponseBuilder rb{ctx, 2}; 713 IPC::RequestParser rp{ctx};
593 rb.Push(RESULT_SUCCESS);
594 714
595 LOG_WARNING(Service_AM, "(STUBBED) called"); 715 const u64 offset{rp.Pop<u64>()};
596 } 716 const std::vector<u8> data{ctx.ReadBuffer()};
597 717
598 void Start(Kernel::HLERequestContext& ctx) { 718 if (data.size() > backing.buffer.size() - offset) {
599 IPC::ResponseBuilder rb{ctx, 2}; 719 IPC::ResponseBuilder rb{ctx, 2};
600 rb.Push(RESULT_SUCCESS); 720 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
601
602 LOG_WARNING(Service_AM, "(STUBBED) called");
603 } 721 }
604 722
605 void PushInData(Kernel::HLERequestContext& ctx) { 723 std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
606 IPC::RequestParser rp{ctx};
607 storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
608 724
609 IPC::ResponseBuilder rb{ctx, 2}; 725 IPC::ResponseBuilder rb{ctx, 2};
610 rb.Push(RESULT_SUCCESS); 726 rb.Push(RESULT_SUCCESS);
611 727
612 LOG_DEBUG(Service_AM, "called"); 728 LOG_DEBUG(Service_AM, "called, offset={}", offset);
613 } 729}
614 730
615 void PopOutData(Kernel::HLERequestContext& ctx) { 731void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
616 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 732 IPC::RequestParser rp{ctx};
617 rb.Push(RESULT_SUCCESS);
618 rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
619 733
620 storage_stack.pop(); 734 const u64 offset{rp.Pop<u64>()};
735 const std::size_t size{ctx.GetWriteBufferSize()};
621 736
622 LOG_DEBUG(Service_AM, "called"); 737 if (size > backing.buffer.size() - offset) {
738 IPC::ResponseBuilder rb{ctx, 2};
739 rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
623 } 740 }
624 741
625 std::stack<std::shared_ptr<AM::IStorage>> storage_stack; 742 ctx.WriteBuffer(backing.buffer.data() + offset, size);
626 Kernel::SharedPtr<Kernel::Event> state_changed_event; 743
627}; 744 IPC::ResponseBuilder rb{ctx, 2};
745 rb.Push(RESULT_SUCCESS);
746
747 LOG_DEBUG(Service_AM, "called, offset={}", offset);
748}
628 749
629ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { 750ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
630 static const FunctionInfo functions[] = { 751 static const FunctionInfo functions[] = {
@@ -632,7 +753,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
632 {1, nullptr, "TerminateAllLibraryApplets"}, 753 {1, nullptr, "TerminateAllLibraryApplets"},
633 {2, nullptr, "AreAnyLibraryAppletsLeft"}, 754 {2, nullptr, "AreAnyLibraryAppletsLeft"},
634 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, 755 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
635 {11, nullptr, "CreateTransferMemoryStorage"}, 756 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
636 {12, nullptr, "CreateHandleStorage"}, 757 {12, nullptr, "CreateHandleStorage"},
637 }; 758 };
638 RegisterHandlers(functions); 759 RegisterHandlers(functions);
@@ -640,11 +761,37 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
640 761
641ILibraryAppletCreator::~ILibraryAppletCreator() = default; 762ILibraryAppletCreator::~ILibraryAppletCreator() = default;
642 763
764static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
765 switch (id) {
766 case AppletId::SoftwareKeyboard:
767 return std::make_shared<Applets::SoftwareKeyboard>();
768 default:
769 LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
770 static_cast<u32>(id));
771 return std::make_shared<Applets::StubApplet>();
772 }
773}
774
643void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 775void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
776 IPC::RequestParser rp{ctx};
777 const auto applet_id = rp.PopRaw<AppletId>();
778 const auto applet_mode = rp.PopRaw<u32>();
779
780 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
781 static_cast<u32>(applet_id), applet_mode);
782
783 const auto applet = GetAppletFromId(applet_id);
784
785 if (applet == nullptr) {
786 IPC::ResponseBuilder rb{ctx, 2};
787 rb.Push(ResultCode(-1));
788 return;
789 }
790
644 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 791 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
645 792
646 rb.Push(RESULT_SUCCESS); 793 rb.Push(RESULT_SUCCESS);
647 rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); 794 rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
648 795
649 LOG_DEBUG(Service_AM, "called"); 796 LOG_DEBUG(Service_AM, "called");
650} 797}
@@ -661,6 +808,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
661 LOG_DEBUG(Service_AM, "called, size={}", size); 808 LOG_DEBUG(Service_AM, "called, size={}", size);
662} 809}
663 810
811void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
812 IPC::RequestParser rp{ctx};
813
814 rp.SetCurrentOffset(3);
815 const auto handle{rp.Pop<Kernel::Handle>()};
816
817 const auto shared_mem =
818 Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
819 handle);
820
821 if (shared_mem == nullptr) {
822 IPC::ResponseBuilder rb{ctx, 2};
823 rb.Push(ResultCode(-1));
824 return;
825 }
826
827 const u8* mem_begin = shared_mem->GetPointer();
828 const u8* mem_end = mem_begin + shared_mem->GetSize();
829 std::vector<u8> memory{mem_begin, mem_end};
830
831 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
832 rb.Push(RESULT_SUCCESS);
833 rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
834}
835
664IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 836IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
665 // clang-format off 837 // clang-format off
666 static const FunctionInfo functions[] = { 838 static const FunctionInfo functions[] = {
@@ -690,7 +862,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
690 {70, nullptr, "RequestToShutdown"}, 862 {70, nullptr, "RequestToShutdown"},
691 {71, nullptr, "RequestToReboot"}, 863 {71, nullptr, "RequestToReboot"},
692 {80, nullptr, "ExitAndRequestToShowThanksMessage"}, 864 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
693 {90, nullptr, "EnableApplicationCrashReport"}, 865 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
694 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"}, 866 {100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
695 {101, nullptr, "SetApplicationCopyrightImage"}, 867 {101, nullptr, "SetApplicationCopyrightImage"},
696 {102, nullptr, "SetApplicationCopyrightVisibility"}, 868 {102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -709,6 +881,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
709 881
710IApplicationFunctions::~IApplicationFunctions() = default; 882IApplicationFunctions::~IApplicationFunctions() = default;
711 883
884void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
885 IPC::ResponseBuilder rb{ctx, 2};
886 rb.Push(RESULT_SUCCESS);
887 LOG_WARNING(Service_AM, "(STUBBED) called");
888}
889
712void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed( 890void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
713 Kernel::HLERequestContext& ctx) { 891 Kernel::HLERequestContext& ctx) {
714 IPC::ResponseBuilder rb{ctx, 2}; 892 IPC::ResponseBuilder rb{ctx, 2};
@@ -778,7 +956,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
778 956
779void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 957void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
780 // Takes an input u32 Result, no output. 958 // Takes an input u32 Result, no output.
781 // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. 959 // For example, in some cases official apps use this with error 0x2A2 then
960 // uses svcBreak.
782 961
783 IPC::RequestParser rp{ctx}; 962 IPC::RequestParser rp{ctx};
784 u32 result = rp.Pop<u32>(); 963 u32 result = rp.Pop<u32>();
@@ -840,8 +1019,12 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
840 1019
841void InstallInterfaces(SM::ServiceManager& service_manager, 1020void InstallInterfaces(SM::ServiceManager& service_manager,
842 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 1021 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
843 std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); 1022 auto message_queue = std::make_shared<AppletMessageQueue>();
844 std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); 1023 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
1024 // game boot
1025
1026 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
1027 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
845 std::make_shared<IdleSys>()->InstallAsService(service_manager); 1028 std::make_shared<IdleSys>()->InstallAsService(service_manager);
846 std::make_shared<OMM>()->InstallAsService(service_manager); 1029 std::make_shared<OMM>()->InstallAsService(service_manager);
847 std::make_shared<SPSM>()->InstallAsService(service_manager); 1030 std::make_shared<SPSM>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 095f94851..44c1bcde5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <queue>
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
10namespace Kernel { 11namespace Kernel {
@@ -39,6 +40,31 @@ enum SystemLanguage {
39 TraditionalChinese = 16, 40 TraditionalChinese = 16,
40}; 41};
41 42
43class AppletMessageQueue {
44public:
45 enum class AppletMessage : u32 {
46 NoMessage = 0,
47 FocusStateChanged = 15,
48 OperationModeChanged = 30,
49 PerformanceModeChanged = 31,
50 };
51
52 AppletMessageQueue();
53 ~AppletMessageQueue();
54
55 const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
56 const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
57 void PushMessage(AppletMessage msg);
58 AppletMessage PopMessage();
59 std::size_t GetMessageCount() const;
60 void OperationModeChanged();
61
62private:
63 std::queue<AppletMessage> messages;
64 Kernel::SharedPtr<Kernel::Event> on_new_message;
65 Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
66};
67
42class IWindowController final : public ServiceFramework<IWindowController> { 68class IWindowController final : public ServiceFramework<IWindowController> {
43public: 69public:
44 IWindowController(); 70 IWindowController();
@@ -102,7 +128,7 @@ private:
102 128
103class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 129class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
104public: 130public:
105 ICommonStateGetter(); 131 explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
106 ~ICommonStateGetter() override; 132 ~ICommonStateGetter() override;
107 133
108private: 134private:
@@ -126,6 +152,35 @@ private:
126 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); 152 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
127 153
128 Kernel::SharedPtr<Kernel::Event> event; 154 Kernel::SharedPtr<Kernel::Event> event;
155 std::shared_ptr<AppletMessageQueue> msg_queue;
156};
157
158class IStorage final : public ServiceFramework<IStorage> {
159public:
160 explicit IStorage(std::vector<u8> buffer);
161 ~IStorage() override;
162
163 const std::vector<u8>& GetData() const;
164
165private:
166 void Open(Kernel::HLERequestContext& ctx);
167
168 std::vector<u8> buffer;
169
170 friend class IStorageAccessor;
171};
172
173class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
174public:
175 explicit IStorageAccessor(IStorage& backing);
176 ~IStorageAccessor() override;
177
178private:
179 void GetSize(Kernel::HLERequestContext& ctx);
180 void Write(Kernel::HLERequestContext& ctx);
181 void Read(Kernel::HLERequestContext& ctx);
182
183 IStorage& backing;
129}; 184};
130 185
131class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 186class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
@@ -136,6 +191,7 @@ public:
136private: 191private:
137 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 192 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
138 void CreateStorage(Kernel::HLERequestContext& ctx); 193 void CreateStorage(Kernel::HLERequestContext& ctx);
194 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
139}; 195};
140 196
141class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 197class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -158,6 +214,7 @@ private:
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx); 214 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); 215 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); 216 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
217 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
161}; 218};
162 219
163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 220class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 68ea778e8..ec93e3529 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
12 12
13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
14public: 14public:
15 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) {
17 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
18 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 20 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
19 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, 21 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -32,7 +34,7 @@ private:
32 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 34 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
33 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 35 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
34 rb.Push(RESULT_SUCCESS); 36 rb.Push(RESULT_SUCCESS);
35 rb.PushIpcInterface<ICommonStateGetter>(); 37 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
36 LOG_DEBUG(Service_AM, "called"); 38 LOG_DEBUG(Service_AM, "called");
37 } 39 }
38 40
@@ -93,12 +95,15 @@ private:
93 } 95 }
94 96
95 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 97 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
98 std::shared_ptr<AppletMessageQueue> msg_queue;
96}; 99};
97 100
98class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 101class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
99public: 102public:
100 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 103 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
101 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) { 104 std::shared_ptr<AppletMessageQueue> msg_queue)
105 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
106 msg_queue(std::move(msg_queue)) {
102 static const FunctionInfo functions[] = { 107 static const FunctionInfo functions[] = {
103 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 108 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
104 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, 109 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -119,7 +124,7 @@ private:
119 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 124 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
120 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 125 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
121 rb.Push(RESULT_SUCCESS); 126 rb.Push(RESULT_SUCCESS);
122 rb.PushIpcInterface<ICommonStateGetter>(); 127 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
123 LOG_DEBUG(Service_AM, "called"); 128 LOG_DEBUG(Service_AM, "called");
124 } 129 }
125 130
@@ -186,31 +191,34 @@ private:
186 LOG_DEBUG(Service_AM, "called"); 191 LOG_DEBUG(Service_AM, "called");
187 } 192 }
188 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 193 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
194 std::shared_ptr<AppletMessageQueue> msg_queue;
189}; 195};
190 196
191void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { 197void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
192 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 198 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
193 rb.Push(RESULT_SUCCESS); 199 rb.Push(RESULT_SUCCESS);
194 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger); 200 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
195 LOG_DEBUG(Service_AM, "called"); 201 LOG_DEBUG(Service_AM, "called");
196} 202}
197 203
198void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { 204void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 205 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(RESULT_SUCCESS); 206 rb.Push(RESULT_SUCCESS);
201 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 207 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
202 LOG_DEBUG(Service_AM, "called"); 208 LOG_DEBUG(Service_AM, "called");
203} 209}
204 210
205void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { 211void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
206 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 212 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
207 rb.Push(RESULT_SUCCESS); 213 rb.Push(RESULT_SUCCESS);
208 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); 214 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
209 LOG_DEBUG(Service_AM, "called"); 215 LOG_DEBUG(Service_AM, "called");
210} 216}
211 217
212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 218AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { 219 std::shared_ptr<AppletMessageQueue> msg_queue)
220 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
221 msg_queue(std::move(msg_queue)) {
214 // clang-format off 222 // clang-format off
215 static const FunctionInfo functions[] = { 223 static const FunctionInfo functions[] = {
216 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 224 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@@ -228,4 +236,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
228 236
229AppletAE::~AppletAE() = default; 237AppletAE::~AppletAE() = default;
230 238
239const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
240 return msg_queue;
241}
242
231} // namespace Service::AM 243} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 1ed77baa4..902db2665 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -17,15 +17,19 @@ namespace AM {
17 17
18class AppletAE final : public ServiceFramework<AppletAE> { 18class AppletAE final : public ServiceFramework<AppletAE> {
19public: 19public:
20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue);
21 ~AppletAE() override; 22 ~AppletAE() override;
22 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
25
23private: 26private:
24 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx); 27 void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
25 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx); 28 void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
26 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); 29 void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
27 30
28 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 31 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
32 std::shared_ptr<AppletMessageQueue> msg_queue;
29}; 33};
30 34
31} // namespace AM 35} // namespace AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index 60717afd9..20c8d5fff 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
12 12
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { 16 std::shared_ptr<AppletMessageQueue> msg_queue)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) {
17 // clang-format off 19 // clang-format off
18 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
19 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 21 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -70,7 +72,7 @@ private:
70 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { 72 void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
71 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 73 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
72 rb.Push(RESULT_SUCCESS); 74 rb.Push(RESULT_SUCCESS);
73 rb.PushIpcInterface<ICommonStateGetter>(); 75 rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
74 LOG_DEBUG(Service_AM, "called"); 76 LOG_DEBUG(Service_AM, "called");
75 } 77 }
76 78
@@ -89,17 +91,20 @@ private:
89 } 91 }
90 92
91 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 93 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
94 std::shared_ptr<AppletMessageQueue> msg_queue;
92}; 95};
93 96
94void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { 97void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
95 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 98 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
96 rb.Push(RESULT_SUCCESS); 99 rb.Push(RESULT_SUCCESS);
97 rb.PushIpcInterface<IApplicationProxy>(nvflinger); 100 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
98 LOG_DEBUG(Service_AM, "called"); 101 LOG_DEBUG(Service_AM, "called");
99} 102}
100 103
101AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 104AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
102 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { 105 std::shared_ptr<AppletMessageQueue> msg_queue)
106 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
107 msg_queue(std::move(msg_queue)) {
103 static const FunctionInfo functions[] = { 108 static const FunctionInfo functions[] = {
104 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 109 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
105 }; 110 };
@@ -108,4 +113,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
108 113
109AppletOE::~AppletOE() = default; 114AppletOE::~AppletOE() = default;
110 115
116const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
117 return msg_queue;
118}
119
111} // namespace Service::AM 120} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 60cfdfd9d..bbd0108ef 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -17,13 +17,17 @@ namespace AM {
17 17
18class AppletOE final : public ServiceFramework<AppletOE> { 18class AppletOE final : public ServiceFramework<AppletOE> {
19public: 19public:
20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue);
21 ~AppletOE() override; 22 ~AppletOE() override;
22 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
25
23private: 26private:
24 void OpenApplicationProxy(Kernel::HLERequestContext& ctx); 27 void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
25 28
26 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 29 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
30 std::shared_ptr<AppletMessageQueue> msg_queue;
27}; 31};
28 32
29} // namespace AM 33} // namespace AM
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
new file mode 100644
index 000000000..becbadd06
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -0,0 +1,113 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/server_port.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/applets.h"
12
13namespace Service::AM::Applets {
14
15AppletDataBroker::AppletDataBroker() {
16 auto& kernel = Core::System::GetInstance().Kernel();
17 state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
18 "ILibraryAppletAccessor:StateChangedEvent");
19 pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
20 "ILibraryAppletAccessor:PopDataOutEvent");
21 pop_interactive_out_data_event = Kernel::Event::Create(
22 kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
23}
24
25AppletDataBroker::~AppletDataBroker() = default;
26
27std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
28 if (out_channel.empty())
29 return nullptr;
30
31 auto out = std::move(out_channel.front());
32 out_channel.pop();
33 return out;
34}
35
36std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
37 if (in_channel.empty())
38 return nullptr;
39
40 auto out = std::move(in_channel.front());
41 in_channel.pop();
42 return out;
43}
44
45std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
46 if (out_interactive_channel.empty())
47 return nullptr;
48
49 auto out = std::move(out_interactive_channel.front());
50 out_interactive_channel.pop();
51 return out;
52}
53
54std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
55 if (in_interactive_channel.empty())
56 return nullptr;
57
58 auto out = std::move(in_interactive_channel.front());
59 in_interactive_channel.pop();
60 return out;
61}
62
63void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
64 in_channel.push(std::make_unique<IStorage>(storage));
65}
66
67void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
68 out_channel.push(std::make_unique<IStorage>(storage));
69 pop_out_data_event->Signal();
70}
71
72void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
73 in_interactive_channel.push(std::make_unique<IStorage>(storage));
74}
75
76void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
77 out_interactive_channel.push(std::make_unique<IStorage>(storage));
78 pop_interactive_out_data_event->Signal();
79}
80
81void AppletDataBroker::SignalStateChanged() const {
82 state_changed_event->Signal();
83}
84
85Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
86 return pop_out_data_event;
87}
88
89Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
90 return pop_interactive_out_data_event;
91}
92
93Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
94 return state_changed_event;
95}
96
97Applet::Applet() = default;
98
99Applet::~Applet() = default;
100
101void Applet::Initialize() {
102 const auto common = broker.PopNormalDataToApplet();
103 ASSERT(common != nullptr);
104
105 const auto common_data = common->GetData();
106
107 ASSERT(common_data.size() >= sizeof(CommonArguments));
108 std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
109
110 initialized = true;
111}
112
113} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
new file mode 100644
index 000000000..f65ea119c
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.h
@@ -0,0 +1,112 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <queue>
9#include "common/swap.h"
10#include "core/hle/kernel/kernel.h"
11
12union ResultCode;
13
14namespace Kernel {
15class Event;
16}
17
18namespace Service::AM {
19
20class IStorage;
21
22namespace Applets {
23
24class AppletDataBroker final {
25public:
26 AppletDataBroker();
27 ~AppletDataBroker();
28
29 std::unique_ptr<IStorage> PopNormalDataToGame();
30 std::unique_ptr<IStorage> PopNormalDataToApplet();
31
32 std::unique_ptr<IStorage> PopInteractiveDataToGame();
33 std::unique_ptr<IStorage> PopInteractiveDataToApplet();
34
35 void PushNormalDataFromGame(IStorage storage);
36 void PushNormalDataFromApplet(IStorage storage);
37
38 void PushInteractiveDataFromGame(IStorage storage);
39 void PushInteractiveDataFromApplet(IStorage storage);
40
41 void SignalStateChanged() const;
42
43 Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
44 Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
45 Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
46
47private:
48 // Queues are named from applet's perspective
49
50 // PopNormalDataToApplet and PushNormalDataFromGame
51 std::queue<std::unique_ptr<IStorage>> in_channel;
52
53 // PopNormalDataToGame and PushNormalDataFromApplet
54 std::queue<std::unique_ptr<IStorage>> out_channel;
55
56 // PopInteractiveDataToApplet and PushInteractiveDataFromGame
57 std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
58
59 // PopInteractiveDataToGame and PushInteractiveDataFromApplet
60 std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
61
62 Kernel::SharedPtr<Kernel::Event> state_changed_event;
63
64 // Signaled on PushNormalDataFromApplet
65 Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
66
67 // Signaled on PushInteractiveDataFromApplet
68 Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
69};
70
71class Applet {
72public:
73 Applet();
74 virtual ~Applet();
75
76 virtual void Initialize();
77
78 virtual bool TransactionComplete() const = 0;
79 virtual ResultCode GetStatus() const = 0;
80 virtual void ExecuteInteractive() = 0;
81 virtual void Execute() = 0;
82
83 bool IsInitialized() const {
84 return initialized;
85 }
86
87 AppletDataBroker& GetBroker() {
88 return broker;
89 }
90
91 const AppletDataBroker& GetBroker() const {
92 return broker;
93 }
94
95protected:
96 struct CommonArguments {
97 u32_le arguments_version;
98 u32_le size;
99 u32_le library_version;
100 u32_le theme_color;
101 u8 play_startup_sound;
102 u64_le system_tick;
103 };
104 static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
105
106 CommonArguments common_args{};
107 AppletDataBroker broker;
108 bool initialized = false;
109};
110
111} // namespace Applets
112} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
new file mode 100644
index 000000000..981bdec51
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -0,0 +1,161 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/assert.h"
7#include "common/string_util.h"
8#include "core/core.h"
9#include "core/frontend/applets/software_keyboard.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/software_keyboard.h"
12
13namespace Service::AM::Applets {
14
15constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
16constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
17constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
18constexpr bool INTERACTIVE_STATUS_OK = false;
19
20static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
21 KeyboardConfig config, std::u16string initial_text) {
22 Core::Frontend::SoftwareKeyboardParameters params{};
23
24 params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
25 config.submit_text.data(), config.submit_text.size());
26 params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
27 config.header_text.data(), config.header_text.size());
28 params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
29 config.sub_text.size());
30 params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
31 config.guide_text.size());
32 params.initial_text = initial_text;
33 params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
34 params.password = static_cast<bool>(config.is_password);
35 params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
36 params.value = static_cast<u8>(config.keyset_disable_bitmask);
37
38 return params;
39}
40
41SoftwareKeyboard::SoftwareKeyboard() = default;
42
43SoftwareKeyboard::~SoftwareKeyboard() = default;
44
45void SoftwareKeyboard::Initialize() {
46 complete = false;
47 initial_text.clear();
48 final_data.clear();
49
50 Applet::Initialize();
51
52 const auto keyboard_config_storage = broker.PopNormalDataToApplet();
53 ASSERT(keyboard_config_storage != nullptr);
54 const auto& keyboard_config = keyboard_config_storage->GetData();
55
56 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
57 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
58
59 const auto work_buffer_storage = broker.PopNormalDataToApplet();
60 ASSERT(work_buffer_storage != nullptr);
61 const auto& work_buffer = work_buffer_storage->GetData();
62
63 if (config.initial_string_size == 0)
64 return;
65
66 std::vector<char16_t> string(config.initial_string_size);
67 std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
68 string.size() * 2);
69 initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
70}
71
72bool SoftwareKeyboard::TransactionComplete() const {
73 return complete;
74}
75
76ResultCode SoftwareKeyboard::GetStatus() const {
77 return RESULT_SUCCESS;
78}
79
80void SoftwareKeyboard::ExecuteInteractive() {
81 if (complete)
82 return;
83
84 const auto storage = broker.PopInteractiveDataToApplet();
85 ASSERT(storage != nullptr);
86 const auto data = storage->GetData();
87 const auto status = static_cast<bool>(data[0]);
88
89 if (status == INTERACTIVE_STATUS_OK) {
90 complete = true;
91 } else {
92 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
93
94 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
95 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
96 frontend.SendTextCheckDialog(
97 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
98 [this] { broker.SignalStateChanged(); });
99 }
100}
101
102void SoftwareKeyboard::Execute() {
103 if (complete) {
104 broker.PushNormalDataFromApplet(IStorage{final_data});
105 return;
106 }
107
108 const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
109
110 const auto parameters = ConvertToFrontendParameters(config, initial_text);
111
112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
113 parameters);
114}
115
116void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
117 std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
118
119 if (text.has_value()) {
120 std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
121
122 if (config.utf_8) {
123 const u64 size = text->size() + 8;
124 const auto new_text = Common::UTF16ToUTF8(*text);
125
126 std::memcpy(output_sub.data(), &size, sizeof(u64));
127 std::memcpy(output_sub.data() + 8, new_text.data(),
128 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
129
130 output_main[0] = INTERACTIVE_STATUS_OK;
131 std::memcpy(output_main.data() + 4, new_text.data(),
132 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
133 } else {
134 const u64 size = text->size() * 2 + 8;
135 std::memcpy(output_sub.data(), &size, sizeof(u64));
136 std::memcpy(output_sub.data() + 8, text->data(),
137 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
138
139 output_main[0] = INTERACTIVE_STATUS_OK;
140 std::memcpy(output_main.data() + 4, text->data(),
141 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
142 }
143
144 complete = !config.text_check;
145 final_data = output_main;
146
147 if (complete) {
148 broker.PushNormalDataFromApplet(IStorage{output_main});
149 } else {
150 broker.PushInteractiveDataFromApplet(IStorage{output_sub});
151 }
152
153 broker.SignalStateChanged();
154 } else {
155 output_main[0] = 1;
156 complete = true;
157 broker.PushNormalDataFromApplet(IStorage{output_main});
158 broker.SignalStateChanged();
159 }
160}
161} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
new file mode 100644
index 000000000..efd5753a1
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -0,0 +1,74 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <string>
9#include <vector>
10
11#include "common/common_funcs.h"
12#include "common/swap.h"
13#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applets/applets.h"
15
16namespace Service::AM::Applets {
17
18enum class KeysetDisable : u32 {
19 Space = 0x02,
20 Address = 0x04,
21 Percent = 0x08,
22 Slashes = 0x10,
23 Numbers = 0x40,
24 DownloadCode = 0x80,
25};
26
27struct KeyboardConfig {
28 INSERT_PADDING_BYTES(4);
29 std::array<char16_t, 9> submit_text;
30 u16_le left_symbol_key;
31 u16_le right_symbol_key;
32 INSERT_PADDING_BYTES(1);
33 KeysetDisable keyset_disable_bitmask;
34 u32_le initial_cursor_position;
35 std::array<char16_t, 65> header_text;
36 std::array<char16_t, 129> sub_text;
37 std::array<char16_t, 257> guide_text;
38 u32_le length_limit;
39 INSERT_PADDING_BYTES(4);
40 u32_le is_password;
41 INSERT_PADDING_BYTES(5);
42 bool utf_8;
43 bool draw_background;
44 u32_le initial_string_offset;
45 u32_le initial_string_size;
46 u32_le user_dictionary_offset;
47 u32_le user_dictionary_size;
48 bool text_check;
49 u64_le text_check_callback;
50};
51static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
52
53class SoftwareKeyboard final : public Applet {
54public:
55 SoftwareKeyboard();
56 ~SoftwareKeyboard() override;
57
58 void Initialize() override;
59
60 bool TransactionComplete() const override;
61 ResultCode GetStatus() const override;
62 void ExecuteInteractive() override;
63 void Execute() override;
64
65 void WriteText(std::optional<std::u16string> text);
66
67private:
68 KeyboardConfig config;
69 std::u16string initial_text;
70 bool complete = false;
71 std::vector<u8> final_data;
72};
73
74} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp
new file mode 100644
index 000000000..ed166b87d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.cpp
@@ -0,0 +1,70 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include "common/hex_util.h"
8#include "common/logging/log.h"
9#include "core/hle/result.h"
10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/stub_applet.h"
12
13namespace Service::AM::Applets {
14
15static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
16 std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
17 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
18 const auto data = storage->GetData();
19 LOG_INFO(Service_AM,
20 "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
21 prefix, data.size(), Common::HexVectorToString(data));
22 }
23
24 storage = broker.PopInteractiveDataToApplet();
25 for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
26 const auto data = storage->GetData();
27 LOG_INFO(Service_AM,
28 "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
29 prefix, data.size(), Common::HexVectorToString(data));
30 }
31}
32
33StubApplet::StubApplet() = default;
34
35StubApplet::~StubApplet() = default;
36
37void StubApplet::Initialize() {
38 LOG_WARNING(Service_AM, "called (STUBBED)");
39 Applet::Initialize();
40 LogCurrentStorage(broker, "Initialize");
41}
42
43bool StubApplet::TransactionComplete() const {
44 LOG_WARNING(Service_AM, "called (STUBBED)");
45 return true;
46}
47
48ResultCode StubApplet::GetStatus() const {
49 LOG_WARNING(Service_AM, "called (STUBBED)");
50 return RESULT_SUCCESS;
51}
52
53void StubApplet::ExecuteInteractive() {
54 LOG_WARNING(Service_AM, "called (STUBBED)");
55 LogCurrentStorage(broker, "ExecuteInteractive");
56
57 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
58 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
59 broker.SignalStateChanged();
60}
61
62void StubApplet::Execute() {
63 LOG_WARNING(Service_AM, "called (STUBBED)");
64 LogCurrentStorage(broker, "Execute");
65
66 broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
67 broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
68 broker.SignalStateChanged();
69}
70} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
new file mode 100644
index 000000000..7d8dc968d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.h
@@ -0,0 +1,24 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/am/applets/applets.h"
8
9namespace Service::AM::Applets {
10
11class StubApplet final : public Applet {
12public:
13 StubApplet();
14 ~StubApplet() override;
15
16 void Initialize() override;
17
18 bool TransactionComplete() const override;
19 ResultCode GetStatus() const override;
20 void ExecuteInteractive() override;
21 void Execute() override;
22};
23
24} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index ff1edefbb..23e1f1165 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -44,8 +44,10 @@ enum class AudioState : u32 {
44 44
45class IAudioOut final : public ServiceFramework<IAudioOut> { 45class IAudioOut final : public ServiceFramework<IAudioOut> {
46public: 46public:
47 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) 47 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
48 : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) { 48 std::string&& unique_name)
49 : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params),
50 device_name(std::move(device_name)) {
49 51
50 static const FunctionInfo functions[] = { 52 static const FunctionInfo functions[] = {
51 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 53 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -69,7 +71,7 @@ public:
69 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); 71 Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
70 72
71 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, 73 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
72 "IAudioOut", [=]() { buffer_event->Signal(); }); 74 std::move(unique_name), [=]() { buffer_event->Signal(); });
73 } 75 }
74 76
75private: 77private:
@@ -177,6 +179,7 @@ private:
177 179
178 AudioCore::AudioOut& audio_core; 180 AudioCore::AudioOut& audio_core;
179 AudioCore::StreamPtr stream; 181 AudioCore::StreamPtr stream;
182 std::string device_name;
180 183
181 AudoutParams audio_params{}; 184 AudoutParams audio_params{};
182 185
@@ -199,7 +202,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
199void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 202void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
200 LOG_DEBUG(Service_Audio, "called"); 203 LOG_DEBUG(Service_Audio, "called");
201 204
202 ctx.WriteBuffer(DefaultDevice); 205 const auto device_name_data{ctx.ReadBuffer()};
206 std::string device_name;
207 if (device_name_data[0] != '\0') {
208 device_name.assign(device_name_data.begin(), device_name_data.end());
209 } else {
210 device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
211 }
212 ctx.WriteBuffer(device_name);
213
203 IPC::RequestParser rp{ctx}; 214 IPC::RequestParser rp{ctx};
204 auto params{rp.PopRaw<AudoutParams>()}; 215 auto params{rp.PopRaw<AudoutParams>()};
205 if (params.channel_count <= 2) { 216 if (params.channel_count <= 2) {
@@ -212,10 +223,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
212 params.sample_rate = DefaultSampleRate; 223 params.sample_rate = DefaultSampleRate;
213 } 224 }
214 225
215 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl 226 std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
216 // will likely need to be updated as well. 227 auto audio_out_interface = std::make_shared<IAudioOut>(
217 ASSERT_MSG(!audio_out_interface, "Unimplemented"); 228 params, *audio_core, std::move(device_name), std::move(unique_name));
218 audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
219 229
220 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 230 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
221 rb.Push(RESULT_SUCCESS); 231 rb.Push(RESULT_SUCCESS);
@@ -224,6 +234,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
224 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); 234 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
225 rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); 235 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
226 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 236 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
237
238 audio_out_interfaces.push_back(std::move(audio_out_interface));
227} 239}
228 240
229AudOutU::AudOutU() : ServiceFramework("audout:u") { 241AudOutU::AudOutU() : ServiceFramework("audout:u") {
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index dcaf64708..aed4c43b2 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace AudioCore { 10namespace AudioCore {
@@ -24,7 +25,7 @@ public:
24 ~AudOutU() override; 25 ~AudOutU() override;
25 26
26private: 27private:
27 std::shared_ptr<IAudioOut> audio_out_interface; 28 std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
28 std::unique_ptr<AudioCore::AudioOut> audio_core; 29 std::unique_ptr<AudioCore::AudioOut> audio_core;
29 30
30 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 31 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index fac6785a5..d3ea57ea7 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -28,13 +28,13 @@ public:
28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
30 {3, &IAudioRenderer::GetState, "GetState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, 31 {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
32 {5, &IAudioRenderer::Start, "Start"}, 32 {5, &IAudioRenderer::Start, "Start"},
33 {6, &IAudioRenderer::Stop, "Stop"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
35 {8, nullptr, "SetRenderingTimeLimit"}, 35 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
36 {9, nullptr, "GetRenderingTimeLimit"}, 36 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
37 {10, nullptr, "RequestUpdateAuto"}, 37 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
38 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
39 }; 39 };
40 // clang-format on 40 // clang-format on
@@ -79,7 +79,7 @@ private:
79 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
80 } 80 }
81 81
82 void RequestUpdate(Kernel::HLERequestContext& ctx) { 82 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
84 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
@@ -110,8 +110,29 @@ private:
110 LOG_WARNING(Service_Audio, "(STUBBED) called"); 110 LOG_WARNING(Service_Audio, "(STUBBED) called");
111 } 111 }
112 112
113 void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
114 IPC::RequestParser rp{ctx};
115 rendering_time_limit_percent = rp.Pop<u32>();
116 ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
117
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS);
120
121 LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
122 rendering_time_limit_percent);
123 }
124
125 void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
126 LOG_DEBUG(Service_Audio, "called");
127
128 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS);
130 rb.Push(rendering_time_limit_percent);
131 }
132
113 Kernel::SharedPtr<Kernel::Event> system_event; 133 Kernel::SharedPtr<Kernel::Event> system_event;
114 std::unique_ptr<AudioCore::AudioRenderer> renderer; 134 std::unique_ptr<AudioCore::AudioRenderer> renderer;
135 u32 rendering_time_limit_percent = 100;
115}; 136};
116 137
117class IAudioDevice final : public ServiceFramework<IAudioDevice> { 138class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 783c39503..763e619a4 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -77,8 +77,8 @@ private:
77 IPC::ResponseBuilder rb{ctx, 6}; 77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS); 78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed); 79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count); 80 rb.Push<u32>(sample_count);
81 rb.Push<u64>(performance);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 } 83 }
84 84
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index d0a15cc4c..f3bde6d0d 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -2,12 +2,49 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/service/btdrv/btdrv.h" 9#include "core/hle/service/btdrv/btdrv.h"
6#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h" 11#include "core/hle/service/sm/sm.h"
8 12
9namespace Service::BtDrv { 13namespace Service::BtDrv {
10 14
15class Bt final : public ServiceFramework<Bt> {
16public:
17 explicit Bt() : ServiceFramework{"bt"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Unknown0"},
21 {1, nullptr, "Unknown1"},
22 {2, nullptr, "Unknown2"},
23 {3, nullptr, "Unknown3"},
24 {4, nullptr, "Unknown4"},
25 {5, nullptr, "Unknown5"},
26 {6, nullptr, "Unknown6"},
27 {7, nullptr, "Unknown7"},
28 {8, nullptr, "Unknown8"},
29 {9, &Bt::RegisterEvent, "RegisterEvent"},
30 };
31 // clang-format on
32 RegisterHandlers(functions);
33 }
34
35private:
36 void RegisterEvent(Kernel::HLERequestContext& ctx) {
37 auto& kernel = Core::System::GetInstance().Kernel();
38 register_event =
39 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
40 IPC::ResponseBuilder rb{ctx, 2, 1};
41 rb.Push(RESULT_SUCCESS);
42 rb.PushCopyObjects(register_event);
43 LOG_WARNING(Service_BTM, "(STUBBED) called");
44 }
45 Kernel::SharedPtr<Kernel::Event> register_event;
46};
47
11class BtDrv final : public ServiceFramework<BtDrv> { 48class BtDrv final : public ServiceFramework<BtDrv> {
12public: 49public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} { 50 explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +104,7 @@ public:
67 104
68void InstallInterfaces(SM::ServiceManager& sm) { 105void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm); 106 std::make_shared<BtDrv>()->InstallAsService(sm);
107 std::make_shared<Bt>()->InstallAsService(sm);
70} 108}
71 109
72} // namespace Service::BtDrv 110} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b949bfabd..a02f6b53a 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -6,13 +6,118 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/btm/btm.h" 11#include "core/hle/service/btm/btm.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 13
14namespace Service::BTM { 14namespace Service::BTM {
15 15
16class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
17public:
18 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
22 {1, nullptr, "Unknown1"},
23 {2, nullptr, "Unknown2"},
24 {3, nullptr, "Unknown3"},
25 {4, nullptr, "Unknown4"},
26 {5, nullptr, "Unknown5"},
27 {6, nullptr, "Unknown6"},
28 {7, nullptr, "Unknown7"},
29 {8, nullptr, "Unknown8"},
30 {9, nullptr, "Unknown9"},
31 {10, nullptr, "Unknown10"},
32 {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
33 {18, nullptr, "Unknown18"},
34 {19, nullptr, "Unknown19"},
35 {20, nullptr, "Unknown20"},
36 {21, nullptr, "Unknown21"},
37 {22, nullptr, "Unknown22"},
38 {23, nullptr, "Unknown23"},
39 {24, nullptr, "Unknown24"},
40 {25, nullptr, "Unknown25"},
41 {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
42 {27, nullptr, "Unknown27"},
43 {28, nullptr, "Unknown28"},
44 {29, nullptr, "Unknown29"},
45 {30, nullptr, "Unknown30"},
46 {31, nullptr, "Unknown31"},
47 {32, nullptr, "Unknown32"},
48 {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
49 {34, nullptr, "Unknown34"},
50 {35, nullptr, "Unknown35"},
51 {36, nullptr, "Unknown36"},
52 {37, nullptr, "Unknown37"},
53 };
54 // clang-format on
55 RegisterHandlers(functions);
56 }
57
58private:
59 void GetScanEvent(Kernel::HLERequestContext& ctx) {
60 auto& kernel = Core::System::GetInstance().Kernel();
61 scan_event =
62 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
63 IPC::ResponseBuilder rb{ctx, 2, 1};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushCopyObjects(scan_event);
66 LOG_WARNING(Service_BTM, "(STUBBED) called");
67 }
68 void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
69 auto& kernel = Core::System::GetInstance().Kernel();
70 connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
71 "IBtmUserCore:ConnectionEvent");
72 IPC::ResponseBuilder rb{ctx, 2, 1};
73 rb.Push(RESULT_SUCCESS);
74 rb.PushCopyObjects(connection_event);
75 LOG_WARNING(Service_BTM, "(STUBBED) called");
76 }
77 void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
78 auto& kernel = Core::System::GetInstance().Kernel();
79 service_discovery =
80 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
81 IPC::ResponseBuilder rb{ctx, 2, 1};
82 rb.Push(RESULT_SUCCESS);
83 rb.PushCopyObjects(service_discovery);
84 LOG_WARNING(Service_BTM, "(STUBBED) called");
85 }
86 void GetConfigEvent(Kernel::HLERequestContext& ctx) {
87 auto& kernel = Core::System::GetInstance().Kernel();
88 config_event =
89 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
90 IPC::ResponseBuilder rb{ctx, 2, 1};
91 rb.Push(RESULT_SUCCESS);
92 rb.PushCopyObjects(config_event);
93 LOG_WARNING(Service_BTM, "(STUBBED) called");
94 }
95 Kernel::SharedPtr<Kernel::Event> scan_event;
96 Kernel::SharedPtr<Kernel::Event> connection_event;
97 Kernel::SharedPtr<Kernel::Event> service_discovery;
98 Kernel::SharedPtr<Kernel::Event> config_event;
99};
100
101class BTM_USR final : public ServiceFramework<BTM_USR> {
102public:
103 explicit BTM_USR() : ServiceFramework{"btm:u"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
107 };
108 // clang-format on
109 RegisterHandlers(functions);
110 }
111
112private:
113 void GetCoreImpl(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(RESULT_SUCCESS);
116 rb.PushIpcInterface<IBtmUserCore>();
117 LOG_DEBUG(Service_BTM, "called");
118 }
119};
120
16class BTM final : public ServiceFramework<BTM> { 121class BTM final : public ServiceFramework<BTM> {
17public: 122public:
18 explicit BTM() : ServiceFramework{"btm"} { 123 explicit BTM() : ServiceFramework{"btm"} {
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
116 std::make_shared<BTM>()->InstallAsService(sm); 221 std::make_shared<BTM>()->InstallAsService(sm);
117 std::make_shared<BTM_DBG>()->InstallAsService(sm); 222 std::make_shared<BTM_DBG>()->InstallAsService(sm);
118 std::make_shared<BTM_SYS>()->InstallAsService(sm); 223 std::make_shared<BTM_SYS>()->InstallAsService(sm);
224 std::make_shared<BTM_USR>()->InstallAsService(sm);
119} 225}
120 226
121} // namespace Service::BTM 227} // namespace Service::BTM
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e32a7c48e..2aa77f68d 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -303,25 +303,46 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
303 static_cast<u8>(space), save_struct.DebugInfo()); 303 static_cast<u8>(space), save_struct.DebugInfo());
304 304
305 if (save_data_factory == nullptr) { 305 if (save_data_factory == nullptr) {
306 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); 306 return FileSys::ERROR_ENTITY_NOT_FOUND;
307 } 307 }
308 308
309 return save_data_factory->Open(space, save_struct); 309 return save_data_factory->Open(space, save_struct);
310} 310}
311 311
312ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
313 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
314
315 if (save_data_factory == nullptr) {
316 return FileSys::ERROR_ENTITY_NOT_FOUND;
317 }
318
319 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
320}
321
312ResultVal<FileSys::VirtualDir> OpenSDMC() { 322ResultVal<FileSys::VirtualDir> OpenSDMC() {
313 LOG_TRACE(Service_FS, "Opening SDMC"); 323 LOG_TRACE(Service_FS, "Opening SDMC");
314 324
315 if (sdmc_factory == nullptr) { 325 if (sdmc_factory == nullptr) {
316 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); 326 return FileSys::ERROR_SD_CARD_NOT_FOUND;
317 } 327 }
318 328
319 return sdmc_factory->Open(); 329 return sdmc_factory->Open();
320} 330}
321 331
322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 332std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ 333
324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); 334std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
335 if (registered_cache_union == nullptr) {
336 registered_cache_union =
337 std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
338 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
339 }
340
341 return registered_cache_union;
342}
343
344void ClearUnionContents() {
345 registered_cache_union = nullptr;
325} 346}
326 347
327FileSys::RegisteredCache* GetSystemNANDContents() { 348FileSys::RegisteredCache* GetSystemNANDContents() {
@@ -360,11 +381,21 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
360 return bis_factory->GetModificationLoadRoot(title_id); 381 return bis_factory->GetModificationLoadRoot(title_id);
361} 382}
362 383
384FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
385 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
386
387 if (bis_factory == nullptr)
388 return nullptr;
389
390 return bis_factory->GetModificationDumpRoot(title_id);
391}
392
363void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 393void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
364 if (overwrite) { 394 if (overwrite) {
365 bis_factory = nullptr; 395 bis_factory = nullptr;
366 save_data_factory = nullptr; 396 save_data_factory = nullptr;
367 sdmc_factory = nullptr; 397 sdmc_factory = nullptr;
398 ClearUnionContents();
368 } 399 }
369 400
370 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 401 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
@@ -373,13 +404,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
373 FileSys::Mode::ReadWrite); 404 FileSys::Mode::ReadWrite);
374 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 405 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
375 FileSys::Mode::ReadWrite); 406 FileSys::Mode::ReadWrite);
407 auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
408 FileSys::Mode::ReadWrite);
376 409
377 if (bis_factory == nullptr) 410 if (bis_factory == nullptr) {
378 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 411 bis_factory =
379 if (save_data_factory == nullptr) 412 std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
413 }
414
415 if (save_data_factory == nullptr) {
380 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 416 save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
381 if (sdmc_factory == nullptr) 417 }
418
419 if (sdmc_factory == nullptr) {
382 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 420 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
421 }
383} 422}
384 423
385void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { 424void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6ca5c5636..0a6cb6635 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -45,15 +45,18 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
45 FileSys::ContentRecordType type); 45 FileSys::ContentRecordType type);
46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 49ResultVal<FileSys::VirtualDir> OpenSDMC();
49 50
50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 51std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
52void ClearUnionContents();
51 53
52FileSys::RegisteredCache* GetSystemNANDContents(); 54FileSys::RegisteredCache* GetSystemNANDContents();
53FileSys::RegisteredCache* GetUserNANDContents(); 55FileSys::RegisteredCache* GetUserNANDContents();
54FileSys::RegisteredCache* GetSDMCContents(); 56FileSys::RegisteredCache* GetSDMCContents();
55 57
56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 58FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
59FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
57 60
58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 61// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
59// above is called. 62// above is called.
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c1c83a11d..038dc80b1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -11,6 +11,7 @@
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hex_util.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
16#include "core/file_sys/directory.h" 17#include "core/file_sys/directory.h"
@@ -62,12 +63,12 @@ private:
62 // Error checking 63 // Error checking
63 if (length < 0) { 64 if (length < 0) {
64 IPC::ResponseBuilder rb{ctx, 2}; 65 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 66 rb.Push(FileSys::ERROR_INVALID_SIZE);
66 return; 67 return;
67 } 68 }
68 if (offset < 0) { 69 if (offset < 0) {
69 IPC::ResponseBuilder rb{ctx, 2}; 70 IPC::ResponseBuilder rb{ctx, 2};
70 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 71 rb.Push(FileSys::ERROR_INVALID_OFFSET);
71 return; 72 return;
72 } 73 }
73 74
@@ -107,12 +108,12 @@ private:
107 // Error checking 108 // Error checking
108 if (length < 0) { 109 if (length < 0) {
109 IPC::ResponseBuilder rb{ctx, 2}; 110 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 111 rb.Push(FileSys::ERROR_INVALID_SIZE);
111 return; 112 return;
112 } 113 }
113 if (offset < 0) { 114 if (offset < 0) {
114 IPC::ResponseBuilder rb{ctx, 2}; 115 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 116 rb.Push(FileSys::ERROR_INVALID_OFFSET);
116 return; 117 return;
117 } 118 }
118 119
@@ -138,12 +139,12 @@ private:
138 // Error checking 139 // Error checking
139 if (length < 0) { 140 if (length < 0) {
140 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); 142 rb.Push(FileSys::ERROR_INVALID_SIZE);
142 return; 143 return;
143 } 144 }
144 if (offset < 0) { 145 if (offset < 0) {
145 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); 147 rb.Push(FileSys::ERROR_INVALID_OFFSET);
147 return; 148 return;
148 } 149 }
149 150
@@ -451,7 +452,147 @@ private:
451 VfsDirectoryServiceWrapper backend; 452 VfsDirectoryServiceWrapper backend;
452}; 453};
453 454
455class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
456public:
457 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
458 : ServiceFramework("ISaveDataInfoReader") {
459 static const FunctionInfo functions[] = {
460 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
461 };
462 RegisterHandlers(functions);
463
464 FindAllSaves(space);
465 }
466
467 void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
468 // Calculate how many entries we can fit in the output buffer
469 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
470
471 // Cap at total number of entries.
472 const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
473
474 // Determine data start and end
475 const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
476 const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
477 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
478
479 next_entry_index += actual_entries;
480
481 // Write the data to memory
482 ctx.WriteBuffer(begin, range_size);
483
484 IPC::ResponseBuilder rb{ctx, 3};
485 rb.Push(RESULT_SUCCESS);
486 rb.Push<u32>(static_cast<u32>(actual_entries));
487 }
488
489private:
490 static u64 stoull_be(std::string_view str) {
491 if (str.size() != 16)
492 return 0;
493
494 const auto bytes = Common::HexStringToArray<0x8>(str);
495 u64 out{};
496 std::memcpy(&out, bytes.data(), sizeof(u64));
497
498 return Common::swap64(out);
499 }
500
501 void FindAllSaves(FileSys::SaveDataSpaceId space) {
502 const auto save_root = OpenSaveDataSpace(space);
503 ASSERT(save_root.Succeeded());
504
505 for (const auto& type : (*save_root)->GetSubdirectories()) {
506 if (type->GetName() == "save") {
507 for (const auto& save_id : type->GetSubdirectories()) {
508 for (const auto& user_id : save_id->GetSubdirectories()) {
509 const auto save_id_numeric = stoull_be(save_id->GetName());
510 auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
511 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
512
513 if (save_id_numeric != 0) {
514 // System Save Data
515 info.emplace_back(SaveDataInfo{
516 0,
517 space,
518 FileSys::SaveDataType::SystemSaveData,
519 {},
520 user_id_numeric,
521 save_id_numeric,
522 0,
523 user_id->GetSize(),
524 {},
525 });
526
527 continue;
528 }
529
530 for (const auto& title_id : user_id->GetSubdirectories()) {
531 const auto device =
532 std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
533 [](u8 val) { return val == 0; });
534 info.emplace_back(SaveDataInfo{
535 0,
536 space,
537 device ? FileSys::SaveDataType::DeviceSaveData
538 : FileSys::SaveDataType::SaveData,
539 {},
540 user_id_numeric,
541 save_id_numeric,
542 stoull_be(title_id->GetName()),
543 title_id->GetSize(),
544 {},
545 });
546 }
547 }
548 }
549 } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
550 // Temporary Storage
551 for (const auto& user_id : type->GetSubdirectories()) {
552 for (const auto& title_id : user_id->GetSubdirectories()) {
553 if (!title_id->GetFiles().empty() ||
554 !title_id->GetSubdirectories().empty()) {
555 auto user_id_numeric =
556 Common::HexStringToArray<0x10>(user_id->GetName());
557 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
558
559 info.emplace_back(SaveDataInfo{
560 0,
561 space,
562 FileSys::SaveDataType::TemporaryStorage,
563 {},
564 user_id_numeric,
565 stoull_be(type->GetName()),
566 stoull_be(title_id->GetName()),
567 title_id->GetSize(),
568 {},
569 });
570 }
571 }
572 }
573 }
574 }
575 }
576
577 struct SaveDataInfo {
578 u64_le save_id_unknown;
579 FileSys::SaveDataSpaceId space;
580 FileSys::SaveDataType type;
581 INSERT_PADDING_BYTES(0x6);
582 std::array<u8, 0x10> user_id;
583 u64_le save_id;
584 u64_le title_id;
585 u64_le save_image_size;
586 INSERT_PADDING_BYTES(0x28);
587 };
588 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
589
590 std::vector<SaveDataInfo> info;
591 u64 next_entry_index = 0;
592};
593
454FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 594FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
595 // clang-format off
455 static const FunctionInfo functions[] = { 596 static const FunctionInfo functions[] = {
456 {0, nullptr, "MountContent"}, 597 {0, nullptr, "MountContent"},
457 {1, &FSP_SRV::Initialize, "Initialize"}, 598 {1, &FSP_SRV::Initialize, "Initialize"},
@@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
485 {58, nullptr, "ReadSaveDataFileSystemExtraData"}, 626 {58, nullptr, "ReadSaveDataFileSystemExtraData"},
486 {59, nullptr, "WriteSaveDataFileSystemExtraData"}, 627 {59, nullptr, "WriteSaveDataFileSystemExtraData"},
487 {60, nullptr, "OpenSaveDataInfoReader"}, 628 {60, nullptr, "OpenSaveDataInfoReader"},
488 {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, 629 {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
489 {62, nullptr, "OpenCacheStorageList"}, 630 {62, nullptr, "OpenCacheStorageList"},
490 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, 631 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
491 {65, nullptr, "UpdateSaveDataMacForDebug"}, 632 {65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
544 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 685 {1009, nullptr, "GetAndClearMemoryReportInfo"},
545 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 686 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
546 }; 687 };
688 // clang-format on
547 RegisterHandlers(functions); 689 RegisterHandlers(functions);
548} 690}
549 691
@@ -602,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
602 744
603 if (dir.Failed()) { 745 if (dir.Failed()) {
604 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 746 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
605 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 747 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
606 return; 748 return;
607 } 749 }
608 750
@@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
618 MountSaveData(ctx); 760 MountSaveData(ctx);
619} 761}
620 762
763void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
764 IPC::RequestParser rp{ctx};
765 const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
766
767 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
768 rb.Push(RESULT_SUCCESS);
769 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
770}
771
621void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 772void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
622 LOG_WARNING(Service_FS, "(STUBBED) called"); 773 LOG_WARNING(Service_FS, "(STUBBED) called");
623 774
@@ -685,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
685 static_cast<u8>(storage_id), title_id); 836 static_cast<u8>(storage_id), title_id);
686 837
687 IPC::ResponseBuilder rb{ctx, 2}; 838 IPC::ResponseBuilder rb{ctx, 2};
688 rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); 839 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
689} 840}
690 841
691} // namespace Service::FileSystem 842} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4aa0358cb..e7abec0a3 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -25,6 +25,7 @@ private:
25 void CreateSaveData(Kernel::HLERequestContext& ctx); 25 void CreateSaveData(Kernel::HLERequestContext& ctx);
26 void MountSaveData(Kernel::HLERequestContext& ctx); 26 void MountSaveData(Kernel::HLERequestContext& ctx);
27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); 27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
28 void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
28 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 29 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 30 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
30 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); 31 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 3d100763f..c22357d8c 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,9 +6,14 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h" 8#include "core/hle/service/hid/controllers/debug_pad.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16
12Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default; 18Controller_DebugPad::~Controller_DebugPad() = default;
14 19
@@ -33,10 +38,44 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
33 38
34 cur_entry.sampling_number = last_entry.sampling_number + 1; 39 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number; 40 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states 41 cur_entry.attribute.connected.Assign(1);
42 auto& pad = cur_entry.pad_state;
43
44 using namespace Settings::NativeButton;
45 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
46 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
47 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
48 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
49 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
50 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
51 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
52 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
53 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
54 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
55 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
56 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
57 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
59
60 const auto [stick_l_x_f, stick_l_y_f] =
61 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
62 const auto [stick_r_x_f, stick_r_y_f] =
63 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
64 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
65 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
66 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
67 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
37 68
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 69 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39} 70}
40 71
41void Controller_DebugPad::OnLoadInputDevices() {} 72void Controller_DebugPad::OnLoadInputDevices() {
73 std::transform(Settings::values.debug_pad_buttons.begin(),
74 Settings::values.debug_pad_buttons.begin() +
75 Settings::NativeButton::NUM_BUTTONS_HID,
76 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
77 std::transform(Settings::values.debug_pad_analogs.begin(),
78 Settings::values.debug_pad_analogs.end(), analogs.begin(),
79 Input::CreateDevice<Input::AnalogDevice>);
80}
42} // namespace Service::HID 81} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 62b4f2682..68b734248 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -5,10 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/settings.h"
12 15
13namespace Service::HID { 16namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase { 17class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
35 }; 38 };
36 static_assert(sizeof(AnalogStick) == 0x8); 39 static_assert(sizeof(AnalogStick) == 0x8);
37 40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32_le> a;
45 BitField<1, 1, u32_le> b;
46 BitField<2, 1, u32_le> x;
47 BitField<3, 1, u32_le> y;
48 BitField<4, 1, u32_le> l;
49 BitField<5, 1, u32_le> r;
50 BitField<6, 1, u32_le> zl;
51 BitField<7, 1, u32_le> zr;
52 BitField<8, 1, u32_le> plus;
53 BitField<9, 1, u32_le> minus;
54 BitField<10, 1, u32_le> d_left;
55 BitField<11, 1, u32_le> d_up;
56 BitField<12, 1, u32_le> d_right;
57 BitField<13, 1, u32_le> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union {
64 u32_le raw{};
65 BitField<0, 1, u32_le> connected;
66 };
67 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
69
38 struct PadStates { 70 struct PadStates {
39 s64_le sampling_number; 71 s64_le sampling_number;
40 s64_le sampling_number2; 72 s64_le sampling_number2;
41 u32_le attribute; 73 Attributes attribute;
42 u32_le button_state; 74 PadState pad_state;
43 AnalogStick r_stick; 75 AnalogStick r_stick;
44 AnalogStick l_stick; 76 AnalogStick l_stick;
45 }; 77 };
@@ -52,5 +84,10 @@ private:
52 }; 84 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{}; 86 SharedMemory shared_memory{};
87
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
55}; 92};
56} // namespace Service::HID 93} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index ccfbce9ac..ca75adc2b 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,9 +6,11 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h" 8#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
12 14
13Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default; 16Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
34 36
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 38 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states 39
40 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
41 for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
42 cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
43 }
44 }
45
46 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
47 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
48 }
38 49
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 50 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 51}
41 52
42void Controller_Keyboard::OnLoadInputDevices() {} 53void Controller_Keyboard::OnLoadInputDevices() {
54 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
55 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
56 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
57 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
58}
43} // namespace Service::HID 59} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 493e68fce..f52775456 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,7 +8,9 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase { 16class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
46 }; 48 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 49 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{}; 50 SharedMemory shared_memory{};
51
52 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4e246a57d..63391dbe9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
8#include "core/hle/service/hid/controllers/mouse.h" 9#include "core/hle/service/hid/controllers/mouse.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
15 16
16void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {} 18void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { 20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
34 34
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states 37
38 if (Settings::values.mouse_enabled) {
39 const auto [px, py, sx, sy] = mouse_device->GetStatus();
40 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
41 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
42 cur_entry.x = x;
43 cur_entry.y = y;
44 cur_entry.delta_x = x - last_entry.x;
45 cur_entry.delta_y = y - last_entry.y;
46 cur_entry.mouse_wheel_x = sx;
47 cur_entry.mouse_wheel_y = sy;
48
49 for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
50 cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
51 }
52 }
38 53
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 55}
41 56
42void Controller_Mouse::OnLoadInputDevices() {} 57void Controller_Mouse::OnLoadInputDevices() {
58 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
59 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
60 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
61}
43} // namespace Service::HID 62} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 543b0b71f..70b654d07 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,7 +7,9 @@
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/settings.h"
11 13
12namespace Service::HID { 14namespace Service::HID {
13class Controller_Mouse final : public ControllerBase { 15class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
35 s32_le y; 37 s32_le y;
36 s32_le delta_x; 38 s32_le delta_x;
37 s32_le delta_y; 39 s32_le delta_y;
38 s32_le mouse_wheel; 40 s32_le mouse_wheel_x;
41 s32_le mouse_wheel_y;
39 s32_le button; 42 s32_le button;
40 s32_le attribute; 43 s32_le attribute;
41 }; 44 };
@@ -46,5 +49,9 @@ private:
46 std::array<MouseState, 17> mouse_states; 49 std::array<MouseState, 17> mouse_states;
47 }; 50 };
48 SharedMemory shared_memory{}; 51 SharedMemory shared_memory{};
52
53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4b4d1324f..46604887c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,22 +17,13 @@
17#include "core/settings.h" 17#include "core/settings.h"
18 18
19namespace Service::HID { 19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 20constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 21constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00; 22constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2; 23constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7; 24constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{ 25constexpr std::array<u32, 10> npad_id_list{
35 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, 26 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
36}; 27};
37 28
38enum class JoystickId : std::size_t { 29enum class JoystickId : std::size_t {
@@ -40,6 +31,66 @@ enum class JoystickId : std::size_t {
40 Joystick_Right, 31 Joystick_Right,
41}; 32};
42 33
34static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
35 switch (type) {
36 case Settings::ControllerType::ProController:
37 return Controller_NPad::NPadControllerType::ProController;
38 case Settings::ControllerType::DualJoycon:
39 return Controller_NPad::NPadControllerType::JoyDual;
40 case Settings::ControllerType::LeftJoycon:
41 return Controller_NPad::NPadControllerType::JoyLeft;
42 case Settings::ControllerType::RightJoycon:
43 return Controller_NPad::NPadControllerType::JoyRight;
44 default:
45 UNREACHABLE();
46 return Controller_NPad::NPadControllerType::JoyDual;
47 }
48}
49
50std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
51 switch (npad_id) {
52 case 0:
53 case 1:
54 case 2:
55 case 3:
56 case 4:
57 case 5:
58 case 6:
59 case 7:
60 return npad_id;
61 case 8:
62 case NPAD_HANDHELD:
63 return 8;
64 case 9:
65 case NPAD_UNKNOWN:
66 return 9;
67 default:
68 UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
69 return 0;
70 }
71}
72
73u32 Controller_NPad::IndexToNPad(std::size_t index) {
74 switch (index) {
75 case 0:
76 case 1:
77 case 2:
78 case 3:
79 case 4:
80 case 5:
81 case 6:
82 case 7:
83 return static_cast<u32>(index);
84 case 8:
85 return NPAD_HANDHELD;
86 case 9:
87 return NPAD_UNKNOWN;
88 default:
89 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
90 return 0;
91 };
92}
93
43Controller_NPad::Controller_NPad() = default; 94Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default; 95Controller_NPad::~Controller_NPad() = default;
45 96
@@ -56,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
56 controller.joy_styles.handheld.Assign(1); 107 controller.joy_styles.handheld.Assign(1);
57 controller.device_type.handheld.Assign(1); 108 controller.device_type.handheld.Assign(1);
58 controller.pad_assignment = NPadAssignments::Dual; 109 controller.pad_assignment = NPadAssignments::Dual;
110 controller.properties.is_vertical.Assign(1);
111 controller.properties.use_plus.Assign(1);
112 controller.properties.use_minus.Assign(1);
59 break; 113 break;
60 case NPadControllerType::JoyDual: 114 case NPadControllerType::JoyDual:
61 controller.joy_styles.joycon_dual.Assign(1); 115 controller.joy_styles.joycon_dual.Assign(1);
62 controller.device_type.joycon_left.Assign(1); 116 controller.device_type.joycon_left.Assign(1);
63 controller.device_type.joycon_right.Assign(1); 117 controller.device_type.joycon_right.Assign(1);
118 controller.properties.is_vertical.Assign(1);
119 controller.properties.use_plus.Assign(1);
120 controller.properties.use_minus.Assign(1);
64 controller.pad_assignment = NPadAssignments::Dual; 121 controller.pad_assignment = NPadAssignments::Dual;
65 break; 122 break;
66 case NPadControllerType::JoyLeft: 123 case NPadControllerType::JoyLeft:
67 controller.joy_styles.joycon_left.Assign(1); 124 controller.joy_styles.joycon_left.Assign(1);
68 controller.device_type.joycon_left.Assign(1); 125 controller.device_type.joycon_left.Assign(1);
69 controller.pad_assignment = NPadAssignments::Dual; 126 controller.properties.is_horizontal.Assign(1);
127 controller.properties.use_minus.Assign(1);
128 controller.pad_assignment = NPadAssignments::Single;
70 break; 129 break;
71 case NPadControllerType::JoyRight: 130 case NPadControllerType::JoyRight:
72 controller.joy_styles.joycon_right.Assign(1); 131 controller.joy_styles.joycon_right.Assign(1);
73 controller.device_type.joycon_right.Assign(1); 132 controller.device_type.joycon_right.Assign(1);
74 controller.pad_assignment = NPadAssignments::Dual; 133 controller.properties.is_horizontal.Assign(1);
134 controller.properties.use_plus.Assign(1);
135 controller.pad_assignment = NPadAssignments::Single;
75 break; 136 break;
76 case NPadControllerType::Pokeball: 137 case NPadControllerType::Pokeball:
77 controller.joy_styles.pokeball.Assign(1); 138 controller.joy_styles.pokeball.Assign(1);
@@ -81,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
81 case NPadControllerType::ProController: 142 case NPadControllerType::ProController:
82 controller.joy_styles.pro_controller.Assign(1); 143 controller.joy_styles.pro_controller.Assign(1);
83 controller.device_type.pro_controller.Assign(1); 144 controller.device_type.pro_controller.Assign(1);
145 controller.properties.is_vertical.Assign(1);
146 controller.properties.use_plus.Assign(1);
147 controller.properties.use_minus.Assign(1);
84 controller.pad_assignment = NPadAssignments::Single; 148 controller.pad_assignment = NPadAssignments::Single;
85 break; 149 break;
86 } 150 }
@@ -90,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
90 controller.single_color.button_color = 0; 154 controller.single_color.button_color = 0;
91 155
92 controller.dual_color_error = ColorReadError::ReadOk; 156 controller.dual_color_error = ColorReadError::ReadOk;
93 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; 157 controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
94 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; 158 controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
95 controller.right_color.body_color = JOYCON_BODY_NEON_RED; 159 controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
96 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; 160 controller.right_color.button_color =
97 161 Settings::values.players[controller_idx].button_color_right;
98 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations 162
99 controller.properties.use_plus.Assign(1);
100 controller.properties.use_minus.Assign(1);
101 controller.battery_level[0] = BATTERY_FULL; 163 controller.battery_level[0] = BATTERY_FULL;
102 controller.battery_level[1] = BATTERY_FULL; 164 controller.battery_level[1] = BATTERY_FULL;
103 controller.battery_level[2] = BATTERY_FULL; 165 controller.battery_level[2] = BATTERY_FULL;
@@ -121,26 +183,109 @@ void Controller_NPad::OnInit() {
121 style.pro_controller.Assign(1); 183 style.pro_controller.Assign(1);
122 style.pokeball.Assign(1); 184 style.pokeball.Assign(1);
123 } 185 }
186
187 std::transform(
188 Settings::values.players.begin(), Settings::values.players.end(),
189 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
190 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
191 });
192
193 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
194 [](const ControllerHolder& holder) { return holder.is_connected; });
195
196 // Account for handheld
197 if (connected_controllers[8].is_connected)
198 connected_controllers[8].type = NPadControllerType::Handheld;
199
200 supported_npad_id_types.resize(npad_id_list.size());
201 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
202 npad_id_list.size() * sizeof(u32));
203
204 // Add a default dual joycon controller if none are present.
124 if (std::none_of(connected_controllers.begin(), connected_controllers.end(), 205 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
125 [](const ControllerHolder& controller) { return controller.is_connected; })) { 206 [](const ControllerHolder& controller) { return controller.is_connected; })) {
126 supported_npad_id_types.resize(npad_id_list.size()); 207 supported_npad_id_types.resize(npad_id_list.size());
127 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 208 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
128 npad_id_list.size() * sizeof(u32)); 209 npad_id_list.size() * sizeof(u32));
129 AddNewController(PREFERRED_CONTROLLER); 210 AddNewController(NPadControllerType::JoyDual);
211 }
212
213 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
214 const auto& controller = connected_controllers[i];
215 if (controller.is_connected) {
216 AddNewControllerAt(controller.type, IndexToNPad(i));
217 }
130 } 218 }
131} 219}
132 220
133void Controller_NPad::OnLoadInputDevices() { 221void Controller_NPad::OnLoadInputDevices() {
134 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 222 const auto& players = Settings::values.players;
135 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 223 for (std::size_t i = 0; i < players.size(); ++i) {
136 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 224 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
137 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 225 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
138 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 226 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
139 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); 227 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
228 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
229 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
230 }
140} 231}
141 232
142void Controller_NPad::OnRelease() {} 233void Controller_NPad::OnRelease() {}
143 234
235void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
236 const auto controller_idx = NPadIdToIndex(npad_id);
237 const auto controller_type = connected_controllers[controller_idx].type;
238 if (!connected_controllers[controller_idx].is_connected) {
239 return;
240 }
241 auto& pad_state = npad_pad_states[controller_idx].pad_states;
242 auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
243 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
244 const auto& button_state = buttons[controller_idx];
245 const auto& analog_state = sticks[controller_idx];
246
247 using namespace Settings::NativeButton;
248 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
249 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
250 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
251 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
252 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
253 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
254 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
255 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
256 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
257 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
258 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
259 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
260
261 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
262 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
263 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
264 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
265
266 pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
267 pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
268 pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
269 pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
270
271 pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
272 pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
273 pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
274 pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
275
276 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
277 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
278
279 const auto [stick_l_x_f, stick_l_y_f] =
280 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
281 const auto [stick_r_x_f, stick_r_y_f] =
282 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
283 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
284 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
285 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
286 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
287}
288
144void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { 289void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
145 if (!IsControllerActivated()) 290 if (!IsControllerActivated())
146 return; 291 return;
@@ -176,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
176 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 321 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
177 continue; 322 continue;
178 } 323 }
179 324 const u32 npad_index = static_cast<u32>(i);
180 // Pad states 325 RequestPadStateUpdate(npad_index);
181 ControllerPadState pad_state{}; 326 auto& pad_state = npad_pad_states[npad_index];
182 using namespace Settings::NativeButton;
183 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
184 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
185 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
186 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
187 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
188 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
189 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
190 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
191 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
192 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
193 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
194 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
195
196 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
197 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
198 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
199 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
200
201 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
202 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
203 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
204 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
205
206 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
208 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
209 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
210
211 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
212 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
213
214 AnalogPosition lstick_entry{};
215 AnalogPosition rstick_entry{};
216
217 const auto [stick_l_x_f, stick_l_y_f] =
218 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
219 const auto [stick_r_x_f, stick_r_y_f] =
220 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
221 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
222 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
223 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
224 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
225
226 if (controller_type == NPadControllerType::JoyLeft ||
227 controller_type == NPadControllerType::JoyRight) {
228 if (npad.properties.is_horizontal) {
229 ControllerPadState state{};
230 AnalogPosition temp_lstick_entry{};
231 AnalogPosition temp_rstick_entry{};
232 if (controller_type == NPadControllerType::JoyLeft) {
233 state.d_down.Assign(pad_state.d_left.Value());
234 state.d_left.Assign(pad_state.d_up.Value());
235 state.d_right.Assign(pad_state.d_down.Value());
236 state.d_up.Assign(pad_state.d_right.Value());
237 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
238 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
239
240 state.zl.Assign(pad_state.zl.Value());
241 state.plus.Assign(pad_state.minus.Value());
242
243 temp_lstick_entry = lstick_entry;
244 temp_rstick_entry = rstick_entry;
245 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
246 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
247 temp_lstick_entry.y *= -1;
248 } else if (controller_type == NPadControllerType::JoyRight) {
249 state.x.Assign(pad_state.a.Value());
250 state.a.Assign(pad_state.b.Value());
251 state.b.Assign(pad_state.y.Value());
252 state.y.Assign(pad_state.b.Value());
253
254 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
255 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
256 state.zr.Assign(pad_state.zr.Value());
257 state.plus.Assign(pad_state.plus.Value());
258
259 temp_lstick_entry = lstick_entry;
260 temp_rstick_entry = rstick_entry;
261 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
262 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
263 temp_rstick_entry.x *= -1;
264 }
265 pad_state.raw = state.raw;
266 lstick_entry = temp_lstick_entry;
267 rstick_entry = temp_rstick_entry;
268 }
269 }
270 327
271 auto& main_controller = 328 auto& main_controller =
272 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; 329 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -281,20 +338,64 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
281 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 338 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
282 339
283 if (hold_type == NpadHoldType::Horizontal) { 340 if (hold_type == NpadHoldType::Horizontal) {
284 // TODO(ogniK): Remap buttons for different orientations 341 ControllerPadState state{};
342 AnalogPosition temp_lstick_entry{};
343 AnalogPosition temp_rstick_entry{};
344 if (controller_type == NPadControllerType::JoyLeft) {
345 state.d_down.Assign(pad_state.pad_states.d_left.Value());
346 state.d_left.Assign(pad_state.pad_states.d_up.Value());
347 state.d_right.Assign(pad_state.pad_states.d_down.Value());
348 state.d_up.Assign(pad_state.pad_states.d_right.Value());
349 state.l.Assign(pad_state.pad_states.l.Value() |
350 pad_state.pad_states.left_sl.Value());
351 state.r.Assign(pad_state.pad_states.r.Value() |
352 pad_state.pad_states.left_sr.Value());
353
354 state.zl.Assign(pad_state.pad_states.zl.Value());
355 state.plus.Assign(pad_state.pad_states.minus.Value());
356
357 temp_lstick_entry = pad_state.l_stick;
358 temp_rstick_entry = pad_state.r_stick;
359 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
360 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
361 temp_lstick_entry.y *= -1;
362 } else if (controller_type == NPadControllerType::JoyRight) {
363 state.x.Assign(pad_state.pad_states.a.Value());
364 state.a.Assign(pad_state.pad_states.b.Value());
365 state.b.Assign(pad_state.pad_states.y.Value());
366 state.y.Assign(pad_state.pad_states.b.Value());
367
368 state.l.Assign(pad_state.pad_states.l.Value() |
369 pad_state.pad_states.right_sl.Value());
370 state.r.Assign(pad_state.pad_states.r.Value() |
371 pad_state.pad_states.right_sr.Value());
372 state.zr.Assign(pad_state.pad_states.zr.Value());
373 state.plus.Assign(pad_state.pad_states.plus.Value());
374
375 temp_lstick_entry = pad_state.l_stick;
376 temp_rstick_entry = pad_state.r_stick;
377 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
378 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
379 temp_rstick_entry.x *= -1;
380 }
381 pad_state.pad_states.raw = state.raw;
382 pad_state.l_stick = temp_lstick_entry;
383 pad_state.r_stick = temp_rstick_entry;
285 } 384 }
385
286 libnx_entry.connection_status.raw = 0; 386 libnx_entry.connection_status.raw = 0;
287 387
288 switch (controller_type) { 388 switch (controller_type) {
289 case NPadControllerType::Handheld: 389 case NPadControllerType::Handheld:
290 handheld_entry.connection_status.raw = 0; 390 handheld_entry.connection_status.raw = 0;
291 handheld_entry.connection_status.IsConnected.Assign(1); 391 handheld_entry.connection_status.IsWired.Assign(1);
292 if (!Settings::values.use_docked_mode) { 392 handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
293 handheld_entry.connection_status.IsWired.Assign(1); 393 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
294 } 394 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
295 handheld_entry.pad_states.raw = pad_state.raw; 395 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
296 handheld_entry.l_stick = lstick_entry; 396 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
297 handheld_entry.r_stick = rstick_entry; 397 handheld_entry.pad.l_stick = pad_state.l_stick;
398 handheld_entry.pad.r_stick = pad_state.r_stick;
298 break; 399 break;
299 case NPadControllerType::JoyDual: 400 case NPadControllerType::JoyDual:
300 dual_entry.connection_status.raw = 0; 401 dual_entry.connection_status.raw = 0;
@@ -307,24 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
307 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
308 libnx_entry.connection_status.IsConnected.Assign(1); 409 libnx_entry.connection_status.IsConnected.Assign(1);
309 410
310 dual_entry.pad_states.raw = pad_state.raw; 411 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
311 dual_entry.l_stick = lstick_entry; 412 dual_entry.pad.l_stick = pad_state.l_stick;
312 dual_entry.r_stick = rstick_entry; 413 dual_entry.pad.r_stick = pad_state.r_stick;
414 break;
313 case NPadControllerType::JoyLeft: 415 case NPadControllerType::JoyLeft:
314 left_entry.connection_status.raw = 0; 416 left_entry.connection_status.raw = 0;
315 417
316 left_entry.connection_status.IsConnected.Assign(1); 418 left_entry.connection_status.IsConnected.Assign(1);
317 left_entry.pad_states.raw = pad_state.raw; 419 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
318 left_entry.l_stick = lstick_entry; 420 left_entry.pad.l_stick = pad_state.l_stick;
319 left_entry.r_stick = rstick_entry; 421 left_entry.pad.r_stick = pad_state.r_stick;
320 break; 422 break;
321 case NPadControllerType::JoyRight: 423 case NPadControllerType::JoyRight:
322 right_entry.connection_status.raw = 0; 424 right_entry.connection_status.raw = 0;
323 425
324 right_entry.connection_status.IsConnected.Assign(1); 426 right_entry.connection_status.IsConnected.Assign(1);
325 right_entry.pad_states.raw = pad_state.raw; 427 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
326 right_entry.l_stick = lstick_entry; 428 right_entry.pad.l_stick = pad_state.l_stick;
327 right_entry.r_stick = rstick_entry; 429 right_entry.pad.r_stick = pad_state.r_stick;
328 break; 430 break;
329 case NPadControllerType::Pokeball: 431 case NPadControllerType::Pokeball:
330 pokeball_entry.connection_status.raw = 0; 432 pokeball_entry.connection_status.raw = 0;
@@ -332,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
332 pokeball_entry.connection_status.IsConnected.Assign(1); 434 pokeball_entry.connection_status.IsConnected.Assign(1);
333 pokeball_entry.connection_status.IsWired.Assign(1); 435 pokeball_entry.connection_status.IsWired.Assign(1);
334 436
335 pokeball_entry.pad_states.raw = pad_state.raw; 437 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
336 pokeball_entry.l_stick = lstick_entry; 438 pokeball_entry.pad.l_stick = pad_state.l_stick;
337 pokeball_entry.r_stick = rstick_entry; 439 pokeball_entry.pad.r_stick = pad_state.r_stick;
338 break; 440 break;
339 case NPadControllerType::ProController: 441 case NPadControllerType::ProController:
340 main_controller.connection_status.raw = 0; 442 main_controller.connection_status.raw = 0;
341 443
342 main_controller.connection_status.IsConnected.Assign(1); 444 main_controller.connection_status.IsConnected.Assign(1);
343 main_controller.connection_status.IsWired.Assign(1); 445 main_controller.connection_status.IsWired.Assign(1);
344 main_controller.pad_states.raw = pad_state.raw; 446 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
345 main_controller.l_stick = lstick_entry; 447 main_controller.pad.l_stick = pad_state.l_stick;
346 main_controller.r_stick = rstick_entry; 448 main_controller.pad.r_stick = pad_state.r_stick;
347 break; 449 break;
348 } 450 }
349 451
350 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 452 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
351 // any controllers. 453 // any controllers.
352 libnx_entry.pad_states.raw = pad_state.raw; 454 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
353 libnx_entry.l_stick = lstick_entry; 455 libnx_entry.pad.l_stick = pad_state.l_stick;
354 libnx_entry.r_stick = rstick_entry; 456 libnx_entry.pad.r_stick = pad_state.r_stick;
355 } 457 }
356 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), 458 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
357 shared_memory_entries.size() * sizeof(NPadEntry)); 459 shared_memory_entries.size() * sizeof(NPadEntry));
358} // namespace Service::HID 460}
359 461
360void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 462void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
361 style.raw = style_set.raw; 463 style.raw = style_set.raw;
@@ -370,14 +472,29 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
370 supported_npad_id_types.clear(); 472 supported_npad_id_types.clear();
371 supported_npad_id_types.resize(length / sizeof(u32)); 473 supported_npad_id_types.resize(length / sizeof(u32));
372 std::memcpy(supported_npad_id_types.data(), data, length); 474 std::memcpy(supported_npad_id_types.data(), data, length);
475 bool had_controller_update = false;
373 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 476 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
374 auto& controller = connected_controllers[i]; 477 auto& controller = connected_controllers[i];
375 if (!controller.is_connected) { 478 if (!controller.is_connected) {
376 continue; 479 continue;
377 } 480 }
378 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 481 const auto requested_controller =
379 controller.type = DecideBestController(PREFERRED_CONTROLLER); 482 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
380 InitNewlyAddedControler(i); 483 : NPadControllerType::Handheld;
484 if (!IsControllerSupported(requested_controller)) {
485 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
486 if (is_handheld) {
487 controller.type = NPadControllerType::None;
488 controller.is_connected = false;
489 AddNewController(requested_controller);
490 } else {
491 controller.type = requested_controller;
492 InitNewlyAddedControler(i);
493 }
494 had_controller_update = true;
495 }
496 if (had_controller_update) {
497 styleset_changed_event->Signal();
381 } 498 }
382 } 499 }
383} 500}
@@ -392,8 +509,10 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
392} 509}
393 510
394void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 511void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
512 styleset_changed_event->Signal();
395 hold_type = joy_hold_type; 513 hold_type = joy_hold_type;
396} 514}
515
397Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { 516Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
398 return hold_type; 517 return hold_type;
399} 518}
@@ -409,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
409 return; 528 return;
410 } 529 }
411 for (std::size_t i = 0; i < controller_ids.size(); i++) { 530 for (std::size_t i = 0; i < controller_ids.size(); i++) {
412 std::size_t controller_pos = i; 531 std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
413 // Handheld controller conversion
414 if (controller_pos == NPAD_HANDHELD) {
415 controller_pos = 8;
416 }
417 // Unknown controller conversion
418 if (controller_pos == NPAD_UNKNOWN) {
419 controller_pos = 9;
420 }
421 if (connected_controllers[controller_pos].is_connected) { 532 if (connected_controllers[controller_pos].is_connected) {
422 // TODO(ogniK): Vibrate the physical controller 533 // TODO(ogniK): Vibrate the physical controller
423 } 534 }
@@ -427,13 +538,18 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
427} 538}
428 539
429Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const { 540Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
541 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
542 // be signalled at least once, and signaled after a new controller is connected?
543 styleset_changed_event->Signal();
430 return styleset_changed_event; 544 return styleset_changed_event;
431} 545}
432 546
433Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 547Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
434 return last_processed_vibration; 548 return last_processed_vibration;
435} 549}
550
436void Controller_NPad::AddNewController(NPadControllerType controller) { 551void Controller_NPad::AddNewController(NPadControllerType controller) {
552 controller = DecideBestController(controller);
437 if (controller == NPadControllerType::Handheld) { 553 if (controller == NPadControllerType::Handheld) {
438 connected_controllers[8] = {controller, true}; 554 connected_controllers[8] = {controller, true};
439 InitNewlyAddedControler(8); 555 InitNewlyAddedControler(8);
@@ -451,16 +567,54 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
451 InitNewlyAddedControler(controller_id); 567 InitNewlyAddedControler(controller_id);
452} 568}
453 569
454void Controller_NPad::ConnectNPad(u32 npad_id) { 570void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
455 if (npad_id >= connected_controllers.size()) 571 controller = DecideBestController(controller);
572 if (controller == NPadControllerType::Handheld) {
573 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
574 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
456 return; 575 return;
457 connected_controllers[npad_id].is_connected = true; 576 }
577
578 connected_controllers[npad_id] = {controller, true};
579 InitNewlyAddedControler(npad_id);
580}
581
582void Controller_NPad::ConnectNPad(u32 npad_id) {
583 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
458} 584}
459 585
460void Controller_NPad::DisconnectNPad(u32 npad_id) { 586void Controller_NPad::DisconnectNPad(u32 npad_id) {
461 if (npad_id >= connected_controllers.size()) 587 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
462 return; 588}
463 connected_controllers[npad_id].is_connected = false; 589
590bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
591 if (controller == NPadControllerType::Handheld) {
592 // Handheld is not even a supported type, lets stop here
593 if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
594 NPAD_HANDHELD) == supported_npad_id_types.end()) {
595 return false;
596 }
597 // Handheld should not be supported in docked mode
598 if (Settings::values.use_docked_mode) {
599 return false;
600 }
601 }
602 switch (controller) {
603 case NPadControllerType::ProController:
604 return style.pro_controller;
605 case NPadControllerType::Handheld:
606 return style.handheld;
607 case NPadControllerType::JoyDual:
608 return style.joycon_dual;
609 case NPadControllerType::JoyLeft:
610 return style.joycon_left;
611 case NPadControllerType::JoyRight:
612 return style.joycon_right;
613 case NPadControllerType::Pokeball:
614 return style.pokeball;
615 default:
616 return false;
617 }
464} 618}
465 619
466Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 620Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
@@ -494,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
494 can_controllers_vibrate = can_vibrate; 648 can_controllers_vibrate = can_vibrate;
495} 649}
496 650
651void Controller_NPad::ClearAllConnectedControllers() {
652 for (auto& controller : connected_controllers) {
653 if (controller.is_connected && controller.type != NPadControllerType::None) {
654 controller.type = NPadControllerType::None;
655 controller.is_connected = false;
656 }
657 }
658}
659void Controller_NPad::DisconnectAllConnectedControllers() {
660 std::for_each(connected_controllers.begin(), connected_controllers.end(),
661 [](ControllerHolder& controller) { controller.is_connected = false; });
662}
663
664void Controller_NPad::ConnectAllDisconnectedControllers() {
665 std::for_each(connected_controllers.begin(), connected_controllers.end(),
666 [](ControllerHolder& controller) {
667 if (controller.type != NPadControllerType::None && !controller.is_connected) {
668 controller.is_connected = false;
669 }
670 });
671}
672
673void Controller_NPad::ClearAllControllers() {
674 std::for_each(connected_controllers.begin(), connected_controllers.end(),
675 [](ControllerHolder& controller) {
676 controller.type = NPadControllerType::None;
677 controller.is_connected = false;
678 });
679}
680
497bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 681bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
498 const bool support_handheld = 682 const bool support_handheld =
499 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != 683 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ac86985ff..ea8057b80 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -5,13 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/frontend/input.h" 10#include "core/frontend/input.h"
11#include "core/hle/kernel/event.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h" 13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14 16
17constexpr u32 NPAD_HANDHELD = 32;
18constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
19
15class Controller_NPad final : public ControllerBase { 20class Controller_NPad final : public ControllerBase {
16public: 21public:
17 Controller_NPad(); 22 Controller_NPad();
@@ -107,11 +112,19 @@ public:
107 Vibration GetLastVibration() const; 112 Vibration GetLastVibration() const;
108 113
109 void AddNewController(NPadControllerType controller); 114 void AddNewController(NPadControllerType controller);
115 void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
110 116
111 void ConnectNPad(u32 npad_id); 117 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id); 118 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id); 119 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate); 120 void SetVibrationEnabled(bool can_vibrate);
121 void ClearAllConnectedControllers();
122 void DisconnectAllConnectedControllers();
123 void ConnectAllDisconnectedControllers();
124 void ClearAllControllers();
125
126 static std::size_t NPadIdToIndex(u32 npad_id);
127 static u32 IndexToNPad(std::size_t index);
115 128
116private: 129private:
117 struct CommonHeader { 130 struct CommonHeader {
@@ -164,8 +177,11 @@ private:
164 BitField<23, 1, u64_le> r_stick_down; 177 BitField<23, 1, u64_le> r_stick_down;
165 178
166 // Not always active? 179 // Not always active?
167 BitField<24, 1, u64_le> sl; 180 BitField<24, 1, u64_le> left_sl;
168 BitField<25, 1, u64_le> sr; 181 BitField<25, 1, u64_le> left_sr;
182
183 BitField<26, 1, u64_le> right_sl;
184 BitField<27, 1, u64_le> right_sr;
169 }; 185 };
170 }; 186 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); 187 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +205,17 @@ private:
189 }; 205 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 206 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191 207
192 struct GenericStates { 208 struct ControllerPad {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states; 209 ControllerPadState pad_states;
196 AnalogPosition l_stick; 210 AnalogPosition l_stick;
197 AnalogPosition r_stick; 211 AnalogPosition r_stick;
212 };
213 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
214
215 struct GenericStates {
216 s64_le timestamp;
217 s64_le timestamp2;
218 ControllerPad pad;
198 ConnectionState connection_status; 219 ConnectionState connection_status;
199 }; 220 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 221 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,15 +287,20 @@ private:
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 287 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267 288
268 struct ControllerHolder { 289 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type; 290 NPadControllerType type;
270 bool is_connected; 291 bool is_connected;
271 }; 292 };
272 293
273 NPadType style{}; 294 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{}; 295 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 296 std::array<
297 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
298 10>
276 buttons; 299 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; 300 std::array<
301 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
302 10>
303 sticks;
278 std::vector<u32> supported_npad_id_types{}; 304 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical}; 305 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event; 306 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
@@ -285,5 +311,8 @@ private:
285 void InitNewlyAddedControler(std::size_t controller_idx); 311 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const; 312 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const; 313 NPadControllerType DecideBestController(NPadControllerType priority) const;
314 void RequestPadStateUpdate(u32 npad_id);
315 std::array<ControllerPad, 10> npad_pad_states{};
316 bool IsControllerSupported(NPadControllerType controller);
288}; 317};
289} // namespace Service::HID 318} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 43efef803..f666b1bd8 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
41 41
42 const auto [x, y, pressed] = touch_device->GetStatus(); 42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0]; 43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) { 44 touch_entry.attribute.raw = 0;
45 if (pressed && Settings::values.touchscreen.enabled) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); 46 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); 47 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15; 48 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
48 touch_entry.diameter_y = 15; 49 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
49 touch_entry.rotation_angle = 0; 50 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
50 const u64 tick = CoreTiming::GetTicks(); 51 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch; 52 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick; 53 last_touch = tick;
53 touch_entry.finger = 0; 54 touch_entry.finger = Settings::values.touchscreen.finger;
54 cur_entry.entry_count = 1; 55 cur_entry.entry_count = 1;
55 } else { 56 } else {
56 cur_entry.entry_count = 0; 57 cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
60} 61}
61 62
62void Controller_Touchscreen::OnLoadInputDevices() { 63void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); 64 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
64} 65}
65} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index e5db6e6ba..94cd0eba9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
7#include "common/common_funcs.h" 8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
29 void OnLoadInputDevices() override; 30 void OnLoadInputDevices() override;
30 31
31private: 32private:
33 struct Attributes {
34 union {
35 u32 raw{};
36 BitField<0, 1, u32_le> start_touch;
37 BitField<1, 1, u32_le> end_touch;
38 };
39 };
40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
41
32 struct TouchState { 42 struct TouchState {
33 u64_le delta_time; 43 u64_le delta_time;
34 u32_le attribute; 44 Attributes attribute;
35 u32_le finger; 45 u32_le finger;
36 u32_le x; 46 u32_le x;
37 u32_le y; 47 u32_le y;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a9aa9ec78..7c0dac5dc 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -34,8 +34,8 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// TODO(shinyquagsire23): These need better values. 37// TODO(ogniK): Find actual polling rate of hid
38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
@@ -96,6 +96,8 @@ public:
96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
97 97
98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
99
100 ReloadInputDevices();
99 } 101 }
100 102
101 void ActivateController(HidController controller) { 103 void ActivateController(HidController controller) {
@@ -284,10 +286,10 @@ public:
284 {519, nullptr, "GetPalmaOperationResult"}, 286 {519, nullptr, "GetPalmaOperationResult"},
285 {520, nullptr, "ReadPalmaPlayLog"}, 287 {520, nullptr, "ReadPalmaPlayLog"},
286 {521, nullptr, "ResetPalmaPlayLog"}, 288 {521, nullptr, "ResetPalmaPlayLog"},
287 {522, nullptr, "SetIsPalmaAllConnectable"}, 289 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
288 {523, nullptr, "SetIsPalmaPairedConnectable"}, 290 {523, nullptr, "SetIsPalmaPairedConnectable"},
289 {524, nullptr, "PairPalma"}, 291 {524, nullptr, "PairPalma"},
290 {525, nullptr, "SetPalmaBoostMode"}, 292 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
291 {1000, nullptr, "SetNpadCommunicationMode"}, 293 {1000, nullptr, "SetNpadCommunicationMode"},
292 {1001, nullptr, "GetNpadCommunicationMode"}, 294 {1001, nullptr, "GetNpadCommunicationMode"},
293 }; 295 };
@@ -594,6 +596,18 @@ private:
594 rb.Push(RESULT_SUCCESS); 596 rb.Push(RESULT_SUCCESS);
595 LOG_WARNING(Service_HID, "(STUBBED) called"); 597 LOG_WARNING(Service_HID, "(STUBBED) called");
596 } 598 }
599
600 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
601 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(RESULT_SUCCESS);
603 LOG_WARNING(Service_HID, "(STUBBED) called");
604 }
605
606 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
607 IPC::ResponseBuilder rb{ctx, 2};
608 rb.Push(RESULT_SUCCESS);
609 LOG_WARNING(Service_HID, "(STUBBED) called");
610 }
597}; 611};
598 612
599class HidDbg final : public ServiceFramework<HidDbg> { 613class HidDbg final : public ServiceFramework<HidDbg> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d607d985e..7a9d0d0dd 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -4,7 +4,10 @@
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h> 6#include <fmt/format.h>
7#include <mbedtls/sha256.h>
7 8
9#include "common/alignment.h"
10#include "common/hex_util.h"
8#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
10#include "core/hle/service/ldr/ldr.h" 13#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,21 @@
13 16
14namespace Service::LDR { 17namespace Service::LDR {
15 18
19constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
20constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
21constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
22constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
23constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
24constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
25constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
26constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
27constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
28constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
29constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
30constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
31
32constexpr u64 MAXIMUM_LOADED_RO = 0x40;
33
16class DebugMonitor final : public ServiceFramework<DebugMonitor> { 34class DebugMonitor final : public ServiceFramework<DebugMonitor> {
17public: 35public:
18 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { 36 explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +82,9 @@ public:
64 // clang-format off 82 // clang-format off
65 static const FunctionInfo functions[] = { 83 static const FunctionInfo functions[] = {
66 {0, &RelocatableObject::LoadNro, "LoadNro"}, 84 {0, &RelocatableObject::LoadNro, "LoadNro"},
67 {1, nullptr, "UnloadNro"}, 85 {1, &RelocatableObject::UnloadNro, "UnloadNro"},
68 {2, &RelocatableObject::LoadNrr, "LoadNrr"}, 86 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
69 {3, nullptr, "UnloadNrr"}, 87 {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
70 {4, &RelocatableObject::Initialize, "Initialize"}, 88 {4, &RelocatableObject::Initialize, "Initialize"},
71 }; 89 };
72 // clang-format on 90 // clang-format on
@@ -75,9 +93,123 @@ public:
75 } 93 }
76 94
77 void LoadNrr(Kernel::HLERequestContext& ctx) { 95 void LoadNrr(Kernel::HLERequestContext& ctx) {
96 IPC::RequestParser rp{ctx};
97 rp.Skip(2, false);
98 const VAddr nrr_addr{rp.Pop<VAddr>()};
99 const u64 nrr_size{rp.Pop<u64>()};
100
101 if (!initialized) {
102 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(ERROR_NOT_INITIALIZED);
105 return;
106 }
107
108 if (nrr.size() >= MAXIMUM_LOADED_RO) {
109 LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
110 "(0x40)! Failing...");
111 IPC::ResponseBuilder rb{ctx, 2};
112 rb.Push(ERROR_MAXIMUM_NRR);
113 return;
114 }
115
116 // NRR Address does not fall on 0x1000 byte boundary
117 if (!Common::Is4KBAligned(nrr_addr)) {
118 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
119 IPC::ResponseBuilder rb{ctx, 2};
120 rb.Push(ERROR_INVALID_ALIGNMENT);
121 return;
122 }
123
124 // NRR Size is zero or causes overflow
125 if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
126 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
127 nrr_addr, nrr_size);
128 IPC::ResponseBuilder rb{ctx, 2};
129 rb.Push(ERROR_INVALID_SIZE);
130 return;
131 }
132 // Read NRR data from memory
133 std::vector<u8> nrr_data(nrr_size);
134 Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
135 NRRHeader header;
136 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
137
138 if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
139 LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
140 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(ERROR_INVALID_NRR);
142 return;
143 }
144
145 if (header.size != nrr_size) {
146 LOG_ERROR(Service_LDR,
147 "NRR header reported size did not match LoadNrr parameter size! "
148 "(header_size={:016X}, loadnrr_size={:016X})",
149 header.size, nrr_size);
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(ERROR_INVALID_SIZE);
152 return;
153 }
154
155 if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
156 LOG_ERROR(Service_LDR,
157 "Attempting to load NRR with title ID other than current process. (actual "
158 "{:016X})!",
159 header.title_id);
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(ERROR_INVALID_NRR);
162 return;
163 }
164
165 std::vector<SHA256Hash> hashes;
166
167 // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
168 for (std::size_t i = header.hash_offset;
169 i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
170 SHA256Hash hash;
171 std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
172 hashes.emplace_back(hash);
173 }
174
175 nrr.insert_or_assign(nrr_addr, std::move(hashes));
176
177 IPC::ResponseBuilder rb{ctx, 2};
178 rb.Push(RESULT_SUCCESS);
179 }
180
181 void UnloadNrr(Kernel::HLERequestContext& ctx) {
182 if (!initialized) {
183 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(ERROR_NOT_INITIALIZED);
186 return;
187 }
188
189 IPC::RequestParser rp{ctx};
190 rp.Skip(2, false);
191 const auto nrr_addr{rp.Pop<VAddr>()};
192
193 if (!Common::Is4KBAligned(nrr_addr)) {
194 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
195 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ERROR_INVALID_ALIGNMENT);
197 return;
198 }
199
200 const auto iter = nrr.find(nrr_addr);
201 if (iter == nrr.end()) {
202 LOG_ERROR(Service_LDR,
203 "Attempting to unload NRR which has not been loaded! (addr={:016X})",
204 nrr_addr);
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(ERROR_INVALID_NRR_ADDRESS);
207 return;
208 }
209
210 nrr.erase(iter);
78 IPC::ResponseBuilder rb{ctx, 2}; 211 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS); 212 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 } 213 }
82 214
83 void LoadNro(Kernel::HLERequestContext& ctx) { 215 void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +220,253 @@ public:
88 const VAddr bss_addr{rp.Pop<VAddr>()}; 220 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()}; 221 const u64 bss_size{rp.Pop<u64>()};
90 222
223 if (!initialized) {
224 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
225 IPC::ResponseBuilder rb{ctx, 2};
226 rb.Push(ERROR_NOT_INITIALIZED);
227 return;
228 }
229
230 if (nro.size() >= MAXIMUM_LOADED_RO) {
231 LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
232 "(0x40)! Failing...");
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_MAXIMUM_NRO);
235 return;
236 }
237
238 // NRO Address does not fall on 0x1000 byte boundary
239 if (!Common::Is4KBAligned(nro_addr)) {
240 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
241 IPC::ResponseBuilder rb{ctx, 2};
242 rb.Push(ERROR_INVALID_ALIGNMENT);
243 return;
244 }
245
246 // NRO Size or BSS Size is zero or causes overflow
247 const auto nro_size_valid =
248 nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
249 const auto bss_size_valid =
250 nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
251
252 if (!nro_size_valid || !bss_size_valid) {
253 LOG_ERROR(Service_LDR,
254 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
255 "bss_address={:016X}, bss_size={:016X})",
256 nro_addr, nro_size, bss_addr, bss_size);
257 IPC::ResponseBuilder rb{ctx, 2};
258 rb.Push(ERROR_INVALID_SIZE);
259 return;
260 }
261
91 // Read NRO data from memory 262 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size); 263 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); 264 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94 265
266 SHA256Hash hash{};
267 mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
268
269 // NRO Hash is already loaded
270 if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
271 return info.second.hash == hash;
272 })) {
273 LOG_ERROR(Service_LDR, "NRO is already loaded!");
274 IPC::ResponseBuilder rb{ctx, 2};
275 rb.Push(ERROR_ALREADY_LOADED);
276 return;
277 }
278
279 // NRO Hash is not in any loaded NRR
280 if (!IsValidNROHash(hash)) {
281 LOG_ERROR(Service_LDR,
282 "NRO hash is not present in any currently loaded NRRs (hash={})!",
283 Common::HexArrayToString(hash));
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ERROR_MISSING_NRR_HASH);
286 return;
287 }
288
289 NROHeader header;
290 std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
291
292 if (!IsValidNRO(header, nro_size, bss_size)) {
293 LOG_ERROR(Service_LDR, "NRO was invalid!");
294 IPC::ResponseBuilder rb{ctx, 2};
295 rb.Push(ERROR_INVALID_NRO);
296 return;
297 }
298
95 // Load NRO as new executable module 299 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; 300 auto* process = Core::CurrentProcess();
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); 301 auto& vm_manager = process->VMManager();
302 auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
303
304 if (!map_address.Succeeded() ||
305 *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
306
307 LOG_ERROR(Service_LDR,
308 "General error while allocation memory or no available memory to allocate!");
309 IPC::ResponseBuilder rb{ctx, 2};
310 rb.Push(ERROR_INVALID_MEMORY_STATE);
311 return;
312 }
313
314 ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
315 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
316 ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
98 317
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. 318 if (bss_size > 0) {
100 // It is currently missing: 319 ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
101 // - Signature checks with LoadNRR 320 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
102 // - Checking if a module has already been loaded 321 ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
103 // - Using/validating BSS, etc. params (these are used from NRO header instead) 322 }
104 // - Error checking 323
105 // - ...Probably other things 324 vm_manager.ReprotectRange(*map_address, header.text_size,
325 Kernel::VMAPermission::ReadExecute);
326 vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
327 Kernel::VMAPermission::Read);
328 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
329 Kernel::VMAPermission::ReadWrite);
330
331 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
332 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
333 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
334 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
335
336 nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
106 337
107 IPC::ResponseBuilder rb{ctx, 4}; 338 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS); 339 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr); 340 rb.Push(*map_address);
110 LOG_WARNING(Service_LDR, "(STUBBED) called"); 341 }
342
343 void UnloadNro(Kernel::HLERequestContext& ctx) {
344 IPC::RequestParser rp{ctx};
345 rp.Skip(2, false);
346 const VAddr mapped_addr{rp.PopRaw<VAddr>()};
347 const VAddr heap_addr{rp.PopRaw<VAddr>()};
348
349 if (!initialized) {
350 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
351 IPC::ResponseBuilder rb{ctx, 2};
352 rb.Push(ERROR_NOT_INITIALIZED);
353 return;
354 }
355
356 if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
357 LOG_ERROR(Service_LDR,
358 "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
359 "bss_addr={:016X})!",
360 mapped_addr, heap_addr);
361 IPC::ResponseBuilder rb{ctx, 2};
362 rb.Push(ERROR_INVALID_ALIGNMENT);
363 return;
364 }
365
366 const auto iter = nro.find(mapped_addr);
367 if (iter == nro.end()) {
368 LOG_ERROR(Service_LDR,
369 "The NRO attempting to unmap was not mapped or has an invalid address "
370 "(actual {:016X})!",
371 mapped_addr);
372 IPC::ResponseBuilder rb{ctx, 2};
373 rb.Push(ERROR_INVALID_NRO_ADDRESS);
374 return;
375 }
376
377 auto* process = Core::CurrentProcess();
378 auto& vm_manager = process->VMManager();
379 const auto& nro_size = iter->second.size;
380
381 ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
382 Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
383 ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
384
385 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
386 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
387 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
388 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
389
390 nro.erase(iter);
391 IPC::ResponseBuilder rb{ctx, 2};
392 rb.Push(RESULT_SUCCESS);
111 } 393 }
112 394
113 void Initialize(Kernel::HLERequestContext& ctx) { 395 void Initialize(Kernel::HLERequestContext& ctx) {
396 initialized = true;
397
114 IPC::ResponseBuilder rb{ctx, 2}; 398 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS); 399 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called"); 400 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 } 401 }
402
403private:
404 using SHA256Hash = std::array<u8, 0x20>;
405
406 struct NROHeader {
407 u32_le entrypoint_insn;
408 u32_le mod_offset;
409 INSERT_PADDING_WORDS(2);
410 u32_le magic;
411 INSERT_PADDING_WORDS(1);
412 u32_le nro_size;
413 INSERT_PADDING_WORDS(1);
414 u32_le text_offset;
415 u32_le text_size;
416 u32_le ro_offset;
417 u32_le ro_size;
418 u32_le rw_offset;
419 u32_le rw_size;
420 u32_le bss_size;
421 INSERT_PADDING_WORDS(1);
422 std::array<u8, 0x20> build_id;
423 INSERT_PADDING_BYTES(0x20);
424 };
425 static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
426
427 struct NRRHeader {
428 u32_le magic;
429 INSERT_PADDING_BYTES(0x1C);
430 u64_le title_id_mask;
431 u64_le title_id_pattern;
432 std::array<u8, 0x100> modulus;
433 std::array<u8, 0x100> signature_1;
434 std::array<u8, 0x100> signature_2;
435 u64_le title_id;
436 u32_le size;
437 INSERT_PADDING_BYTES(4);
438 u32_le hash_offset;
439 u32_le hash_count;
440 INSERT_PADDING_BYTES(8);
441 };
442 static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
443
444 struct NROInfo {
445 SHA256Hash hash;
446 u64 size;
447 };
448
449 bool initialized = false;
450
451 std::map<VAddr, NROInfo> nro;
452 std::map<VAddr, std::vector<SHA256Hash>> nrr;
453
454 bool IsValidNROHash(const SHA256Hash& hash) {
455 return std::any_of(
456 nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
457 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
458 });
459 }
460
461 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
462 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
463 header.nro_size == nro_size && header.bss_size == bss_size &&
464 header.ro_offset == header.text_offset + header.text_size &&
465 header.rw_offset == header.ro_offset + header.ro_size &&
466 nro_size == header.rw_offset + header.rw_size &&
467 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
468 Common::Is4KBAligned(header.rw_size);
469 }
118}; 470};
119 471
120void InstallInterfaces(SM::ServiceManager& sm) { 472void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index c89157a4d..4e5fdb16e 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -18,7 +18,7 @@ public:
18 ILogger() : ServiceFramework("ILogger") { 18 ILogger() : ServiceFramework("ILogger") {
19 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
20 {0x00000000, &ILogger::Initialize, "Initialize"}, 20 {0x00000000, &ILogger::Initialize, "Initialize"},
21 {0x00000001, nullptr, "SetDestination"}, 21 {0x00000001, &ILogger::SetDestination, "SetDestination"},
22 }; 22 };
23 RegisterHandlers(functions); 23 RegisterHandlers(functions);
24 } 24 }
@@ -178,6 +178,17 @@ private:
178 } 178 }
179 } 179 }
180 180
181 // This service function is intended to be used as a way to
182 // redirect logging output to different destinations, however,
183 // given we always want to see the logging output, it's sufficient
184 // to do nothing and return success here.
185 void SetDestination(Kernel::HLERequestContext& ctx) {
186 LOG_DEBUG(Service_LM, "called");
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(RESULT_SUCCESS);
190 }
191
181 std::ostringstream log_stream; 192 std::ostringstream log_stream;
182}; 193};
183 194
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index c1af878fe..1d6e7756f 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -212,7 +212,7 @@ private:
212 IPC::ResponseBuilder rb{ctx, 2}; 212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer(); 213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{}; 214 TagInfo tag_info{};
215 std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); 215 tag_info.uuid = amiibo.uuid;
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217 217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 07c1381fe..1d2978f24 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,6 +2,9 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h"
5#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
7#include "core/hle/service/ns/ns.h" 10#include "core/hle/service/ns/ns.h"
@@ -118,7 +121,7 @@ public:
118 {305, nullptr, "TerminateSystemApplet"}, 121 {305, nullptr, "TerminateSystemApplet"},
119 {306, nullptr, "LaunchOverlayApplet"}, 122 {306, nullptr, "LaunchOverlayApplet"},
120 {307, nullptr, "TerminateOverlayApplet"}, 123 {307, nullptr, "TerminateOverlayApplet"},
121 {400, nullptr, "GetApplicationControlData"}, 124 {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
122 {401, nullptr, "InvalidateAllApplicationControlCache"}, 125 {401, nullptr, "InvalidateAllApplicationControlCache"},
123 {402, nullptr, "RequestDownloadApplicationControlData"}, 126 {402, nullptr, "RequestDownloadApplicationControlData"},
124 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 127 {403, nullptr, "GetMaxApplicationControlCacheCount"},
@@ -243,6 +246,65 @@ public:
243 246
244 RegisterHandlers(functions); 247 RegisterHandlers(functions);
245 } 248 }
249
250 void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
251 IPC::RequestParser rp{ctx};
252 const auto flag = rp.PopRaw<u64>();
253 LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
254
255 const auto title_id = rp.PopRaw<u64>();
256
257 const auto size = ctx.GetWriteBufferSize();
258
259 const FileSys::PatchManager pm{title_id};
260 const auto control = pm.GetControlMetadata();
261
262 std::vector<u8> out;
263
264 if (control.first != nullptr) {
265 if (size < 0x4000) {
266 LOG_ERROR(Service_NS,
267 "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
268 size);
269 IPC::ResponseBuilder rb{ctx, 2};
270 // TODO(DarkLordZach): Find a better error code for this.
271 rb.Push(ResultCode(-1));
272 return;
273 }
274
275 out.resize(0x4000);
276 const auto bytes = control.first->GetRawBytes();
277 std::memcpy(out.data(), bytes.data(), bytes.size());
278 } else {
279 LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
280 title_id);
281 out.resize(std::min<u64>(0x4000, size));
282 }
283
284 if (control.second != nullptr) {
285 if (size < 0x4000 + control.second->GetSize()) {
286 LOG_ERROR(Service_NS,
287 "output buffer is too small! (actual={:016X}, expected_min={:016X})",
288 size, 0x4000 + control.second->GetSize());
289 IPC::ResponseBuilder rb{ctx, 2};
290 // TODO(DarkLordZach): Find a better error code for this.
291 rb.Push(ResultCode(-1));
292 return;
293 }
294
295 out.resize(0x4000 + control.second->GetSize());
296 control.second->Read(out.data() + 0x4000, control.second->GetSize());
297 } else {
298 LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
299 title_id);
300 }
301
302 ctx.WriteBuffer(out);
303
304 IPC::ResponseBuilder rb{ctx, 3};
305 rb.Push(RESULT_SUCCESS);
306 rb.Push<u32>(static_cast<u32>(out.size()));
307 }
246}; 308};
247 309
248class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { 310class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 44accecb7..1066bf505 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
351 font_sizes.push_back(region.size); 351 font_sizes.push_back(region.size);
352 } 352 }
353 353
354 // Resize buffers if game requests smaller size output.
355 font_codes.resize(
356 std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
357 font_offsets.resize(
358 std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
359 font_sizes.resize(
360 std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
361
354 ctx.WriteBuffer(font_codes, 0); 362 ctx.WriteBuffer(font_codes, 0);
355 ctx.WriteBuffer(font_offsets, 1); 363 ctx.WriteBuffer(font_offsets, 1);
356 ctx.WriteBuffer(font_sizes, 2); 364 ctx.WriteBuffer(font_sizes, 2);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 7a88ae029..792d26e52 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -5,6 +5,8 @@
5#include <cstring> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
9 11
10namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
@@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
33 return ZBCQueryTable(input, output); 35 return ZBCQueryTable(input, output);
34 case IoctlCommand::IocFlushL2: 36 case IoctlCommand::IocFlushL2:
35 return FlushL2(input, output); 37 return FlushL2(input, output);
38 case IoctlCommand::IocGetGpuTime:
39 return GetGpuTime(input, output);
36 } 40 }
37 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 41 UNIMPLEMENTED_MSG("Unimplemented ioctl");
38 return 0; 42 return 0;
@@ -169,4 +173,13 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp
169 return 0; 173 return 0;
170} 174}
171 175
176u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
177 LOG_DEBUG(Service_NVDRV, "called");
178 IoctlGetGpuTime params{};
179 std::memcpy(&params, input.data(), input.size());
180 params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
181 std::memcpy(output.data(), &params, output.size());
182 return 0;
183}
184
172} // namespace Service::Nvidia::Devices 185} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 3bbf028ad..240435eea 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -156,6 +156,11 @@ private:
156 }; 156 };
157 static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size"); 157 static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
158 158
159 struct IoctlGetGpuTime {
160 u64_le gpu_time;
161 };
162 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
163
159 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 164 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
160 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 165 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
161 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
@@ -164,6 +169,7 @@ private:
164 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output); 169 u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
165 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 170 u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
166 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 171 u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
167}; 173};
168 174
169} // namespace Service::Nvidia::Devices 175} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 2fe81a560..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -58,9 +58,9 @@ public:
58 /// Rotate source image 90 degrees clockwise 58 /// Rotate source image 90 degrees clockwise
59 Rotate90 = 0x04, 59 Rotate90 = 0x04,
60 /// Rotate source image 180 degrees 60 /// Rotate source image 180 degrees
61 Roate180 = 0x03, 61 Rotate180 = 0x03,
62 /// Rotate source image 270 degrees clockwise 62 /// Rotate source image 270 degrees clockwise
63 Roate270 = 0x07, 63 Rotate270 = 0x07,
64 }; 64 };
65 65
66 struct Buffer { 66 struct Buffer {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a4cf45267..1ec340466 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -80,8 +80,8 @@ namespace Service {
80 * Creates a function string for logging, complete with the name (or header code, depending 80 * Creates a function string for logging, complete with the name (or header code, depending
81 * on what's passed in) the port name, and all the cmd_buff arguments. 81 * on what's passed in) the port name, and all the cmd_buff arguments.
82 */ 82 */
83static std::string MakeFunctionString(const char* name, const char* port_name, 83[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
84 const u32* cmd_buff) { 84 const u32* cmd_buff) {
85 // Number of params == bits 0-5 + bits 6-11 85 // Number of params == bits 0-5 + bits 6-11
86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); 86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
87 87
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 44a6717d0..b2de2a818 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -3,18 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono>
6#include <cstdlib> 7#include <cstdlib>
8#include <ctime>
9#include <functional>
7#include <vector> 10#include <vector>
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/spl/csrng.h" 13#include "core/hle/service/spl/csrng.h"
11#include "core/hle/service/spl/module.h" 14#include "core/hle/service/spl/module.h"
12#include "core/hle/service/spl/spl.h" 15#include "core/hle/service/spl/spl.h"
16#include "core/settings.h"
13 17
14namespace Service::SPL { 18namespace Service::SPL {
15 19
16Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
17 : ServiceFramework(name), module(std::move(module)) {} 21 : ServiceFramework(name), module(std::move(module)),
22 rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
18 23
19Module::Interface::~Interface() = default; 24Module::Interface::~Interface() = default;
20 25
@@ -23,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
23 28
24 std::size_t size = ctx.GetWriteBufferSize(); 29 std::size_t size = ctx.GetWriteBufferSize();
25 30
31 std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
26 std::vector<u8> data(size); 32 std::vector<u8> data(size);
27 std::generate(data.begin(), data.end(), std::rand); 33 std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
28 34
29 ctx.WriteBuffer(data); 35 ctx.WriteBuffer(data);
30 36
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index 48fda6099..afa1f0295 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <random>
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::SPL { 10namespace Service::SPL {
@@ -19,6 +20,9 @@ public:
19 20
20 protected: 21 protected:
21 std::shared_ptr<Module> module; 22 std::shared_ptr<Module> module;
23
24 private:
25 std::mt19937 rng;
22 }; 26 };
23}; 27};
24 28
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 18a5d71d5..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
21 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 21 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, nullptr, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
27 "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 28 {501, nullptr, "CalculateSpanBetween"},
28 }; 29 };
29 RegisterHandlers(functions); 30 RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 28fd8debc..e561a0c52 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -15,6 +15,44 @@
15 15
16namespace Service::Time { 16namespace Service::Time {
17 17
18static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
19 CalendarAdditionalInfo& additional_info,
20 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
21 const std::time_t time(posix_time);
22 const std::tm* tm = std::localtime(&time);
23 if (tm == nullptr) {
24 calendar_time = {};
25 additional_info = {};
26 return;
27 }
28 calendar_time.year = tm->tm_year + 1900;
29 calendar_time.month = tm->tm_mon + 1;
30 calendar_time.day = tm->tm_mday;
31 calendar_time.hour = tm->tm_hour;
32 calendar_time.minute = tm->tm_min;
33 calendar_time.second = tm->tm_sec;
34
35 additional_info.day_of_week = tm->tm_wday;
36 additional_info.day_of_year = tm->tm_yday;
37 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
38 additional_info.utc_offset = 0;
39}
40
41static u64 CalendarToPosix(const CalendarTime& calendar_time,
42 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
43 std::tm time{};
44 time.tm_year = calendar_time.year - 1900;
45 time.tm_mon = calendar_time.month - 1;
46 time.tm_mday = calendar_time.day;
47
48 time.tm_hour = calendar_time.hour;
49 time.tm_min = calendar_time.minute;
50 time.tm_sec = calendar_time.second;
51
52 std::time_t epoch_time = std::mktime(&time);
53 return static_cast<u64>(epoch_time);
54}
55
18class ISystemClock final : public ServiceFramework<ISystemClock> { 56class ISystemClock final : public ServiceFramework<ISystemClock> {
19public: 57public:
20 ISystemClock() : ServiceFramework("ISystemClock") { 58 ISystemClock() : ServiceFramework("ISystemClock") {
@@ -80,8 +118,8 @@ public:
80 {5, nullptr, "GetTimeZoneRuleVersion"}, 118 {5, nullptr, "GetTimeZoneRuleVersion"},
81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 119 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 120 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
83 {201, nullptr, "ToPosixTime"}, 121 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
84 {202, nullptr, "ToPosixTimeWithMyRule"}, 122 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
85 }; 123 };
86 RegisterHandlers(functions); 124 RegisterHandlers(functions);
87 } 125 }
@@ -151,24 +189,29 @@ private:
151 rb.PushRaw(additional_info); 189 rb.PushRaw(additional_info);
152 } 190 }
153 191
154 void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, 192 void ToPosixTime(Kernel::HLERequestContext& ctx) {
155 CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { 193 // TODO(ogniK): Figure out how to handle multiple times
156 std::time_t t(posix_time); 194 LOG_WARNING(Service_Time, "(STUBBED) called");
157 std::tm* tm = std::localtime(&t); 195 IPC::RequestParser rp{ctx};
158 if (!tm) { 196 auto calendar_time = rp.PopRaw<CalendarTime>();
159 return; 197 auto posix_time = CalendarToPosix(calendar_time, {});
160 } 198
161 calendar_time.year = tm->tm_year + 1900; 199 IPC::ResponseBuilder rb{ctx, 3};
162 calendar_time.month = tm->tm_mon + 1; 200 rb.Push(RESULT_SUCCESS);
163 calendar_time.day = tm->tm_mday; 201 rb.PushRaw<u32>(1); // Amount of times we're returning
164 calendar_time.hour = tm->tm_hour; 202 ctx.WriteBuffer(&posix_time, sizeof(u64));
165 calendar_time.minute = tm->tm_min; 203 }
166 calendar_time.second = tm->tm_sec; 204
167 205 void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
168 additional_info.day_of_week = tm->tm_wday; 206 LOG_WARNING(Service_Time, "(STUBBED) called");
169 additional_info.day_of_year = tm->tm_yday; 207 IPC::RequestParser rp{ctx};
170 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); 208 auto calendar_time = rp.PopRaw<CalendarTime>();
171 additional_info.utc_offset = 0; 209 auto posix_time = CalendarToPosix(calendar_time, {});
210
211 IPC::ResponseBuilder rb{ctx, 3};
212 rb.Push(RESULT_SUCCESS);
213 rb.PushRaw<u32>(1); // Amount of times we're returning
214 ctx.WriteBuffer(&posix_time, sizeof(u64));
172 } 215 }
173}; 216};
174 217
@@ -207,6 +250,70 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
207 LOG_DEBUG(Service_Time, "called"); 250 LOG_DEBUG(Service_Time, "called");
208} 251}
209 252
253void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
254 LOG_DEBUG(Service_Time, "called");
255
256 IPC::RequestParser rp{ctx};
257 auto unknown_u8 = rp.PopRaw<u8>();
258
259 ClockSnapshot clock_snapshot{};
260
261 const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
262 std::chrono::system_clock::now().time_since_epoch())
263 .count()};
264 CalendarTime calendar_time{};
265 const std::time_t time(time_since_epoch);
266 const std::tm* tm = std::localtime(&time);
267 if (tm == nullptr) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
270 return;
271 }
272 SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
273 1000};
274
275 LocationName location_name{"UTC"};
276 calendar_time.year = tm->tm_year + 1900;
277 calendar_time.month = tm->tm_mon + 1;
278 calendar_time.day = tm->tm_mday;
279 calendar_time.hour = tm->tm_hour;
280 calendar_time.minute = tm->tm_min;
281 calendar_time.second = tm->tm_sec;
282 clock_snapshot.system_posix_time = time_since_epoch;
283 clock_snapshot.network_posix_time = time_since_epoch;
284 clock_snapshot.system_calendar_time = calendar_time;
285 clock_snapshot.network_calendar_time = calendar_time;
286
287 CalendarAdditionalInfo additional_info{};
288 PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
289
290 clock_snapshot.system_calendar_info = additional_info;
291 clock_snapshot.network_calendar_info = additional_info;
292
293 clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
294 clock_snapshot.location_name = location_name;
295 clock_snapshot.clock_auto_adjustment_enabled = 1;
296 clock_snapshot.ipc_u8 = unknown_u8;
297 IPC::ResponseBuilder rb{ctx, 2};
298 rb.Push(RESULT_SUCCESS);
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300}
301
302void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
303 Kernel::HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called");
305
306 IPC::RequestParser rp{ctx};
307 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
308 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
309 const u64 difference =
310 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
311
312 IPC::ResponseBuilder rb{ctx, 4};
313 rb.Push(RESULT_SUCCESS);
314 rb.PushRaw<u64>(difference);
315}
316
210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
211 : ServiceFramework(name), time(std::move(time)) {} 318 : ServiceFramework(name), time(std::move(time)) {}
212 319
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 5659ecad3..ea43fbea7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/common_funcs.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
10namespace Service::Time { 11namespace Service::Time {
@@ -53,6 +54,23 @@ struct SystemClockContext {
53static_assert(sizeof(SystemClockContext) == 0x20, 54static_assert(sizeof(SystemClockContext) == 0x20,
54 "SystemClockContext structure has incorrect size"); 55 "SystemClockContext structure has incorrect size");
55 56
57struct ClockSnapshot {
58 SystemClockContext user_clock_context;
59 SystemClockContext network_clock_context;
60 s64_le system_posix_time;
61 s64_le network_posix_time;
62 CalendarTime system_calendar_time;
63 CalendarTime network_calendar_time;
64 CalendarAdditionalInfo system_calendar_info;
65 CalendarAdditionalInfo network_calendar_info;
66 SteadyClockTimePoint steady_clock_timepoint;
67 LocationName location_name;
68 u8 clock_auto_adjustment_enabled;
69 u8 ipc_u8;
70 INSERT_PADDING_BYTES(2);
71};
72static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
73
56class Module final { 74class Module final {
57public: 75public:
58 class Interface : public ServiceFramework<Interface> { 76 class Interface : public ServiceFramework<Interface> {
@@ -65,6 +83,8 @@ public:
65 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); 83 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
66 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
67 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
87 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
68 88
69 protected: 89 protected:
70 std::shared_ptr<Module> time; 90 std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index d764b2406..a72416084 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -237,6 +237,22 @@ private:
237 Data data{}; 237 Data data{};
238}; 238};
239 239
240/// Represents a parcel containing one int '0' as its data
241/// Used by DetachBuffer and Disconnect
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
242 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) 258 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -494,7 +510,11 @@ private:
494 510
495 if (transaction == TransactionId::Connect) { 511 if (transaction == TransactionId::Connect) {
496 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 512 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
497 IGBPConnectResponseParcel response{1280, 720}; 513 IGBPConnectResponseParcel response{
514 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
515 Settings::values.resolution_factor),
516 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
517 Settings::values.resolution_factor)};
498 ctx.WriteBuffer(response.Serialize()); 518 ctx.WriteBuffer(response.Serialize());
499 } else if (transaction == TransactionId::SetPreallocatedBuffer) { 519 } else if (transaction == TransactionId::SetPreallocatedBuffer) {
500 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; 520 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
@@ -554,6 +574,12 @@ private:
554 ctx.WriteBuffer(response.Serialize()); 574 ctx.WriteBuffer(response.Serialize());
555 } else if (transaction == TransactionId::CancelBuffer) { 575 } else if (transaction == TransactionId::CancelBuffer) {
556 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); 576 LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
577 } else if (transaction == TransactionId::Disconnect ||
578 transaction == TransactionId::DetachBuffer) {
579 const auto buffer = ctx.ReadBuffer();
580
581 IGBPEmptyResponseParcel response{};
582 ctx.WriteBuffer(response.Serialize());
557 } else { 583 } else {
558 ASSERT_MSG(false, "Unimplemented"); 584 ASSERT_MSG(false, "Unimplemented");
559 } 585 }
@@ -670,11 +696,15 @@ private:
670 rb.Push(RESULT_SUCCESS); 696 rb.Push(RESULT_SUCCESS);
671 697
672 if (Settings::values.use_docked_mode) { 698 if (Settings::values.use_docked_mode) {
673 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 699 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
674 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 700 static_cast<u32>(Settings::values.resolution_factor));
701 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
702 static_cast<u32>(Settings::values.resolution_factor));
675 } else { 703 } else {
676 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); 704 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
677 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); 705 static_cast<u32>(Settings::values.resolution_factor));
706 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
707 static_cast<u32>(Settings::values.resolution_factor));
678 } 708 }
679 709
680 rb.PushRaw<float>(60.0f); 710 rb.PushRaw<float>(60.0f);
@@ -879,11 +909,15 @@ private:
879 rb.Push(RESULT_SUCCESS); 909 rb.Push(RESULT_SUCCESS);
880 910
881 if (Settings::values.use_docked_mode) { 911 if (Settings::values.use_docked_mode) {
882 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); 912 rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
883 rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); 913 static_cast<u32>(Settings::values.resolution_factor));
914 rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
915 static_cast<u32>(Settings::values.resolution_factor));
884 } else { 916 } else {
885 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); 917 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
886 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); 918 static_cast<u32>(Settings::values.resolution_factor));
919 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
920 static_cast<u32>(Settings::values.resolution_factor));
887 } 921 }
888 } 922 }
889 923
@@ -900,6 +934,8 @@ private:
900 void ListDisplays(Kernel::HLERequestContext& ctx) { 934 void ListDisplays(Kernel::HLERequestContext& ctx) {
901 IPC::RequestParser rp{ctx}; 935 IPC::RequestParser rp{ctx};
902 DisplayInfo display_info; 936 DisplayInfo display_info;
937 display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
938 display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
903 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 939 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
904 IPC::ResponseBuilder rb{ctx, 4}; 940 IPC::ResponseBuilder rb{ctx, 4};
905 rb.Push(RESULT_SUCCESS); 941 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index bc8e402a8..fbbd6b0de 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -12,10 +12,12 @@
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 17#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
20#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/nro.h" 21#include "core/loader/nro.h"
20#include "core/loader/nso.h" 22#include "core/loader/nso.h"
21#include "core/memory.h" 23#include "core/memory.h"
@@ -168,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) {
168 arg_data.size()); 170 arg_data.size());
169 } 171 }
170 172
171 // Read MOD header
172 ModHeader mod_header{};
173 // Default .bss to NRO header bss size if MOD0 section doesn't exist 173 // Default .bss to NRO header bss size if MOD0 section doesn't exist
174 u32 bss_size{PageAlignSize(nro_header.bss_size)}; 174 u32 bss_size{PageAlignSize(nro_header.bss_size)};
175
176 // Read MOD header
177 ModHeader mod_header{};
175 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, 178 std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
176 sizeof(ModHeader)); 179 sizeof(ModHeader));
180
177 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; 181 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
178 if (has_mod_header) { 182 if (has_mod_header) {
179 // Resize program image to include .bss section and page align each section 183 // Resize program image to include .bss section and page align each section
180 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 184 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
181 } 185 }
186
182 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
183 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
184 189
@@ -208,6 +213,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
208 return ResultStatus::ErrorLoadingNRO; 213 return ResultStatus::ErrorLoadingNRO;
209 } 214 }
210 215
216 if (romfs != nullptr)
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
218
211 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 219 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
212 220
213 is_loaded = true; 221 is_loaded = true;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 68efca5c0..aaf006309 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
154 program_image.resize(image_size); 154 program_image.resize(image_size);
155 155
156 // Apply patches if necessary 156 // Apply patches if necessary
157 if (pm && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
158 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0da159559..26fcd3405 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -10,6 +10,56 @@
10 10
11namespace Settings { 11namespace Settings {
12 12
13namespace NativeButton {
14const std::array<const char*, NumButtons> mapping = {{
15 "button_a",
16 "button_b",
17 "button_x",
18 "button_y",
19 "button_lstick",
20 "button_rstick",
21 "button_l",
22 "button_r",
23 "button_zl",
24 "button_zr",
25 "button_plus",
26 "button_minus",
27 "button_dleft",
28 "button_dup",
29 "button_dright",
30 "button_ddown",
31 "button_lstick_left",
32 "button_lstick_up",
33 "button_lstick_right",
34 "button_lstick_down",
35 "button_rstick_left",
36 "button_rstick_up",
37 "button_rstick_right",
38 "button_rstick_down",
39 "button_sl",
40 "button_sr",
41 "button_home",
42 "button_screenshot",
43}};
44}
45
46namespace NativeAnalog {
47const std::array<const char*, NumAnalogs> mapping = {{
48 "lstick",
49 "rstick",
50}};
51}
52
53namespace NativeMouseButton {
54const std::array<const char*, NumMouseButtons> mapping = {{
55 "left",
56 "right",
57 "middle",
58 "forward",
59 "back",
60}};
61}
62
13Values values = {}; 63Values values = {};
14 64
15void Apply() { 65void Apply() {
diff --git a/src/core/settings.h b/src/core/settings.h
index b5aeff29b..a0c5fd447 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <optional>
9#include <string> 10#include <string>
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
@@ -59,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons;
59constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; 60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
60constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; 61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
61 62
62static const std::array<const char*, NumButtons> mapping = {{ 63extern const std::array<const char*, NumButtons> mapping;
63 "button_a",
64 "button_b",
65 "button_x",
66 "button_y",
67 "button_lstick",
68 "button_rstick",
69 "button_l",
70 "button_r",
71 "button_zl",
72 "button_zr",
73 "button_plus",
74 "button_minus",
75 "button_dleft",
76 "button_dup",
77 "button_dright",
78 "button_ddown",
79 "button_lstick_left",
80 "button_lstick_up",
81 "button_lstick_right",
82 "button_lstick_down",
83 "button_rstick_left",
84 "button_rstick_up",
85 "button_rstick_right",
86 "button_rstick_down",
87 "button_sl",
88 "button_sr",
89 "button_home",
90 "button_screenshot",
91}};
92 64
93} // namespace NativeButton 65} // namespace NativeButton
94 66
@@ -104,24 +76,298 @@ constexpr int STICK_HID_BEGIN = LStick;
104constexpr int STICK_HID_END = NumAnalogs; 76constexpr int STICK_HID_END = NumAnalogs;
105constexpr int NUM_STICKS_HID = NumAnalogs; 77constexpr int NUM_STICKS_HID = NumAnalogs;
106 78
107static const std::array<const char*, NumAnalogs> mapping = {{ 79extern const std::array<const char*, NumAnalogs> mapping;
108 "lstick",
109 "rstick",
110}};
111} // namespace NativeAnalog 80} // namespace NativeAnalog
112 81
82namespace NativeMouseButton {
83enum Values {
84 Left,
85 Right,
86 Middle,
87 Forward,
88 Back,
89
90 NumMouseButtons,
91};
92
93constexpr int MOUSE_HID_BEGIN = Left;
94constexpr int MOUSE_HID_END = NumMouseButtons;
95constexpr int NUM_MOUSE_HID = NumMouseButtons;
96
97extern const std::array<const char*, NumMouseButtons> mapping;
98} // namespace NativeMouseButton
99
100namespace NativeKeyboard {
101enum Keys {
102 None,
103 Error,
104
105 A = 4,
106 B,
107 C,
108 D,
109 E,
110 F,
111 G,
112 H,
113 I,
114 J,
115 K,
116 L,
117 M,
118 N,
119 O,
120 P,
121 Q,
122 R,
123 S,
124 T,
125 U,
126 V,
127 W,
128 X,
129 Y,
130 Z,
131 N1,
132 N2,
133 N3,
134 N4,
135 N5,
136 N6,
137 N7,
138 N8,
139 N9,
140 N0,
141 Enter,
142 Escape,
143 Backspace,
144 Tab,
145 Space,
146 Minus,
147 Equal,
148 LeftBrace,
149 RightBrace,
150 Backslash,
151 Tilde,
152 Semicolon,
153 Apostrophe,
154 Grave,
155 Comma,
156 Dot,
157 Slash,
158 CapsLockKey,
159
160 F1,
161 F2,
162 F3,
163 F4,
164 F5,
165 F6,
166 F7,
167 F8,
168 F9,
169 F10,
170 F11,
171 F12,
172
173 SystemRequest,
174 ScrollLockKey,
175 Pause,
176 Insert,
177 Home,
178 PageUp,
179 Delete,
180 End,
181 PageDown,
182 Right,
183 Left,
184 Down,
185 Up,
186
187 NumLockKey,
188 KPSlash,
189 KPAsterisk,
190 KPMinus,
191 KPPlus,
192 KPEnter,
193 KP1,
194 KP2,
195 KP3,
196 KP4,
197 KP5,
198 KP6,
199 KP7,
200 KP8,
201 KP9,
202 KP0,
203 KPDot,
204
205 Key102,
206 Compose,
207 Power,
208 KPEqual,
209
210 F13,
211 F14,
212 F15,
213 F16,
214 F17,
215 F18,
216 F19,
217 F20,
218 F21,
219 F22,
220 F23,
221 F24,
222
223 Open,
224 Help,
225 Properties,
226 Front,
227 Stop,
228 Repeat,
229 Undo,
230 Cut,
231 Copy,
232 Paste,
233 Find,
234 Mute,
235 VolumeUp,
236 VolumeDown,
237 CapsLockActive,
238 NumLockActive,
239 ScrollLockActive,
240 KPComma,
241
242 KPLeftParenthesis,
243 KPRightParenthesis,
244
245 LeftControlKey = 0xE0,
246 LeftShiftKey,
247 LeftAltKey,
248 LeftMetaKey,
249 RightControlKey,
250 RightShiftKey,
251 RightAltKey,
252 RightMetaKey,
253
254 MediaPlayPause,
255 MediaStopCD,
256 MediaPrevious,
257 MediaNext,
258 MediaEject,
259 MediaVolumeUp,
260 MediaVolumeDown,
261 MediaMute,
262 MediaWebsite,
263 MediaBack,
264 MediaForward,
265 MediaStop,
266 MediaFind,
267 MediaScrollUp,
268 MediaScrollDown,
269 MediaEdit,
270 MediaSleep,
271 MediaCoffee,
272 MediaRefresh,
273 MediaCalculator,
274
275 NumKeyboardKeys,
276};
277
278static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
279
280enum Modifiers {
281 LeftControl,
282 LeftShift,
283 LeftAlt,
284 LeftMeta,
285 RightControl,
286 RightShift,
287 RightAlt,
288 RightMeta,
289 CapsLock,
290 ScrollLock,
291 NumLock,
292
293 NumKeyboardMods,
294};
295
296constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
297constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
298constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
299
300constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
301constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
302constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
303
304} // namespace NativeKeyboard
305
306using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
307using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
308using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
309using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
310using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
311
312constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
313constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
314constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
315constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
316
317enum class ControllerType {
318 ProController,
319 DualJoycon,
320 RightJoycon,
321 LeftJoycon,
322};
323
324struct PlayerInput {
325 bool connected;
326 ControllerType type;
327 ButtonsRaw buttons;
328 AnalogsRaw analogs;
329
330 u32 body_color_right;
331 u32 button_color_right;
332 u32 body_color_left;
333 u32 button_color_left;
334};
335
336struct TouchscreenInput {
337 bool enabled;
338 std::string device;
339
340 u32 finger;
341 u32 diameter_x;
342 u32 diameter_y;
343 u32 rotation_angle;
344};
345
113struct Values { 346struct Values {
114 // System 347 // System
115 bool use_docked_mode; 348 bool use_docked_mode;
116 bool enable_nfc; 349 bool enable_nfc;
117 int current_user; 350 std::optional<u32> rng_seed;
118 int language_index; 351 s32 current_user;
352 s32 language_index;
119 353
120 // Controls 354 // Controls
121 std::array<std::string, NativeButton::NumButtons> buttons; 355 std::array<PlayerInput, 10> players;
122 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 356
357 bool mouse_enabled;
358 std::string mouse_device;
359 MouseButtonsRaw mouse_buttons;
360
361 bool keyboard_enabled;
362 KeyboardKeysRaw keyboard_keys;
363 KeyboardModsRaw keyboard_mods;
364
365 bool debug_pad_enabled;
366 ButtonsRaw debug_pad_buttons;
367 AnalogsRaw debug_pad_analogs;
368
123 std::string motion_device; 369 std::string motion_device;
124 std::string touch_device; 370 TouchscreenInput touchscreen;
125 std::atomic_bool is_device_reload_pending{true}; 371 std::atomic_bool is_device_reload_pending{true};
126 372
127 // Core 373 // Core
@@ -157,6 +403,8 @@ struct Values {
157 bool use_gdbstub; 403 bool use_gdbstub;
158 u16 gdbstub_port; 404 u16 gdbstub_port;
159 std::string program_args; 405 std::string program_args;
406 bool dump_exefs;
407 bool dump_nso;
160 408
161 // WebService 409 // WebService
162 bool enable_telemetry; 410 bool enable_telemetry;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index ddb1a1d69..a780215c1 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -21,6 +21,7 @@ add_library(video_core STATIC
21 macro_interpreter.h 21 macro_interpreter.h
22 memory_manager.cpp 22 memory_manager.cpp
23 memory_manager.h 23 memory_manager.h
24 rasterizer_cache.cpp
24 rasterizer_cache.h 25 rasterizer_cache.h
25 rasterizer_interface.h 26 rasterizer_interface.h
26 renderer_base.cpp 27 renderer_base.cpp
@@ -33,6 +34,7 @@ add_library(video_core STATIC
33 renderer_opengl/gl_rasterizer.h 34 renderer_opengl/gl_rasterizer.h
34 renderer_opengl/gl_rasterizer_cache.cpp 35 renderer_opengl/gl_rasterizer_cache.cpp
35 renderer_opengl/gl_rasterizer_cache.h 36 renderer_opengl/gl_rasterizer_cache.h
37 renderer_opengl/gl_resource_manager.cpp
36 renderer_opengl/gl_resource_manager.h 38 renderer_opengl/gl_resource_manager.h
37 renderer_opengl/gl_shader_cache.cpp 39 renderer_opengl/gl_shader_cache.cpp
38 renderer_opengl/gl_shader_cache.h 40 renderer_opengl/gl_shader_cache.h
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d79c50919..2bc534be3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -34,8 +34,49 @@ void Maxwell3D::InitializeRegisterDefaults() {
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is 34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS. 35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { 36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f; 37 regs.viewports[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f; 38 regs.viewports[viewport].depth_range_far = 1.0f;
39 }
40 // Doom and Bomberman seems to use the uninitialized registers and just enable blend
41 // so initialize blend registers with sane values
42 regs.blend.equation_rgb = Regs::Blend::Equation::Add;
43 regs.blend.factor_source_rgb = Regs::Blend::Factor::One;
44 regs.blend.factor_dest_rgb = Regs::Blend::Factor::Zero;
45 regs.blend.equation_a = Regs::Blend::Equation::Add;
46 regs.blend.factor_source_a = Regs::Blend::Factor::One;
47 regs.blend.factor_dest_a = Regs::Blend::Factor::Zero;
48 for (std::size_t blend_index = 0; blend_index < Regs::NumRenderTargets; blend_index++) {
49 regs.independent_blend[blend_index].equation_rgb = Regs::Blend::Equation::Add;
50 regs.independent_blend[blend_index].factor_source_rgb = Regs::Blend::Factor::One;
51 regs.independent_blend[blend_index].factor_dest_rgb = Regs::Blend::Factor::Zero;
52 regs.independent_blend[blend_index].equation_a = Regs::Blend::Equation::Add;
53 regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
54 regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
55 }
56 regs.stencil_front_op_fail = Regs::StencilOp::Keep;
57 regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
58 regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
59 regs.stencil_front_func_func = Regs::ComparisonOp::Always;
60 regs.stencil_front_func_mask = 0xFFFFFFFF;
61 regs.stencil_front_mask = 0xFFFFFFFF;
62 regs.stencil_two_side_enable = 1;
63 regs.stencil_back_op_fail = Regs::StencilOp::Keep;
64 regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
65 regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF;
69 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
70 // register carrying a default value. Assume it's OpenGL's default (1).
71 regs.point_size = 1.0f;
72
73 // TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
74 // default of enabled fixes rendering here.
75 for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
76 regs.color_mask[color_mask].R.Assign(1);
77 regs.color_mask[color_mask].G.Assign(1);
78 regs.color_mask[color_mask].B.Assign(1);
79 regs.color_mask[color_mask].A.Assign(1);
39 } 80 }
40} 81}
41 82
@@ -92,7 +133,13 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
92 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); 133 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
93 } 134 }
94 135
95 regs.reg_array[method] = value; 136 if (regs.reg_array[method] != value) {
137 regs.reg_array[method] = value;
138 if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
139 method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
140 dirty_flags.vertex_attrib_format = true;
141 }
142 }
96 143
97 switch (method) { 144 switch (method) {
98 case MAXWELL3D_REG_INDEX(macros.data): { 145 case MAXWELL3D_REG_INDEX(macros.data): {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 50873813e..4f137e693 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -345,6 +345,14 @@ public:
345 Invert = 6, 345 Invert = 6,
346 IncrWrap = 7, 346 IncrWrap = 7,
347 DecrWrap = 8, 347 DecrWrap = 8,
348 KeepOGL = 0x1E00,
349 ZeroOGL = 0,
350 ReplaceOGL = 0x1E01,
351 IncrOGL = 0x1E02,
352 DecrOGL = 0x1E03,
353 InvertOGL = 0x150A,
354 IncrWrapOGL = 0x8507,
355 DecrWrapOGL = 0x8508,
348 }; 356 };
349 357
350 enum class MemoryLayout : u32 { 358 enum class MemoryLayout : u32 {
@@ -381,6 +389,13 @@ public:
381 ReverseSubtract = 3, 389 ReverseSubtract = 3,
382 Min = 4, 390 Min = 4,
383 Max = 5, 391 Max = 5,
392
393 // These values are used by Nouveau and some games.
394 AddGL = 0x8006,
395 SubtractGL = 0x8007,
396 ReverseSubtractGL = 0x8008,
397 MinGL = 0x800a,
398 MaxGL = 0x800b
384 }; 399 };
385 400
386 enum class Factor : u32 { 401 enum class Factor : u32 {
@@ -462,6 +477,77 @@ public:
462 } 477 }
463 }; 478 };
464 479
480 struct ColorMask {
481 union {
482 u32 raw;
483 BitField<0, 4, u32> R;
484 BitField<4, 4, u32> G;
485 BitField<8, 4, u32> B;
486 BitField<12, 4, u32> A;
487 };
488 };
489
490 struct ViewportTransform {
491 f32 scale_x;
492 f32 scale_y;
493 f32 scale_z;
494 f32 translate_x;
495 f32 translate_y;
496 f32 translate_z;
497 INSERT_PADDING_WORDS(2);
498
499 MathUtil::Rectangle<s32> GetRect() const {
500 return {
501 GetX(), // left
502 GetY() + GetHeight(), // top
503 GetX() + GetWidth(), // right
504 GetY() // bottom
505 };
506 };
507
508 s32 GetX() const {
509 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
510 }
511
512 s32 GetY() const {
513 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
514 }
515
516 s32 GetWidth() const {
517 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
518 }
519
520 s32 GetHeight() const {
521 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
522 }
523 };
524
525 struct ScissorTest {
526 u32 enable;
527 union {
528 BitField<0, 16, u32> min_x;
529 BitField<16, 16, u32> max_x;
530 };
531 union {
532 BitField<0, 16, u32> min_y;
533 BitField<16, 16, u32> max_y;
534 };
535 u32 fill;
536 };
537
538 struct ViewPort {
539 union {
540 BitField<0, 16, u32> x;
541 BitField<16, 16, u32> width;
542 };
543 union {
544 BitField<0, 16, u32> y;
545 BitField<16, 16, u32> height;
546 };
547 float depth_range_near;
548 float depth_range_far;
549 };
550
465 bool IsShaderConfigEnabled(std::size_t index) const { 551 bool IsShaderConfigEnabled(std::size_t index) const {
466 // The VertexB is always enabled. 552 // The VertexB is always enabled.
467 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { 553 if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -487,55 +573,11 @@ public:
487 573
488 INSERT_PADDING_WORDS(0x2E); 574 INSERT_PADDING_WORDS(0x2E);
489 575
490 RenderTargetConfig rt[NumRenderTargets]; 576 std::array<RenderTargetConfig, NumRenderTargets> rt;
491 577
492 struct { 578 std::array<ViewportTransform, NumViewports> viewport_transform;
493 f32 scale_x;
494 f32 scale_y;
495 f32 scale_z;
496 f32 translate_x;
497 f32 translate_y;
498 f32 translate_z;
499 INSERT_PADDING_WORDS(2);
500
501 MathUtil::Rectangle<s32> GetRect() const {
502 return {
503 GetX(), // left
504 GetY() + GetHeight(), // top
505 GetX() + GetWidth(), // right
506 GetY() // bottom
507 };
508 };
509 579
510 s32 GetX() const { 580 std::array<ViewPort, NumViewports> viewports;
511 return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
512 }
513
514 s32 GetY() const {
515 return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
516 }
517
518 s32 GetWidth() const {
519 return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
520 }
521
522 s32 GetHeight() const {
523 return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
524 }
525 } viewport_transform[NumViewports];
526
527 struct {
528 union {
529 BitField<0, 16, u32> x;
530 BitField<16, 16, u32> width;
531 };
532 union {
533 BitField<0, 16, u32> y;
534 BitField<16, 16, u32> height;
535 };
536 float depth_range_near;
537 float depth_range_far;
538 } viewport[NumViewports];
539 581
540 INSERT_PADDING_WORDS(0x1D); 582 INSERT_PADDING_WORDS(0x1D);
541 583
@@ -553,25 +595,19 @@ public:
553 595
554 INSERT_PADDING_WORDS(0x17); 596 INSERT_PADDING_WORDS(0x17);
555 597
556 struct { 598 std::array<ScissorTest, NumViewports> scissor_test;
557 u32 enable;
558 union {
559 BitField<0, 16, u32> min_x;
560 BitField<16, 16, u32> max_x;
561 };
562 union {
563 BitField<0, 16, u32> min_y;
564 BitField<16, 16, u32> max_y;
565 };
566 } scissor_test;
567 599
568 INSERT_PADDING_WORDS(0x52); 600 INSERT_PADDING_WORDS(0x15);
569 601
570 s32 stencil_back_func_ref; 602 s32 stencil_back_func_ref;
571 u32 stencil_back_mask; 603 u32 stencil_back_mask;
572 u32 stencil_back_func_mask; 604 u32 stencil_back_func_mask;
573 605
574 INSERT_PADDING_WORDS(0x13); 606 INSERT_PADDING_WORDS(0xC);
607
608 u32 color_mask_common;
609
610 INSERT_PADDING_WORDS(0x6);
575 611
576 u32 rt_separate_frag_data; 612 u32 rt_separate_frag_data;
577 613
@@ -595,7 +631,16 @@ public:
595 } 631 }
596 } zeta; 632 } zeta;
597 633
598 INSERT_PADDING_WORDS(0x5B); 634 INSERT_PADDING_WORDS(0x41);
635
636 union {
637 BitField<0, 4, u32> stencil;
638 BitField<4, 4, u32> unknown;
639 BitField<8, 4, u32> scissor;
640 BitField<12, 4, u32> viewport;
641 } clear_flags;
642
643 INSERT_PADDING_WORDS(0x19);
599 644
600 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; 645 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
601 646
@@ -646,8 +691,14 @@ public:
646 ComparisonOp depth_test_func; 691 ComparisonOp depth_test_func;
647 float alpha_test_ref; 692 float alpha_test_ref;
648 ComparisonOp alpha_test_func; 693 ComparisonOp alpha_test_func;
649 694 u32 draw_tfb_stride;
650 INSERT_PADDING_WORDS(0x9); 695 struct {
696 float r;
697 float g;
698 float b;
699 float a;
700 } blend_color;
701 INSERT_PADDING_WORDS(0x4);
651 702
652 struct { 703 struct {
653 u32 separate_alpha; 704 u32 separate_alpha;
@@ -672,7 +723,9 @@ public:
672 u32 stencil_front_func_mask; 723 u32 stencil_front_func_mask;
673 u32 stencil_front_mask; 724 u32 stencil_front_mask;
674 725
675 INSERT_PADDING_WORDS(0x3); 726 INSERT_PADDING_WORDS(0x2);
727
728 u32 frag_color_clamp;
676 729
677 union { 730 union {
678 BitField<4, 1, u32> triangle_rast_flip; 731 BitField<4, 1, u32> triangle_rast_flip;
@@ -690,7 +743,12 @@ public:
690 743
691 u32 zeta_enable; 744 u32 zeta_enable;
692 745
693 INSERT_PADDING_WORDS(0x8); 746 union {
747 BitField<0, 1, u32> alpha_to_coverage;
748 BitField<4, 1, u32> alpha_to_one;
749 } multisample_control;
750
751 INSERT_PADDING_WORDS(0x7);
694 752
695 struct { 753 struct {
696 u32 tsc_address_high; 754 u32 tsc_address_high;
@@ -841,8 +899,9 @@ public:
841 BitField<6, 4, u32> RT; 899 BitField<6, 4, u32> RT;
842 BitField<10, 11, u32> layer; 900 BitField<10, 11, u32> layer;
843 } clear_buffers; 901 } clear_buffers;
844 902 INSERT_PADDING_WORDS(0xB);
845 INSERT_PADDING_WORDS(0x4B); 903 std::array<ColorMask, NumRenderTargets> color_mask;
904 INSERT_PADDING_WORDS(0x38);
846 905
847 struct { 906 struct {
848 u32 query_address_high; 907 u32 query_address_high;
@@ -983,6 +1042,12 @@ public:
983 State state{}; 1042 State state{};
984 MemoryManager& memory_manager; 1043 MemoryManager& memory_manager;
985 1044
1045 struct DirtyFlags {
1046 bool vertex_attrib_format = true;
1047 };
1048
1049 DirtyFlags dirty_flags;
1050
986 /// Reads a register value located at the input method address 1051 /// Reads a register value located at the input method address
987 u32 GetRegisterValue(u32 method) const; 1052 u32 GetRegisterValue(u32 method) const;
988 1053
@@ -1065,8 +1130,8 @@ private:
1065ASSERT_REG_POSITION(macros, 0x45); 1130ASSERT_REG_POSITION(macros, 0x45);
1066ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1131ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1067ASSERT_REG_POSITION(rt, 0x200); 1132ASSERT_REG_POSITION(rt, 0x200);
1068ASSERT_REG_POSITION(viewport_transform[0], 0x280); 1133ASSERT_REG_POSITION(viewport_transform, 0x280);
1069ASSERT_REG_POSITION(viewport, 0x300); 1134ASSERT_REG_POSITION(viewports, 0x300);
1070ASSERT_REG_POSITION(vertex_buffer, 0x35D); 1135ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1071ASSERT_REG_POSITION(clear_color[0], 0x360); 1136ASSERT_REG_POSITION(clear_color[0], 0x360);
1072ASSERT_REG_POSITION(clear_depth, 0x364); 1137ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1075,8 +1140,10 @@ ASSERT_REG_POSITION(scissor_test, 0x380);
1075ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1140ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1076ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1141ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1077ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1142ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
1143ASSERT_REG_POSITION(color_mask_common, 0x3E4);
1078ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); 1144ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
1079ASSERT_REG_POSITION(zeta, 0x3F8); 1145ASSERT_REG_POSITION(zeta, 0x3F8);
1146ASSERT_REG_POSITION(clear_flags, 0x43E);
1080ASSERT_REG_POSITION(vertex_attrib_format, 0x458); 1147ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
1081ASSERT_REG_POSITION(rt_control, 0x487); 1148ASSERT_REG_POSITION(rt_control, 0x487);
1082ASSERT_REG_POSITION(zeta_width, 0x48a); 1149ASSERT_REG_POSITION(zeta_width, 0x48a);
@@ -1087,6 +1154,10 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
1087ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); 1154ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
1088ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 1155ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
1089ASSERT_REG_POSITION(depth_test_func, 0x4C3); 1156ASSERT_REG_POSITION(depth_test_func, 0x4C3);
1157ASSERT_REG_POSITION(alpha_test_ref, 0x4C4);
1158ASSERT_REG_POSITION(alpha_test_func, 0x4C5);
1159ASSERT_REG_POSITION(draw_tfb_stride, 0x4C6);
1160ASSERT_REG_POSITION(blend_color, 0x4C7);
1090ASSERT_REG_POSITION(blend, 0x4CF); 1161ASSERT_REG_POSITION(blend, 0x4CF);
1091ASSERT_REG_POSITION(stencil_enable, 0x4E0); 1162ASSERT_REG_POSITION(stencil_enable, 0x4E0);
1092ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1); 1163ASSERT_REG_POSITION(stencil_front_op_fail, 0x4E1);
@@ -1096,10 +1167,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
1096ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 1167ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
1097ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 1168ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1098ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1169ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1170ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1099ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1171ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1100ASSERT_REG_POSITION(vb_element_base, 0x50D); 1172ASSERT_REG_POSITION(vb_element_base, 0x50D);
1101ASSERT_REG_POSITION(point_size, 0x546); 1173ASSERT_REG_POSITION(point_size, 0x546);
1102ASSERT_REG_POSITION(zeta_enable, 0x54E); 1174ASSERT_REG_POSITION(zeta_enable, 0x54E);
1175ASSERT_REG_POSITION(multisample_control, 0x54F);
1103ASSERT_REG_POSITION(tsc, 0x557); 1176ASSERT_REG_POSITION(tsc, 0x557);
1104ASSERT_REG_POSITION(tic, 0x55D); 1177ASSERT_REG_POSITION(tic, 0x55D);
1105ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 1178ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
@@ -1117,6 +1190,7 @@ ASSERT_REG_POSITION(instanced_arrays, 0x620);
1117ASSERT_REG_POSITION(cull, 0x646); 1190ASSERT_REG_POSITION(cull, 0x646);
1118ASSERT_REG_POSITION(logic_op, 0x671); 1191ASSERT_REG_POSITION(logic_op, 0x671);
1119ASSERT_REG_POSITION(clear_buffers, 0x674); 1192ASSERT_REG_POSITION(clear_buffers, 0x674);
1193ASSERT_REG_POSITION(color_mask, 0x680);
1120ASSERT_REG_POSITION(query, 0x6C0); 1194ASSERT_REG_POSITION(query, 0x6C0);
1121ASSERT_REG_POSITION(vertex_array[0], 0x700); 1195ASSERT_REG_POSITION(vertex_array[0], 0x700);
1122ASSERT_REG_POSITION(independent_blend, 0x780); 1196ASSERT_REG_POSITION(independent_blend, 0x780);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 83a6fd875..7e8449bc4 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -153,6 +153,7 @@ enum class PredCondition : u64 {
153 NotEqual = 5, 153 NotEqual = 5,
154 GreaterEqual = 6, 154 GreaterEqual = 6,
155 LessThanWithNan = 9, 155 LessThanWithNan = 9,
156 LessEqualWithNan = 11,
156 GreaterThanWithNan = 12, 157 GreaterThanWithNan = 12,
157 NotEqualWithNan = 13, 158 NotEqualWithNan = 13,
158 GreaterEqualWithNan = 14, 159 GreaterEqualWithNan = 14,
@@ -261,7 +262,7 @@ enum class FlowCondition : u64 {
261 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? 262 Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
262}; 263};
263 264
264enum class ControlCode : u64 { 265enum class ConditionCode : u64 {
265 F = 0, 266 F = 0,
266 LT = 1, 267 LT = 1,
267 EQ = 2, 268 EQ = 2,
@@ -569,7 +570,6 @@ union Instruction {
569 BitField<39, 2, u64> tab5cb8_2; 570 BitField<39, 2, u64> tab5cb8_2;
570 BitField<41, 3, u64> tab5c68_1; 571 BitField<41, 3, u64> tab5c68_1;
571 BitField<44, 2, u64> tab5c68_0; 572 BitField<44, 2, u64> tab5c68_0;
572 BitField<47, 1, u64> cc;
573 BitField<48, 1, u64> negate_b; 573 BitField<48, 1, u64> negate_b;
574 } fmul; 574 } fmul;
575 575
@@ -831,7 +831,7 @@ union Instruction {
831 union { 831 union {
832 BitField<0, 3, u64> pred0; 832 BitField<0, 3, u64> pred0;
833 BitField<3, 3, u64> pred3; 833 BitField<3, 3, u64> pred3;
834 BitField<8, 5, ControlCode> cc; // flag in cc 834 BitField<8, 5, ConditionCode> cc; // flag in cc
835 BitField<39, 3, u64> pred39; 835 BitField<39, 3, u64> pred39;
836 BitField<42, 1, u64> neg_pred39; 836 BitField<42, 1, u64> neg_pred39;
837 BitField<45, 4, PredOperation> op; // op with pred39 837 BitField<45, 4, PredOperation> op; // op with pred39
@@ -1235,7 +1235,7 @@ union Instruction {
1235 BitField<60, 1, u64> is_b_gpr; 1235 BitField<60, 1, u64> is_b_gpr;
1236 BitField<59, 1, u64> is_c_gpr; 1236 BitField<59, 1, u64> is_c_gpr;
1237 BitField<20, 24, s64> smem_imm; 1237 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code; 1238 BitField<0, 5, ConditionCode> flow_condition_code;
1239 1239
1240 Attribute attribute; 1240 Attribute attribute;
1241 Sampler sampler; 1241 Sampler sampler;
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 83c7e5b0b..51b3904f6 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -17,6 +17,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
17 switch (format) { 17 switch (format) {
18 case PixelFormat::ABGR8: 18 case PixelFormat::ABGR8:
19 return 4; 19 return 4;
20 default:
21 return 4;
20 } 22 }
21 23
22 UNREACHABLE(); 24 UNREACHABLE();
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 335a8d407..2b0dea5cd 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -35,6 +35,7 @@ void MacroInterpreter::Reset() {
35 // The next parameter index starts at 1, because $r1 already has the value of the first 35 // The next parameter index starts at 1, because $r1 already has the value of the first
36 // parameter. 36 // parameter.
37 next_parameter_index = 1; 37 next_parameter_index = 1;
38 carry_flag = false;
38} 39}
39 40
40bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { 41bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
@@ -135,14 +136,28 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
135 return {macro_memory[offset + pc / sizeof(u32)]}; 136 return {macro_memory[offset + pc / sizeof(u32)]};
136} 137}
137 138
138u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { 139u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) {
139 switch (operation) { 140 switch (operation) {
140 case ALUOperation::Add: 141 case ALUOperation::Add: {
141 return src_a + src_b; 142 const u64 result{static_cast<u64>(src_a) + src_b};
142 // TODO(Subv): Implement AddWithCarry 143 carry_flag = result > 0xffffffff;
143 case ALUOperation::Subtract: 144 return static_cast<u32>(result);
144 return src_a - src_b; 145 }
145 // TODO(Subv): Implement SubtractWithBorrow 146 case ALUOperation::AddWithCarry: {
147 const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)};
148 carry_flag = result > 0xffffffff;
149 return static_cast<u32>(result);
150 }
151 case ALUOperation::Subtract: {
152 const u64 result{static_cast<u64>(src_a) - src_b};
153 carry_flag = result < 0x100000000;
154 return static_cast<u32>(result);
155 }
156 case ALUOperation::SubtractWithBorrow: {
157 const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)};
158 carry_flag = result < 0x100000000;
159 return static_cast<u32>(result);
160 }
146 case ALUOperation::Xor: 161 case ALUOperation::Xor:
147 return src_a ^ src_b; 162 return src_a ^ src_b;
148 case ALUOperation::Or: 163 case ALUOperation::Or:
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index 62d1ce289..cde360288 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -117,7 +117,7 @@ private:
117 bool Step(u32 offset, bool is_delay_slot); 117 bool Step(u32 offset, bool is_delay_slot);
118 118
119 /// Calculates the result of an ALU operation. src_a OP src_b; 119 /// Calculates the result of an ALU operation. src_a OP src_b;
120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; 120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b);
121 121
122 /// Performs the result operation on the input result and stores it in the specified register 122 /// Performs the result operation on the input result and stores it in the specified register
123 /// (if necessary). 123 /// (if necessary).
@@ -165,5 +165,7 @@ private:
165 std::vector<u32> parameters; 165 std::vector<u32> parameters;
166 /// Index of the next parameter that will be fetched by the 'parm' instruction. 166 /// Index of the next parameter that will be fetched by the 'parm' instruction.
167 u32 next_parameter_index = 0; 167 u32 next_parameter_index = 0;
168
169 bool carry_flag{};
168}; 170};
169} // namespace Tegra 171} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 90a8e825d..77a20bb84 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -4,18 +4,21 @@
4 4
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h"
7#include "video_core/memory_manager.h" 8#include "video_core/memory_manager.h"
8 9
9namespace Tegra { 10namespace Tegra {
10 11
11GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { 12GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
12 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align); 13 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, align, PageStatus::Unmapped)};
13 ASSERT(gpu_addr);
14 14
15 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 15 ASSERT_MSG(gpu_addr, "unable to find available GPU memory");
16 VAddr& slot = PageSlot(*gpu_addr + offset); 16
17 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
18 VAddr& slot{PageSlot(*gpu_addr + offset)};
17 19
18 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 20 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
21
19 slot = static_cast<u64>(PageStatus::Allocated); 22 slot = static_cast<u64>(PageStatus::Allocated);
20 } 23 }
21 24
@@ -23,10 +26,11 @@ GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
23} 26}
24 27
25GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { 28GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
26 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 29 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
27 VAddr& slot = PageSlot(gpu_addr + offset); 30 VAddr& slot{PageSlot(gpu_addr + offset)};
28 31
29 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 32 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
33
30 slot = static_cast<u64>(PageStatus::Allocated); 34 slot = static_cast<u64>(PageStatus::Allocated);
31 } 35 }
32 36
@@ -34,17 +38,19 @@ GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
34} 38}
35 39
36GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { 40GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
37 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE); 41 const std::optional<GPUVAddr> gpu_addr{FindFreeBlock(0, size, PAGE_SIZE, PageStatus::Unmapped)};
38 ASSERT(gpu_addr); 42
43 ASSERT_MSG(gpu_addr, "unable to find available GPU memory");
39 44
40 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 45 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
41 VAddr& slot = PageSlot(*gpu_addr + offset); 46 VAddr& slot{PageSlot(*gpu_addr + offset)};
42 47
43 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped)); 48 ASSERT(slot == static_cast<u64>(PageStatus::Unmapped));
49
44 slot = cpu_addr + offset; 50 slot = cpu_addr + offset;
45 } 51 }
46 52
47 MappedRegion region{cpu_addr, *gpu_addr, size}; 53 const MappedRegion region{cpu_addr, *gpu_addr, size};
48 mapped_regions.push_back(region); 54 mapped_regions.push_back(region);
49 55
50 return *gpu_addr; 56 return *gpu_addr;
@@ -53,14 +59,31 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
53GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { 59GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
54 ASSERT((gpu_addr & PAGE_MASK) == 0); 60 ASSERT((gpu_addr & PAGE_MASK) == 0);
55 61
56 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 62 if (PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Allocated)) {
57 VAddr& slot = PageSlot(gpu_addr + offset); 63 // Page has been already mapped. In this case, we must find a new area of memory to use that
64 // is different than the specified one. Super Mario Odyssey hits this scenario when changing
65 // areas, but we do not want to overwrite the old pages.
66 // TODO(bunnei): We need to write a hardware test to confirm this behavior.
67
68 LOG_ERROR(HW_GPU, "attempting to map addr 0x{:016X}, which is not available!", gpu_addr);
69
70 const std::optional<GPUVAddr> new_gpu_addr{
71 FindFreeBlock(gpu_addr, size, PAGE_SIZE, PageStatus::Allocated)};
72
73 ASSERT_MSG(new_gpu_addr, "unable to find available GPU memory");
74
75 gpu_addr = *new_gpu_addr;
76 }
77
78 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
79 VAddr& slot{PageSlot(gpu_addr + offset)};
58 80
59 ASSERT(slot == static_cast<u64>(PageStatus::Allocated)); 81 ASSERT(slot == static_cast<u64>(PageStatus::Allocated));
82
60 slot = cpu_addr + offset; 83 slot = cpu_addr + offset;
61 } 84 }
62 85
63 MappedRegion region{cpu_addr, gpu_addr, size}; 86 const MappedRegion region{cpu_addr, gpu_addr, size};
64 mapped_regions.push_back(region); 87 mapped_regions.push_back(region);
65 88
66 return gpu_addr; 89 return gpu_addr;
@@ -69,11 +92,12 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
69GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { 92GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
70 ASSERT((gpu_addr & PAGE_MASK) == 0); 93 ASSERT((gpu_addr & PAGE_MASK) == 0);
71 94
72 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 95 for (u64 offset{}; offset < size; offset += PAGE_SIZE) {
73 VAddr& slot = PageSlot(gpu_addr + offset); 96 VAddr& slot{PageSlot(gpu_addr + offset)};
74 97
75 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) && 98 ASSERT(slot != static_cast<u64>(PageStatus::Allocated) &&
76 slot != static_cast<u64>(PageStatus::Unmapped)); 99 slot != static_cast<u64>(PageStatus::Unmapped));
100
77 slot = static_cast<u64>(PageStatus::Unmapped); 101 slot = static_cast<u64>(PageStatus::Unmapped);
78 } 102 }
79 103
@@ -97,13 +121,14 @@ GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
97 return {}; 121 return {};
98} 122}
99 123
100std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 124std::optional<GPUVAddr> MemoryManager::FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
101 GPUVAddr gpu_addr = 0; 125 PageStatus status) {
102 u64 free_space = 0; 126 GPUVAddr gpu_addr{region_start};
127 u64 free_space{};
103 align = (align + PAGE_MASK) & ~PAGE_MASK; 128 align = (align + PAGE_MASK) & ~PAGE_MASK;
104 129
105 while (gpu_addr + free_space < MAX_ADDRESS) { 130 while (gpu_addr + free_space < MAX_ADDRESS) {
106 if (!IsPageMapped(gpu_addr + free_space)) { 131 if (PageSlot(gpu_addr + free_space) == static_cast<u64>(status)) {
107 free_space += PAGE_SIZE; 132 free_space += PAGE_SIZE;
108 if (free_space >= size) { 133 if (free_space >= size) {
109 return gpu_addr; 134 return gpu_addr;
@@ -119,7 +144,7 @@ std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
119} 144}
120 145
121std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 146std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
122 VAddr base_addr = PageSlot(gpu_addr); 147 const VAddr base_addr{PageSlot(gpu_addr)};
123 148
124 if (base_addr == static_cast<u64>(PageStatus::Allocated) || 149 if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
125 base_addr == static_cast<u64>(PageStatus::Unmapped)) { 150 base_addr == static_cast<u64>(PageStatus::Unmapped)) {
@@ -133,19 +158,15 @@ std::vector<GPUVAddr> MemoryManager::CpuToGpuAddress(VAddr cpu_addr) const {
133 std::vector<GPUVAddr> results; 158 std::vector<GPUVAddr> results;
134 for (const auto& region : mapped_regions) { 159 for (const auto& region : mapped_regions) {
135 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) { 160 if (cpu_addr >= region.cpu_addr && cpu_addr < (region.cpu_addr + region.size)) {
136 u64 offset = cpu_addr - region.cpu_addr; 161 const u64 offset{cpu_addr - region.cpu_addr};
137 results.push_back(region.gpu_addr + offset); 162 results.push_back(region.gpu_addr + offset);
138 } 163 }
139 } 164 }
140 return results; 165 return results;
141} 166}
142 167
143bool MemoryManager::IsPageMapped(GPUVAddr gpu_addr) {
144 return PageSlot(gpu_addr) != static_cast<u64>(PageStatus::Unmapped);
145}
146
147VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) { 168VAddr& MemoryManager::PageSlot(GPUVAddr gpu_addr) {
148 auto& block = page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]; 169 auto& block{page_table[(gpu_addr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]};
149 if (!block) { 170 if (!block) {
150 block = std::make_unique<PageBlock>(); 171 block = std::make_unique<PageBlock>();
151 block->fill(static_cast<VAddr>(PageStatus::Unmapped)); 172 block->fill(static_cast<VAddr>(PageStatus::Unmapped));
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index b1255fd56..4eb338aa2 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -34,15 +34,15 @@ public:
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
35 35
36private: 36private:
37 std::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1);
38 bool IsPageMapped(GPUVAddr gpu_addr);
39 VAddr& PageSlot(GPUVAddr gpu_addr);
40
41 enum class PageStatus : u64 { 37 enum class PageStatus : u64 {
42 Unmapped = 0xFFFFFFFFFFFFFFFFULL, 38 Unmapped = 0xFFFFFFFFFFFFFFFFULL,
43 Allocated = 0xFFFFFFFFFFFFFFFEULL, 39 Allocated = 0xFFFFFFFFFFFFFFFEULL,
44 }; 40 };
45 41
42 std::optional<GPUVAddr> FindFreeBlock(GPUVAddr region_start, u64 size, u64 align,
43 PageStatus status);
44 VAddr& PageSlot(GPUVAddr gpu_addr);
45
46 static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; 46 static constexpr u64 MAX_ADDRESS{0x10000000000ULL};
47 static constexpr u64 PAGE_TABLE_BITS{10}; 47 static constexpr u64 PAGE_TABLE_BITS{10};
48 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; 48 static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS};
diff --git a/src/video_core/rasterizer_cache.cpp b/src/video_core/rasterizer_cache.cpp
new file mode 100644
index 000000000..093b2cdf4
--- /dev/null
+++ b/src/video_core/rasterizer_cache.cpp
@@ -0,0 +1,7 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/rasterizer_cache.h"
6
7RasterizerCacheObject::~RasterizerCacheObject() = default;
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 0a3b3951e..bcf0c15a4 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -5,18 +5,19 @@
5#pragma once 5#pragma once
6 6
7#include <set> 7#include <set>
8#include <unordered_map>
8 9
9#include <boost/icl/interval_map.hpp> 10#include <boost/icl/interval_map.hpp>
10#include <boost/range/iterator_range_core.hpp> 11#include <boost/range/iterator_range_core.hpp>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "core/core.h"
14#include "core/settings.h" 14#include "core/settings.h"
15#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
16#include "video_core/renderer_base.h"
17 16
18class RasterizerCacheObject { 17class RasterizerCacheObject {
19public: 18public:
19 virtual ~RasterizerCacheObject();
20
20 /// Gets the address of the shader in guest memory, required for cache management 21 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0; 22 virtual VAddr GetAddr() const = 0;
22 23
@@ -64,6 +65,8 @@ class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject; 65 friend class RasterizerCacheObject;
65 66
66public: 67public:
68 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
69
67 /// Write any cached resources overlapping the specified region back to memory 70 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) { 71 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)}; 72 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
@@ -86,45 +89,39 @@ public:
86 89
87 /// Invalidates everything in the cache 90 /// Invalidates everything in the cache
88 void InvalidateAll() { 91 void InvalidateAll() {
89 while (object_cache.begin() != object_cache.end()) { 92 while (interval_cache.begin() != interval_cache.end()) {
90 Unregister(*object_cache.begin()->second.begin()); 93 Unregister(*interval_cache.begin()->second.begin());
91 } 94 }
92 } 95 }
93 96
94protected: 97protected:
95 /// Tries to get an object from the cache with the specified address 98 /// Tries to get an object from the cache with the specified address
96 T TryGet(VAddr addr) const { 99 T TryGet(VAddr addr) const {
97 const ObjectInterval interval{addr}; 100 const auto iter = map_cache.find(addr);
98 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 101 if (iter != map_cache.end())
99 for (auto& cached_object : pair.second) { 102 return iter->second;
100 if (cached_object->GetAddr() == addr) {
101 return cached_object;
102 }
103 }
104 }
105 return nullptr; 103 return nullptr;
106 } 104 }
107 105
108 /// Register an object into the cache 106 /// Register an object into the cache
109 void Register(const T& object) { 107 void Register(const T& object) {
110 object->SetIsRegistered(true); 108 object->SetIsRegistered(true);
111 object_cache.add({GetInterval(object), ObjectSet{object}}); 109 interval_cache.add({GetInterval(object), ObjectSet{object}});
112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 110 map_cache.insert({object->GetAddr(), object});
113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 111 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
114 } 112 }
115 113
116 /// Unregisters an object from the cache 114 /// Unregisters an object from the cache
117 void Unregister(const T& object) { 115 void Unregister(const T& object) {
118 object->SetIsRegistered(false); 116 object->SetIsRegistered(false);
119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 117 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit 118 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) { 119 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object); 120 FlushObject(object);
125 } 121 }
126 122
127 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 123 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
124 map_cache.erase(object->GetAddr());
128 } 125 }
129 126
130 /// Returns a ticks counter used for tracking when cached objects were last modified 127 /// Returns a ticks counter used for tracking when cached objects were last modified
@@ -141,7 +138,7 @@ private:
141 138
142 std::vector<T> objects; 139 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size}; 140 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 141 for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) { 142 for (auto& cached_object : pair.second) {
146 if (!cached_object) { 143 if (!cached_object) {
147 continue; 144 continue;
@@ -167,14 +164,17 @@ private:
167 } 164 }
168 165
169 using ObjectSet = std::set<T>; 166 using ObjectSet = std::set<T>;
170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 167 using ObjectCache = std::unordered_map<VAddr, T>;
171 using ObjectInterval = typename ObjectCache::interval_type; 168 using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
169 using ObjectInterval = typename IntervalCache::interval_type;
172 170
173 static auto GetInterval(const T& object) { 171 static auto GetInterval(const T& object) {
174 return ObjectInterval::right_open(object->GetAddr(), 172 return ObjectInterval::right_open(object->GetAddr(),
175 object->GetAddr() + object->GetSizeInBytes()); 173 object->GetAddr() + object->GetSizeInBytes());
176 } 174 }
177 175
178 ObjectCache object_cache; ///< Cache of objects 176 ObjectCache map_cache;
179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing 177 IntervalCache interval_cache; ///< Cache of objects
178 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
179 VideoCore::RasterizerInterface& rasterizer;
180}; 180};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 0df3725c2..1482cdb40 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -5,7 +5,6 @@
5#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/gl_rasterizer.h"
9 8
10namespace VideoCore { 9namespace VideoCore {
11 10
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 41a54b3e7..075192c3f 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -9,10 +9,12 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/memory.h" 10#include "core/memory.h"
11#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
12#include "video_core/renderer_opengl/gl_rasterizer.h"
12 13
13namespace OpenGL { 14namespace OpenGL {
14 15
15OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} 16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
17 : RasterizerCache{rasterizer}, stream_buffer(GL_ARRAY_BUFFER, size) {}
16 18
17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 19GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
18 std::size_t alignment, bool cache) { 20 std::size_t alignment, bool cache) {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index be29dc8be..91fca3f6c 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,6 +15,8 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18class RasterizerOpenGL;
19
18struct CachedBufferEntry final : public RasterizerCacheObject { 20struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const override { 21 VAddr GetAddr() const override {
20 return addr; 22 return addr;
@@ -35,7 +37,7 @@ struct CachedBufferEntry final : public RasterizerCacheObject {
35 37
36class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 38class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
37public: 39public:
38 explicit OGLBufferCache(std::size_t size); 40 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size);
39 41
40 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been 42 /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
41 /// allocated. 43 /// allocated.
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index 741f14bc3..d9ed08437 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h"
9#include "core/memory.h" 10#include "core/memory.h"
10#include "video_core/renderer_opengl/gl_buffer_cache.h" 11#include "video_core/renderer_opengl/gl_buffer_cache.h"
11#include "video_core/renderer_opengl/gl_primitive_assembler.h" 12#include "video_core/renderer_opengl/gl_primitive_assembler.h"
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a0527fe57..630a58e49 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -33,7 +33,8 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
33using PixelFormat = VideoCore::Surface::PixelFormat; 33using PixelFormat = VideoCore::Surface::PixelFormat;
34using SurfaceType = VideoCore::Surface::SurfaceType; 34using SurfaceType = VideoCore::Surface::SurfaceType;
35 35
36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); 36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Format Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_VB, "OpenGL", "Vertex Buffer Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); 38MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
38MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192)); 39MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
39MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192)); 40MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
@@ -79,7 +80,8 @@ struct DrawParameters {
79}; 80};
80 81
81RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 82RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
82 : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { 83 : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info},
84 buffer_cache(*this, STREAM_BUFFER_SIZE) {
83 // Create sampler objects 85 // Create sampler objects
84 for (std::size_t i = 0; i < texture_samplers.size(); ++i) { 86 for (std::size_t i = 0; i < texture_samplers.size(); ++i) {
85 texture_samplers[i].Create(); 87 texture_samplers[i].Create();
@@ -96,17 +98,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
96 has_ARB_direct_state_access = true; 98 has_ARB_direct_state_access = true;
97 } else if (extension == "GL_ARB_multi_bind") { 99 } else if (extension == "GL_ARB_multi_bind") {
98 has_ARB_multi_bind = true; 100 has_ARB_multi_bind = true;
99 } else if (extension == "GL_ARB_separate_shader_objects") {
100 has_ARB_separate_shader_objects = true;
101 } else if (extension == "GL_ARB_vertex_attrib_binding") {
102 has_ARB_vertex_attrib_binding = true;
103 } 101 }
104 } 102 }
105 103
106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
107 OpenGLState::ApplyDefaultState(); 104 OpenGLState::ApplyDefaultState();
108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
109 state.clip_distance[0] = true;
110 105
111 // Create render framebuffer 106 // Create render framebuffer
112 framebuffer.Create(); 107 framebuffer.Create();
@@ -122,18 +117,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
122 117
123RasterizerOpenGL::~RasterizerOpenGL() {} 118RasterizerOpenGL::~RasterizerOpenGL() {}
124 119
125void RasterizerOpenGL::SetupVertexArrays() { 120void RasterizerOpenGL::SetupVertexFormat() {
126 MICROPROFILE_SCOPE(OpenGL_VAO); 121 auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
127 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
128 const auto& regs = gpu.regs; 122 const auto& regs = gpu.regs;
129 123
124 if (!gpu.dirty_flags.vertex_attrib_format)
125 return;
126 gpu.dirty_flags.vertex_attrib_format = false;
127
128 MICROPROFILE_SCOPE(OpenGL_VAO);
129
130 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format); 130 auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
131 auto& VAO = iter->second; 131 auto& VAO = iter->second;
132 132
133 if (is_cache_miss) { 133 if (is_cache_miss) {
134 VAO.Create(); 134 VAO.Create();
135 state.draw.vertex_array = VAO.handle; 135 state.draw.vertex_array = VAO.handle;
136 state.Apply(); 136 state.ApplyVertexBufferState();
137 137
138 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work 138 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
139 // around. 139 // around.
@@ -175,8 +175,13 @@ void RasterizerOpenGL::SetupVertexArrays() {
175 } 175 }
176 } 176 }
177 state.draw.vertex_array = VAO.handle; 177 state.draw.vertex_array = VAO.handle;
178 state.draw.vertex_buffer = buffer_cache.GetHandle(); 178 state.ApplyVertexBufferState();
179 state.Apply(); 179}
180
181void RasterizerOpenGL::SetupVertexBuffer() {
182 MICROPROFILE_SCOPE(OpenGL_VB);
183 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
184 const auto& regs = gpu.regs;
180 185
181 // Upload all guest vertex arrays sequentially to our buffer 186 // Upload all guest vertex arrays sequentially to our buffer
182 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { 187 for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -203,6 +208,9 @@ void RasterizerOpenGL::SetupVertexArrays() {
203 glVertexBindingDivisor(index, 0); 208 glVertexBindingDivisor(index, 0);
204 } 209 }
205 } 210 }
211
212 // Implicit set by glBindVertexBuffer. Stupid glstate handling...
213 state.draw.vertex_buffer = buffer_cache.GetHandle();
206} 214}
207 215
208DrawParameters RasterizerOpenGL::SetupDraw() { 216DrawParameters RasterizerOpenGL::SetupDraw() {
@@ -327,8 +335,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
327 index++; 335 index++;
328 } 336 }
329 } 337 }
330
331 state.Apply();
332} 338}
333 339
334std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 340std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -397,8 +403,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
397 cached_pages.add({pages_interval, delta}); 403 cached_pages.add({pages_interval, delta});
398} 404}
399 405
400void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 406void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
401 bool preserve_contents, 407 bool using_depth_fb, bool preserve_contents,
402 std::optional<std::size_t> single_color_target) { 408 std::optional<std::size_t> single_color_target) {
403 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 409 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
404 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 410 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -414,9 +420,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
414 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); 420 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
415 421
416 // Bind the framebuffer surfaces 422 // Bind the framebuffer surfaces
417 state.draw.draw_framebuffer = framebuffer.handle; 423 current_state.draw.draw_framebuffer = framebuffer.handle;
418 state.Apply(); 424 current_state.ApplyFramebufferState();
419 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; 425 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
420 426
421 if (using_color_fb) { 427 if (using_color_fb) {
422 if (single_color_target) { 428 if (single_color_target) {
@@ -494,10 +500,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
494 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 500 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
495 0); 501 0);
496 } 502 }
497 503 SyncViewport(current_state);
498 SyncViewport();
499
500 state.Apply();
501} 504}
502 505
503void RasterizerOpenGL::Clear() { 506void RasterizerOpenGL::Clear() {
@@ -510,22 +513,23 @@ void RasterizerOpenGL::Clear() {
510 bool use_stencil{}; 513 bool use_stencil{};
511 514
512 OpenGLState clear_state; 515 OpenGLState clear_state;
513 clear_state.draw.draw_framebuffer = framebuffer.handle;
514 clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
515 clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
516 clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
517 clear_state.color_mask.alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
518
519 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 516 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
520 regs.clear_buffers.A) { 517 regs.clear_buffers.A) {
521 use_color = true; 518 use_color = true;
522 } 519 }
520 if (use_color) {
521 clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
522 clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
523 clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
524 clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
525 }
523 if (regs.clear_buffers.Z) { 526 if (regs.clear_buffers.Z) {
524 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 527 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
525 use_depth = true; 528 use_depth = true;
526 529
527 // Always enable the depth write when clearing the depth buffer. The depth write mask is 530 // Always enable the depth write when clearing the depth buffer. The depth write mask is
528 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. 531 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
532 // true.
529 clear_state.depth.test_enabled = true; 533 clear_state.depth.test_enabled = true;
530 clear_state.depth.test_func = GL_ALWAYS; 534 clear_state.depth.test_func = GL_ALWAYS;
531 } 535 }
@@ -533,6 +537,30 @@ void RasterizerOpenGL::Clear() {
533 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); 537 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
534 use_stencil = true; 538 use_stencil = true;
535 clear_state.stencil.test_enabled = true; 539 clear_state.stencil.test_enabled = true;
540 if (regs.clear_flags.stencil) {
541 // Stencil affects the clear so fill it with the used masks
542 clear_state.stencil.front.test_func = GL_ALWAYS;
543 clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
544 clear_state.stencil.front.action_stencil_fail = GL_KEEP;
545 clear_state.stencil.front.action_depth_fail = GL_KEEP;
546 clear_state.stencil.front.action_depth_pass = GL_KEEP;
547 clear_state.stencil.front.write_mask = regs.stencil_front_mask;
548 if (regs.stencil_two_side_enable) {
549 clear_state.stencil.back.test_func = GL_ALWAYS;
550 clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
551 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
552 clear_state.stencil.back.action_depth_fail = GL_KEEP;
553 clear_state.stencil.back.action_depth_pass = GL_KEEP;
554 clear_state.stencil.back.write_mask = regs.stencil_back_mask;
555 } else {
556 clear_state.stencil.back.test_func = GL_ALWAYS;
557 clear_state.stencil.back.test_mask = 0xFFFFFFFF;
558 clear_state.stencil.back.write_mask = 0xFFFFFFFF;
559 clear_state.stencil.back.action_stencil_fail = GL_KEEP;
560 clear_state.stencil.back.action_depth_fail = GL_KEEP;
561 clear_state.stencil.back.action_depth_pass = GL_KEEP;
562 }
563 }
536 } 564 }
537 565
538 if (!use_color && !use_depth && !use_stencil) { 566 if (!use_color && !use_depth && !use_stencil) {
@@ -542,11 +570,16 @@ void RasterizerOpenGL::Clear() {
542 570
543 ScopeAcquireGLContext acquire_context{emu_window}; 571 ScopeAcquireGLContext acquire_context{emu_window};
544 572
545 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 573 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
546 regs.clear_buffers.RT.Value()); 574 regs.clear_buffers.RT.Value());
547 // Copy the sRGB setting to the clear state to avoid problem with 575 if (regs.clear_flags.scissor) {
548 // specific driver implementations 576 SyncScissorTest(clear_state);
549 clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled; 577 }
578
579 if (regs.clear_flags.viewport) {
580 clear_state.EmulateViewportWithScissor();
581 }
582
550 clear_state.Apply(); 583 clear_state.Apply();
551 584
552 if (use_color) { 585 if (use_color) {
@@ -572,16 +605,17 @@ void RasterizerOpenGL::DrawArrays() {
572 605
573 ScopeAcquireGLContext acquire_context{emu_window}; 606 ScopeAcquireGLContext acquire_context{emu_window};
574 607
575 ConfigureFramebuffers(); 608 ConfigureFramebuffers(state);
576 609 SyncColorMask();
610 SyncFragmentColorClampState();
611 SyncMultiSampleState();
577 SyncDepthTestState(); 612 SyncDepthTestState();
578 SyncStencilTestState(); 613 SyncStencilTestState();
579 SyncBlendState(); 614 SyncBlendState();
580 SyncLogicOpState(); 615 SyncLogicOpState();
581 SyncCullMode(); 616 SyncCullMode();
582 SyncPrimitiveRestart(); 617 SyncPrimitiveRestart();
583 SyncDepthRange(); 618 SyncScissorTest(state);
584 SyncScissorTest();
585 // Alpha Testing is synced on shaders. 619 // Alpha Testing is synced on shaders.
586 SyncTransformFeedback(); 620 SyncTransformFeedback();
587 SyncPointState(); 621 SyncPointState();
@@ -594,7 +628,7 @@ void RasterizerOpenGL::DrawArrays() {
594 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 628 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
595 629
596 state.draw.vertex_buffer = buffer_cache.GetHandle(); 630 state.draw.vertex_buffer = buffer_cache.GetHandle();
597 state.Apply(); 631 state.ApplyVertexBufferState();
598 632
599 std::size_t buffer_size = CalculateVertexArraysSize(); 633 std::size_t buffer_size = CalculateVertexArraysSize();
600 634
@@ -621,7 +655,8 @@ void RasterizerOpenGL::DrawArrays() {
621 655
622 buffer_cache.Map(buffer_size); 656 buffer_cache.Map(buffer_size);
623 657
624 SetupVertexArrays(); 658 SetupVertexFormat();
659 SetupVertexBuffer();
625 DrawParameters params = SetupDraw(); 660 DrawParameters params = SetupDraw();
626 SetupShaders(params.primitive_mode); 661 SetupShaders(params.primitive_mode);
627 662
@@ -634,7 +669,7 @@ void RasterizerOpenGL::DrawArrays() {
634 params.DispatchDraw(); 669 params.DispatchDraw();
635 670
636 // Disable scissor test 671 // Disable scissor test
637 state.scissor.enabled = false; 672 state.viewports[0].scissor.enabled = false;
638 673
639 accelerate_draw = AccelDraw::Disabled; 674 accelerate_draw = AccelDraw::Disabled;
640 675
@@ -727,7 +762,6 @@ void RasterizerOpenGL::SamplerInfo::Create() {
727 762
728void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 763void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
729 const GLuint s = sampler.handle; 764 const GLuint s = sampler.handle;
730
731 if (mag_filter != config.mag_filter) { 765 if (mag_filter != config.mag_filter) {
732 mag_filter = config.mag_filter; 766 mag_filter = config.mag_filter;
733 glSamplerParameteri( 767 glSamplerParameteri(
@@ -769,15 +803,51 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
769 MaxwellToGL::DepthCompareFunc(depth_compare_func)); 803 MaxwellToGL::DepthCompareFunc(depth_compare_func));
770 } 804 }
771 805
772 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || 806 GLvec4 new_border_color;
773 wrap_p == Tegra::Texture::WrapMode::Border) { 807 if (config.srgb_conversion) {
774 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 808 new_border_color[0] = config.srgb_border_color_r / 255.0f;
775 config.border_color_b, config.border_color_a}}; 809 new_border_color[1] = config.srgb_border_color_g / 255.0f;
776 if (border_color != new_border_color) { 810 new_border_color[2] = config.srgb_border_color_g / 255.0f;
777 border_color = new_border_color; 811 } else {
778 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 812 new_border_color[0] = config.border_color_r;
813 new_border_color[1] = config.border_color_g;
814 new_border_color[2] = config.border_color_b;
815 }
816 new_border_color[3] = config.border_color_a;
817
818 if (border_color != new_border_color) {
819 border_color = new_border_color;
820 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
821 }
822
823 const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
824 if (anisotropic_max != max_anisotropic) {
825 max_anisotropic = anisotropic_max;
826 if (GLAD_GL_ARB_texture_filter_anisotropic) {
827 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
828 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
829 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
779 } 830 }
780 } 831 }
832 const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
833 if (lod_min != min_lod) {
834 min_lod = lod_min;
835 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
836 }
837
838 const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
839 if (lod_max != max_lod) {
840 max_lod = lod_max;
841 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
842 }
843 const u32 bias = config.mip_lod_bias.Value();
844 // Sign extend the 13-bit value.
845 constexpr u32 mask = 1U << (13 - 1);
846 const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
847 if (lod_bias != bias_lod) {
848 lod_bias = bias_lod;
849 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
850 }
781} 851}
782 852
783u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 853u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
@@ -897,14 +967,18 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
897 return current_unit + static_cast<u32>(entries.size()); 967 return current_unit + static_cast<u32>(entries.size());
898} 968}
899 969
900void RasterizerOpenGL::SyncViewport() { 970void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
901 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 971 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
902 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; 972 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
903 973 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
904 state.viewport.x = viewport_rect.left; 974 auto& viewport = current_state.viewports[i];
905 state.viewport.y = viewport_rect.bottom; 975 viewport.x = viewport_rect.left;
906 state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); 976 viewport.y = viewport_rect.bottom;
907 state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); 977 viewport.width = viewport_rect.GetWidth();
978 viewport.height = viewport_rect.GetHeight();
979 viewport.depth_range_far = regs.viewports[i].depth_range_far;
980 viewport.depth_range_near = regs.viewports[i].depth_range_near;
981 }
908} 982}
909 983
910void RasterizerOpenGL::SyncClipEnabled() { 984void RasterizerOpenGL::SyncClipEnabled() {
@@ -946,13 +1020,6 @@ void RasterizerOpenGL::SyncPrimitiveRestart() {
946 state.primitive_restart.index = regs.primitive_restart.index; 1020 state.primitive_restart.index = regs.primitive_restart.index;
947} 1021}
948 1022
949void RasterizerOpenGL::SyncDepthRange() {
950 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
951
952 state.depth.depth_range_near = regs.viewport->depth_range_near;
953 state.depth.depth_range_far = regs.viewport->depth_range_far;
954}
955
956void RasterizerOpenGL::SyncDepthTestState() { 1023void RasterizerOpenGL::SyncDepthTestState() {
957 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1024 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
958 1025
@@ -973,9 +1040,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
973 return; 1040 return;
974 } 1041 }
975 1042
976 // TODO(bunnei): Verify behavior when this is not set
977 ASSERT(regs.stencil_two_side_enable);
978
979 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); 1043 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
980 state.stencil.front.test_ref = regs.stencil_front_func_ref; 1044 state.stencil.front.test_ref = regs.stencil_front_func_ref;
981 state.stencil.front.test_mask = regs.stencil_front_func_mask; 1045 state.stencil.front.test_mask = regs.stencil_front_func_mask;
@@ -983,42 +1047,95 @@ void RasterizerOpenGL::SyncStencilTestState() {
983 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); 1047 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
984 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); 1048 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
985 state.stencil.front.write_mask = regs.stencil_front_mask; 1049 state.stencil.front.write_mask = regs.stencil_front_mask;
1050 if (regs.stencil_two_side_enable) {
1051 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
1052 state.stencil.back.test_ref = regs.stencil_back_func_ref;
1053 state.stencil.back.test_mask = regs.stencil_back_func_mask;
1054 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
1055 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
1056 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
1057 state.stencil.back.write_mask = regs.stencil_back_mask;
1058 } else {
1059 state.stencil.back.test_func = GL_ALWAYS;
1060 state.stencil.back.test_ref = 0;
1061 state.stencil.back.test_mask = 0xFFFFFFFF;
1062 state.stencil.back.write_mask = 0xFFFFFFFF;
1063 state.stencil.back.action_stencil_fail = GL_KEEP;
1064 state.stencil.back.action_depth_fail = GL_KEEP;
1065 state.stencil.back.action_depth_pass = GL_KEEP;
1066 }
1067}
986 1068
987 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); 1069void RasterizerOpenGL::SyncColorMask() {
988 state.stencil.back.test_ref = regs.stencil_back_func_ref; 1070 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
989 state.stencil.back.test_mask = regs.stencil_back_func_mask; 1071 const std::size_t count =
990 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); 1072 regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
991 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); 1073 for (std::size_t i = 0; i < count; i++) {
992 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); 1074 const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
993 state.stencil.back.write_mask = regs.stencil_back_mask; 1075 auto& dest = state.color_mask[i];
1076 dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
1077 dest.green_enabled = (source.G == 0) ? GL_FALSE : GL_TRUE;
1078 dest.blue_enabled = (source.B == 0) ? GL_FALSE : GL_TRUE;
1079 dest.alpha_enabled = (source.A == 0) ? GL_FALSE : GL_TRUE;
1080 }
994} 1081}
995 1082
996void RasterizerOpenGL::SyncBlendState() { 1083void RasterizerOpenGL::SyncMultiSampleState() {
997 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1084 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1085 state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
1086 state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
1087}
998 1088
999 // TODO(Subv): Support more than just render target 0. 1089void RasterizerOpenGL::SyncFragmentColorClampState() {
1000 state.blend.enabled = regs.blend.enable[0] != 0; 1090 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1091 state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
1092}
1001 1093
1002 if (!state.blend.enabled) 1094void RasterizerOpenGL::SyncBlendState() {
1003 return; 1095 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1004 1096
1005 ASSERT_MSG(regs.logic_op.enable == 0, 1097 state.blend_color.red = regs.blend_color.r;
1006 "Blending and logic op can't be enabled at the same time."); 1098 state.blend_color.green = regs.blend_color.g;
1099 state.blend_color.blue = regs.blend_color.b;
1100 state.blend_color.alpha = regs.blend_color.a;
1101
1102 state.independant_blend.enabled = regs.independent_blend_enable;
1103 if (!state.independant_blend.enabled) {
1104 auto& blend = state.blend[0];
1105 const auto& src = regs.blend;
1106 blend.enabled = src.enable[0] != 0;
1107 if (blend.enabled) {
1108 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1109 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1110 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1111 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1112 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1113 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1114 }
1115 for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1116 state.blend[i].enabled = false;
1117 }
1118 return;
1119 }
1007 1120
1008 ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented"); 1121 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
1009 ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented"); 1122 auto& blend = state.blend[i];
1010 state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb); 1123 const auto& src = regs.independent_blend[i];
1011 state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb); 1124 blend.enabled = regs.blend.enable[i] != 0;
1012 state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_rgb); 1125 if (!blend.enabled)
1013 state.blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_a); 1126 continue;
1014 state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a); 1127 blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
1015 state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_a); 1128 blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
1129 blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
1130 blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
1131 blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
1132 blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
1133 }
1016} 1134}
1017 1135
1018void RasterizerOpenGL::SyncLogicOpState() { 1136void RasterizerOpenGL::SyncLogicOpState() {
1019 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1137 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1020 1138
1021 // TODO(Subv): Support more than just render target 0.
1022 state.logic_op.enabled = regs.logic_op.enable != 0; 1139 state.logic_op.enabled = regs.logic_op.enable != 0;
1023 1140
1024 if (!state.logic_op.enabled) 1141 if (!state.logic_op.enabled)
@@ -1030,19 +1147,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
1030 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1147 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1031} 1148}
1032 1149
1033void RasterizerOpenGL::SyncScissorTest() { 1150void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1151 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1035 1152 for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
1036 state.scissor.enabled = (regs.scissor_test.enable != 0); 1153 const auto& src = regs.scissor_test[i];
1037 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's 1154 auto& dst = current_state.viewports[i].scissor;
1038 // implemented. 1155 dst.enabled = (src.enable != 0);
1039 if (regs.scissor_test.enable != 0) { 1156 if (dst.enabled == 0) {
1040 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x; 1157 return;
1041 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y; 1158 }
1042 state.scissor.x = regs.scissor_test.min_x; 1159 const u32 width = src.max_x - src.min_x;
1043 state.scissor.y = regs.scissor_test.min_y; 1160 const u32 height = src.max_y - src.min_y;
1044 state.scissor.width = width; 1161 dst.x = src.min_x;
1045 state.scissor.height = height; 1162 dst.y = src.min_y;
1163 dst.width = width;
1164 dst.height = height;
1046 } 1165 }
1047} 1166}
1048 1167
@@ -1057,20 +1176,15 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1057 1176
1058void RasterizerOpenGL::SyncPointState() { 1177void RasterizerOpenGL::SyncPointState() {
1059 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1178 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1060 1179 state.point.size = regs.point_size;
1061 // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
1062 // register carrying a default value. For now, if the point size is zero, assume it's
1063 // OpenGL's default (1).
1064 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1065} 1180}
1066 1181
1067void RasterizerOpenGL::CheckAlphaTests() { 1182void RasterizerOpenGL::CheckAlphaTests() {
1068 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1183 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1069 1184
1070 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { 1185 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1071 LOG_CRITICAL( 1186 LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
1072 Render_OpenGL, 1187 "this behavior is undefined.");
1073 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1074 UNREACHABLE(); 1188 UNREACHABLE();
1075 } 1189 }
1076} 1190}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 47097c569..f4354289c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,18 +88,23 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::TSCEntry& config); 91 void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest;
95 Tegra::Texture::TextureFilter min_filter; 95 Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest;
96 Tegra::Texture::TextureMipmapFilter mip_filter; 96 Tegra::Texture::TextureMipmapFilter mip_filter = Tegra::Texture::TextureMipmapFilter::None;
97 Tegra::Texture::WrapMode wrap_u; 97 Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge;
98 Tegra::Texture::WrapMode wrap_v; 98 Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge;
99 Tegra::Texture::WrapMode wrap_p; 99 Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge;
100 bool uses_depth_compare; 100 bool uses_depth_compare = false;
101 Tegra::Texture::DepthCompareFunc depth_compare_func; 101 Tegra::Texture::DepthCompareFunc depth_compare_func =
102 GLvec4 border_color; 102 Tegra::Texture::DepthCompareFunc::Always;
103 GLvec4 border_color = {};
104 float min_lod = 0.0f;
105 float max_lod = 16.0f;
106 float lod_bias = 0.0f;
107 float max_anisotropic = 1.0f;
103 }; 108 };
104 109
105 /** 110 /**
@@ -109,8 +114,8 @@ private:
109 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer. 114 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
110 * @param single_color_target Specifies if a single color buffer target should be used. 115 * @param single_color_target Specifies if a single color buffer target should be used.
111 */ 116 */
112 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, 117 void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
113 bool preserve_contents = true, 118 bool using_depth_fb = true, bool preserve_contents = true,
114 std::optional<std::size_t> single_color_target = {}); 119 std::optional<std::size_t> single_color_target = {});
115 120
116 /* 121 /*
@@ -133,8 +138,8 @@ private:
133 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 138 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
134 GLenum primitive_mode, u32 current_unit); 139 GLenum primitive_mode, u32 current_unit);
135 140
136 /// Syncs the viewport to match the guest state 141 /// Syncs the viewport and depth range to match the guest state
137 void SyncViewport(); 142 void SyncViewport(OpenGLState& current_state);
138 143
139 /// Syncs the clip enabled status to match the guest state 144 /// Syncs the clip enabled status to match the guest state
140 void SyncClipEnabled(); 145 void SyncClipEnabled();
@@ -148,9 +153,6 @@ private:
148 /// Syncs the primitve restart to match the guest state 153 /// Syncs the primitve restart to match the guest state
149 void SyncPrimitiveRestart(); 154 void SyncPrimitiveRestart();
150 155
151 /// Syncs the depth range to match the guest state
152 void SyncDepthRange();
153
154 /// Syncs the depth test state to match the guest state 156 /// Syncs the depth test state to match the guest state
155 void SyncDepthTestState(); 157 void SyncDepthTestState();
156 158
@@ -163,8 +165,14 @@ private:
163 /// Syncs the LogicOp state to match the guest state 165 /// Syncs the LogicOp state to match the guest state
164 void SyncLogicOpState(); 166 void SyncLogicOpState();
165 167
168 /// Syncs the the color clamp state
169 void SyncFragmentColorClampState();
170
171 /// Syncs the alpha coverage and alpha to one
172 void SyncMultiSampleState();
173
166 /// Syncs the scissor test state to match the guest state 174 /// Syncs the scissor test state to match the guest state
167 void SyncScissorTest(); 175 void SyncScissorTest(OpenGLState& current_state);
168 176
169 /// Syncs the transform feedback state to match the guest state 177 /// Syncs the transform feedback state to match the guest state
170 void SyncTransformFeedback(); 178 void SyncTransformFeedback();
@@ -172,13 +180,14 @@ private:
172 /// Syncs the point state to match the guest state 180 /// Syncs the point state to match the guest state
173 void SyncPointState(); 181 void SyncPointState();
174 182
183 /// Syncs Color Mask
184 void SyncColorMask();
185
175 /// Check asserts for alpha testing. 186 /// Check asserts for alpha testing.
176 void CheckAlphaTests(); 187 void CheckAlphaTests();
177 188
178 bool has_ARB_direct_state_access = false; 189 bool has_ARB_direct_state_access = false;
179 bool has_ARB_multi_bind = false; 190 bool has_ARB_multi_bind = false;
180 bool has_ARB_separate_shader_objects = false;
181 bool has_ARB_vertex_attrib_binding = false;
182 191
183 OpenGLState state; 192 OpenGLState state;
184 193
@@ -207,7 +216,8 @@ private:
207 216
208 std::size_t CalculateIndexBufferSize() const; 217 std::size_t CalculateIndexBufferSize() const;
209 218
210 void SetupVertexArrays(); 219 void SetupVertexFormat();
220 void SetupVertexBuffer();
211 221
212 DrawParameters SetupDraw(); 222 DrawParameters SetupDraw();
213 223
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index f194a7687..4f434fc31 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,7 +15,9 @@
15#include "core/memory.h" 15#include "core/memory.h"
16#include "core/settings.h" 16#include "core/settings.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/renderer_opengl/gl_rasterizer.h"
18#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 19#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
20#include "video_core/renderer_opengl/gl_state.h"
19#include "video_core/renderer_opengl/utils.h" 21#include "video_core/renderer_opengl/utils.h"
20#include "video_core/surface.h" 22#include "video_core/surface.h"
21#include "video_core/textures/astc.h" 23#include "video_core/textures/astc.h"
@@ -58,16 +60,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
58 60
59std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only, 61std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
60 bool uncompressed) const { 62 bool uncompressed) const {
61 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 63 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
64 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
62 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; 65 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
63 u32 m_depth = (layer_only ? 1U : depth); 66 u32 m_depth = (layer_only ? 1U : depth);
64 u32 m_width = MipWidth(mip_level); 67 u32 m_width = MipWidth(mip_level);
65 u32 m_height = MipHeight(mip_level); 68 u32 m_height = MipHeight(mip_level);
66 m_width = uncompressed ? m_width 69 m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
67 : std::max(1U, (m_width + compression_factor - 1) / compression_factor); 70 m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
68 m_height = uncompressed
69 ? m_height
70 : std::max(1U, (m_height + compression_factor - 1) / compression_factor);
71 m_depth = std::max(1U, m_depth >> mip_level); 71 m_depth = std::max(1U, m_depth >> mip_level);
72 u32 m_block_height = MipBlockHeight(mip_level); 72 u32 m_block_height = MipBlockHeight(mip_level);
73 u32 m_block_depth = MipBlockDepth(mip_level); 73 u32 m_block_depth = MipBlockDepth(mip_level);
@@ -128,6 +128,13 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
128 params.target = SurfaceTarget::Texture2D; 128 params.target = SurfaceTarget::Texture2D;
129 } 129 }
130 break; 130 break;
131 case SurfaceTarget::TextureCubeArray:
132 params.depth = config.tic.Depth() * 6;
133 if (!entry.IsArray()) {
134 ASSERT(params.depth == 6);
135 params.target = SurfaceTarget::TextureCubemap;
136 }
137 break;
131 default: 138 default:
132 LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target)); 139 LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
133 UNREACHABLE(); 140 UNREACHABLE();
@@ -258,11 +265,11 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
258 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 265 {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
259 true}, // DXN2UNORM 266 true}, // DXN2UNORM
260 {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM 267 {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
261 {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 268 {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
262 true}, // BC7U 269 true}, // BC7U
263 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, 270 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
264 ComponentType::Float, true}, // BC6H_UF16 271 true}, // BC6H_UF16
265 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, 272 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
266 true}, // BC6H_SF16 273 true}, // BC6H_SF16
267 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 274 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
268 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U 275 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
@@ -299,12 +306,16 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
299 true}, // DXT23_SRGB 306 true}, // DXT23_SRGB
300 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 307 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
301 true}, // DXT45_SRGB 308 true}, // DXT45_SRGB
302 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 309 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
303 ComponentType::UNorm, true}, // BC7U_SRGB 310 true}, // BC7U_SRGB
304 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB 311 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
305 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB 312 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
306 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB 313 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
307 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB 314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
315 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
316 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
317 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
318 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
308 319
309 // Depth formats 320 // Depth formats
310 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 321 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -334,6 +345,8 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) {
334 return GL_TEXTURE_2D_ARRAY; 345 return GL_TEXTURE_2D_ARRAY;
335 case SurfaceTarget::TextureCubemap: 346 case SurfaceTarget::TextureCubemap:
336 return GL_TEXTURE_CUBE_MAP; 347 return GL_TEXTURE_CUBE_MAP;
348 case SurfaceTarget::TextureCubeArray:
349 return GL_TEXTURE_CUBE_MAP_ARRAY;
337 } 350 }
338 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); 351 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
339 UNREACHABLE(); 352 UNREACHABLE();
@@ -364,15 +377,15 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
364 377
365 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 378 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
366 // pixel values. 379 // pixel values.
367 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 380 const u32 tile_size_x{GetDefaultBlockWidth(format)};
381 const u32 tile_size_y{GetDefaultBlockHeight(format)};
368 382
369 if (morton_to_gl) { 383 if (morton_to_gl) {
370 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 384 Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
371 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 385 stride, height, depth, block_height, block_depth);
372 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
373 memcpy(gl_buffer, data.data(), size_to_copy);
374 } else { 386 } else {
375 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth, 387 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
388 (height + tile_size_y - 1) / tile_size_y, depth,
376 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), 389 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
377 gl_buffer, false, block_height, block_depth); 390 gl_buffer, false, block_height, block_depth);
378 } 391 }
@@ -440,6 +453,10 @@ static constexpr GLConversionArray morton_to_gl_fns = {
440 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, 453 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
441 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, 454 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
442 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 455 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
456 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
457 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
458 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
459 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
443 MortonCopy<true, PixelFormat::Z32F>, 460 MortonCopy<true, PixelFormat::Z32F>,
444 MortonCopy<true, PixelFormat::Z16>, 461 MortonCopy<true, PixelFormat::Z16>,
445 MortonCopy<true, PixelFormat::Z24S8>, 462 MortonCopy<true, PixelFormat::Z24S8>,
@@ -508,6 +525,10 @@ static constexpr GLConversionArray gl_to_morton_fns = {
508 nullptr, 525 nullptr,
509 nullptr, 526 nullptr,
510 nullptr, 527 nullptr,
528 nullptr,
529 nullptr,
530 nullptr,
531 nullptr,
511 MortonCopy<false, PixelFormat::Z32F>, 532 MortonCopy<false, PixelFormat::Z32F>,
512 MortonCopy<false, PixelFormat::Z16>, 533 MortonCopy<false, PixelFormat::Z16>,
513 MortonCopy<false, PixelFormat::Z24S8>, 534 MortonCopy<false, PixelFormat::Z24S8>,
@@ -526,8 +547,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
526 if (params.is_layered) { 547 if (params.is_layered) {
527 u64 offset = params.GetMipmapLevelOffset(mip_level); 548 u64 offset = params.GetMipmapLevelOffset(mip_level);
528 u64 offset_gl = 0; 549 u64 offset_gl = 0;
529 u64 layer_size = params.LayerMemorySize(); 550 const u64 layer_size = params.LayerMemorySize();
530 u64 gl_size = params.LayerSizeGL(mip_level); 551 const u64 gl_size = params.LayerSizeGL(mip_level);
531 for (u32 i = 0; i < params.depth; i++) { 552 for (u32 i = 0; i < params.depth; i++) {
532 functions[static_cast<std::size_t>(params.pixel_format)]( 553 functions[static_cast<std::size_t>(params.pixel_format)](
533 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 554 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
@@ -537,7 +558,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
537 offset_gl += gl_size; 558 offset_gl += gl_size;
538 } 559 }
539 } else { 560 } else {
540 u64 offset = params.GetMipmapLevelOffset(mip_level); 561 const u64 offset = params.GetMipmapLevelOffset(mip_level);
541 functions[static_cast<std::size_t>(params.pixel_format)]( 562 functions[static_cast<std::size_t>(params.pixel_format)](
542 params.MipWidth(mip_level), params.MipBlockHeight(mip_level), 563 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
543 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), 564 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
@@ -545,9 +566,11 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
545 } 566 }
546} 567}
547 568
569MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64));
548static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, 570static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
549 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, 571 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
550 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 572 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
573 MICROPROFILE_SCOPE(OpenGL_BlitSurface);
551 574
552 const auto& src_params{src_surface->GetSurfaceParams()}; 575 const auto& src_params{src_surface->GetSurfaceParams()};
553 const auto& dst_params{dst_surface->GetSurfaceParams()}; 576 const auto& dst_params{dst_surface->GetSurfaceParams()};
@@ -560,7 +583,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
560 state.draw.draw_framebuffer = draw_fb_handle; 583 state.draw.draw_framebuffer = draw_fb_handle;
561 // Set sRGB enabled if the destination surfaces need it 584 // Set sRGB enabled if the destination surfaces need it
562 state.framebuffer_srgb.enabled = dst_params.srgb_conversion; 585 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
563 state.Apply(); 586 state.ApplyFramebufferState();
564 587
565 u32 buffers{}; 588 u32 buffers{};
566 589
@@ -687,21 +710,23 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
687 0, 0, width, height, 1); 710 0, 0, width, height, 1);
688} 711}
689 712
713MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
690static void CopySurface(const Surface& src_surface, const Surface& dst_surface, 714static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
691 GLuint copy_pbo_handle, GLenum src_attachment = 0, 715 const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
692 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 716 const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
717 MICROPROFILE_SCOPE(OpenGL_CopySurface);
693 ASSERT_MSG(dst_attachment == 0, "Unimplemented"); 718 ASSERT_MSG(dst_attachment == 0, "Unimplemented");
694 719
695 const auto& src_params{src_surface->GetSurfaceParams()}; 720 const auto& src_params{src_surface->GetSurfaceParams()};
696 const auto& dst_params{dst_surface->GetSurfaceParams()}; 721 const auto& dst_params{dst_surface->GetSurfaceParams()};
697 722
698 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 723 const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
699 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 724 const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
700 725
701 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); 726 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
702 727
703 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 728 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
704 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 729 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW);
705 if (source_format.compressed) { 730 if (source_format.compressed) {
706 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 731 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
707 static_cast<GLsizei>(src_params.size_in_bytes), nullptr); 732 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
@@ -722,13 +747,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
722 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 747 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
723 "reinterpretation but the texture is tiled."); 748 "reinterpretation but the texture is tiled.");
724 } 749 }
725 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 750 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
726 std::vector<u8> data(remaining_size);
727 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
728 data.size());
729 751
730 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 752 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
731 data.data()); 753 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
732 } 754 }
733 755
734 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 756 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -754,6 +776,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
754 break; 776 break;
755 case SurfaceTarget::Texture3D: 777 case SurfaceTarget::Texture3D:
756 case SurfaceTarget::Texture2DArray: 778 case SurfaceTarget::Texture2DArray:
779 case SurfaceTarget::TextureCubeArray:
757 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, 780 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
758 static_cast<GLsizei>(dst_params.depth), dest_format.format, 781 static_cast<GLsizei>(dst_params.depth), dest_format.format,
759 dest_format.type, nullptr); 782 dest_format.type, nullptr);
@@ -806,6 +829,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
806 break; 829 break;
807 case SurfaceTarget::Texture3D: 830 case SurfaceTarget::Texture3D:
808 case SurfaceTarget::Texture2DArray: 831 case SurfaceTarget::Texture2DArray:
832 case SurfaceTarget::TextureCubeArray:
809 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, 833 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
810 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), 834 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
811 params.depth); 835 params.depth);
@@ -897,21 +921,26 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
897 * typical desktop GPUs. 921 * typical desktop GPUs.
898 */ 922 */
899static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 923static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
900 u32 width, u32 height) { 924 u32 width, u32 height, u32 depth) {
901 switch (pixel_format) { 925 switch (pixel_format) {
902 case PixelFormat::ASTC_2D_4X4: 926 case PixelFormat::ASTC_2D_4X4:
903 case PixelFormat::ASTC_2D_8X8: 927 case PixelFormat::ASTC_2D_8X8:
904 case PixelFormat::ASTC_2D_8X5: 928 case PixelFormat::ASTC_2D_8X5:
905 case PixelFormat::ASTC_2D_5X4: 929 case PixelFormat::ASTC_2D_5X4:
930 case PixelFormat::ASTC_2D_5X5:
906 case PixelFormat::ASTC_2D_4X4_SRGB: 931 case PixelFormat::ASTC_2D_4X4_SRGB:
907 case PixelFormat::ASTC_2D_8X8_SRGB: 932 case PixelFormat::ASTC_2D_8X8_SRGB:
908 case PixelFormat::ASTC_2D_8X5_SRGB: 933 case PixelFormat::ASTC_2D_8X5_SRGB:
909 case PixelFormat::ASTC_2D_5X4_SRGB: { 934 case PixelFormat::ASTC_2D_5X4_SRGB:
935 case PixelFormat::ASTC_2D_5X5_SRGB:
936 case PixelFormat::ASTC_2D_10X8:
937 case PixelFormat::ASTC_2D_10X8_SRGB: {
910 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 938 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
911 u32 block_width{}; 939 u32 block_width{};
912 u32 block_height{}; 940 u32 block_height{};
913 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 941 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
914 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); 942 data =
943 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
915 break; 944 break;
916 } 945 }
917 case PixelFormat::S8Z24: 946 case PixelFormat::S8Z24:
@@ -940,7 +969,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
940 case PixelFormat::ASTC_2D_4X4: 969 case PixelFormat::ASTC_2D_4X4:
941 case PixelFormat::ASTC_2D_8X8: 970 case PixelFormat::ASTC_2D_8X8:
942 case PixelFormat::ASTC_2D_4X4_SRGB: 971 case PixelFormat::ASTC_2D_4X4_SRGB:
943 case PixelFormat::ASTC_2D_8X8_SRGB: { 972 case PixelFormat::ASTC_2D_8X8_SRGB:
973 case PixelFormat::ASTC_2D_5X5:
974 case PixelFormat::ASTC_2D_5X5_SRGB:
975 case PixelFormat::ASTC_2D_10X8:
976 case PixelFormat::ASTC_2D_10X8_SRGB: {
944 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", 977 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
945 static_cast<u32>(pixel_format)); 978 static_cast<u32>(pixel_format));
946 UNREACHABLE(); 979 UNREACHABLE();
@@ -953,7 +986,7 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
953 } 986 }
954} 987}
955 988
956MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 989MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 192, 64));
957void CachedSurface::LoadGLBuffer() { 990void CachedSurface::LoadGLBuffer() {
958 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 991 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
959 gl_buffer.resize(params.max_mip_level); 992 gl_buffer.resize(params.max_mip_level);
@@ -971,7 +1004,7 @@ void CachedSurface::LoadGLBuffer() {
971 } 1004 }
972 for (u32 i = 0; i < params.max_mip_level; i++) 1005 for (u32 i = 0; i < params.max_mip_level; i++)
973 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), 1006 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
974 params.MipHeight(i)); 1007 params.MipHeight(i), params.MipDepth(i));
975} 1008}
976 1009
977MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 1010MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
@@ -1055,6 +1088,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1055 &gl_buffer[mip_map][buffer_offset]); 1088 &gl_buffer[mip_map][buffer_offset]);
1056 break; 1089 break;
1057 case SurfaceTarget::Texture2DArray: 1090 case SurfaceTarget::Texture2DArray:
1091 case SurfaceTarget::TextureCubeArray:
1058 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, 1092 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
1059 static_cast<GLsizei>(params.MipWidth(mip_map)), 1093 static_cast<GLsizei>(params.MipWidth(mip_map)),
1060 static_cast<GLsizei>(params.MipHeight(mip_map)), 1094 static_cast<GLsizei>(params.MipHeight(mip_map)),
@@ -1104,6 +1138,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1104 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); 1138 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
1105 break; 1139 break;
1106 case SurfaceTarget::Texture2DArray: 1140 case SurfaceTarget::Texture2DArray:
1141 case SurfaceTarget::TextureCubeArray:
1107 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, 1142 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
1108 static_cast<GLsizei>(rect.GetWidth()), 1143 static_cast<GLsizei>(rect.GetWidth()),
1109 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, 1144 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
@@ -1133,7 +1168,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1133 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 1168 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1134} 1169}
1135 1170
1136MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 1171MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
1137void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { 1172void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
1138 if (params.type == SurfaceType::Fill) 1173 if (params.type == SurfaceType::Fill)
1139 return; 1174 return;
@@ -1144,7 +1179,8 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
1144 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); 1179 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle);
1145} 1180}
1146 1181
1147RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 1182RasterizerCacheOpenGL::RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer)
1183 : RasterizerCache{rasterizer} {
1148 read_framebuffer.Create(); 1184 read_framebuffer.Create();
1149 draw_framebuffer.Create(); 1185 draw_framebuffer.Create();
1150 copy_pbo.Create(); 1186 copy_pbo.Create();
@@ -1239,6 +1275,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
1239 return surface; 1275 return surface;
1240} 1276}
1241 1277
1278void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
1279 const Surface& dst_surface) {
1280 const auto& init_params{src_surface->GetSurfaceParams()};
1281 const auto& dst_params{dst_surface->GetSurfaceParams()};
1282 VAddr address = init_params.addr;
1283 const std::size_t layer_size = dst_params.LayerMemorySize();
1284 for (u32 layer = 0; layer < dst_params.depth; layer++) {
1285 for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
1286 const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
1287 const Surface& copy = TryGet(sub_address);
1288 if (!copy)
1289 continue;
1290 const auto& src_params{copy->GetSurfaceParams()};
1291 const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
1292 const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
1293
1294 glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0,
1295 0, 0, dst_surface->Texture().handle,
1296 SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width,
1297 height, 1);
1298 }
1299 address += layer_size;
1300 }
1301}
1302
1242void RasterizerCacheOpenGL::FermiCopySurface( 1303void RasterizerCacheOpenGL::FermiCopySurface(
1243 const Tegra::Engines::Fermi2D::Regs::Surface& src_config, 1304 const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
1244 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) { 1305 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
@@ -1304,10 +1365,14 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1304 CopySurface(old_surface, new_surface, copy_pbo.handle); 1365 CopySurface(old_surface, new_surface, copy_pbo.handle);
1305 } 1366 }
1306 break; 1367 break;
1307 case SurfaceTarget::TextureCubemap:
1308 case SurfaceTarget::Texture3D: 1368 case SurfaceTarget::Texture3D:
1309 AccurateCopySurface(old_surface, new_surface); 1369 AccurateCopySurface(old_surface, new_surface);
1310 break; 1370 break;
1371 case SurfaceTarget::TextureCubemap:
1372 case SurfaceTarget::Texture2DArray:
1373 case SurfaceTarget::TextureCubeArray:
1374 FastLayeredCopySurface(old_surface, new_surface);
1375 break;
1311 default: 1376 default:
1312 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1377 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1313 static_cast<u32>(new_params.target)); 1378 static_cast<u32>(new_params.target));
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index f255f4419..9ac79c5a4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -49,6 +49,8 @@ struct SurfaceParams {
49 return "Texture2DArray"; 49 return "Texture2DArray";
50 case SurfaceTarget::TextureCubemap: 50 case SurfaceTarget::TextureCubemap:
51 return "TextureCubemap"; 51 return "TextureCubemap";
52 case SurfaceTarget::TextureCubeArray:
53 return "TextureCubeArray";
52 default: 54 default:
53 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 55 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
54 UNREACHABLE(); 56 UNREACHABLE();
@@ -139,7 +141,7 @@ struct SurfaceParams {
139 } 141 }
140 142
141 u32 MipDepth(u32 mip_level) const { 143 u32 MipDepth(u32 mip_level) const {
142 return std::max(1U, depth >> mip_level); 144 return is_layered ? depth : std::max(1U, depth >> mip_level);
143 } 145 }
144 146
145 // Auto block resizing algorithm from: 147 // Auto block resizing algorithm from:
@@ -262,6 +264,8 @@ struct hash<SurfaceReserveKey> {
262 264
263namespace OpenGL { 265namespace OpenGL {
264 266
267class RasterizerOpenGL;
268
265class CachedSurface final : public RasterizerCacheObject { 269class CachedSurface final : public RasterizerCacheObject {
266public: 270public:
267 CachedSurface(const SurfaceParams& params); 271 CachedSurface(const SurfaceParams& params);
@@ -309,7 +313,7 @@ private:
309 313
310class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 314class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
311public: 315public:
312 RasterizerCacheOpenGL(); 316 explicit RasterizerCacheOpenGL(RasterizerOpenGL& rasterizer);
313 317
314 /// Get a surface based on the texture configuration 318 /// Get a surface based on the texture configuration
315 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config, 319 Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
@@ -346,6 +350,7 @@ private:
346 350
347 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data 351 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
348 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); 352 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
353 void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
349 354
350 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 355 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
351 /// previously been used. This is to prevent surfaces from being constantly created and 356 /// previously been used. This is to prevent surfaces from being constantly created and
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
new file mode 100644
index 000000000..c17d5ac00
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -0,0 +1,186 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <utility>
6#include <glad/glad.h>
7#include "common/common_types.h"
8#include "common/microprofile.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/renderer_opengl/gl_shader_util.h"
11#include "video_core/renderer_opengl/gl_state.h"
12
13MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
14MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
15
16namespace OpenGL {
17
18void OGLTexture::Create() {
19 if (handle != 0)
20 return;
21
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenTextures(1, &handle);
24}
25
26void OGLTexture::Release() {
27 if (handle == 0)
28 return;
29
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteTextures(1, &handle);
32 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
33 handle = 0;
34}
35
36void OGLSampler::Create() {
37 if (handle != 0)
38 return;
39
40 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
41 glGenSamplers(1, &handle);
42}
43
44void OGLSampler::Release() {
45 if (handle == 0)
46 return;
47
48 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
49 glDeleteSamplers(1, &handle);
50 OpenGLState::GetCurState().ResetSampler(handle).Apply();
51 handle = 0;
52}
53
54void OGLShader::Create(const char* source, GLenum type) {
55 if (handle != 0)
56 return;
57 if (source == nullptr)
58 return;
59
60 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
61 handle = GLShader::LoadShader(source, type);
62}
63
64void OGLShader::Release() {
65 if (handle == 0)
66 return;
67
68 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
69 glDeleteShader(handle);
70 handle = 0;
71}
72
73void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader,
74 const char* frag_shader, bool separable_program) {
75 OGLShader vert, geo, frag;
76 if (vert_shader)
77 vert.Create(vert_shader, GL_VERTEX_SHADER);
78 if (geo_shader)
79 geo.Create(geo_shader, GL_GEOMETRY_SHADER);
80 if (frag_shader)
81 frag.Create(frag_shader, GL_FRAGMENT_SHADER);
82
83 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
84 Create(separable_program, vert.handle, geo.handle, frag.handle);
85}
86
87void OGLProgram::Release() {
88 if (handle == 0)
89 return;
90
91 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
92 glDeleteProgram(handle);
93 OpenGLState::GetCurState().ResetProgram(handle).Apply();
94 handle = 0;
95}
96
97void OGLPipeline::Create() {
98 if (handle != 0)
99 return;
100
101 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
102 glGenProgramPipelines(1, &handle);
103}
104
105void OGLPipeline::Release() {
106 if (handle == 0)
107 return;
108
109 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
110 glDeleteProgramPipelines(1, &handle);
111 OpenGLState::GetCurState().ResetPipeline(handle).Apply();
112 handle = 0;
113}
114
115void OGLBuffer::Create() {
116 if (handle != 0)
117 return;
118
119 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
120 glGenBuffers(1, &handle);
121}
122
123void OGLBuffer::Release() {
124 if (handle == 0)
125 return;
126
127 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
128 glDeleteBuffers(1, &handle);
129 OpenGLState::GetCurState().ResetBuffer(handle).Apply();
130 handle = 0;
131}
132
133void OGLSync::Create() {
134 if (handle != 0)
135 return;
136
137 // Don't profile here, this one is expected to happen ingame.
138 handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
139}
140
141void OGLSync::Release() {
142 if (handle == 0)
143 return;
144
145 // Don't profile here, this one is expected to happen ingame.
146 glDeleteSync(handle);
147 handle = 0;
148}
149
150void OGLVertexArray::Create() {
151 if (handle != 0)
152 return;
153
154 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
155 glGenVertexArrays(1, &handle);
156}
157
158void OGLVertexArray::Release() {
159 if (handle == 0)
160 return;
161
162 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
163 glDeleteVertexArrays(1, &handle);
164 OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
165 handle = 0;
166}
167
168void OGLFramebuffer::Create() {
169 if (handle != 0)
170 return;
171
172 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
173 glGenFramebuffers(1, &handle);
174}
175
176void OGLFramebuffer::Release() {
177 if (handle == 0)
178 return;
179
180 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
181 glDeleteFramebuffers(1, &handle);
182 OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
183 handle = 0;
184}
185
186} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 3bc1b83b5..e33f1e973 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -8,7 +8,6 @@
8#include <glad/glad.h> 8#include <glad/glad.h>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_opengl/gl_shader_util.h" 10#include "video_core/renderer_opengl/gl_shader_util.h"
11#include "video_core/renderer_opengl/gl_state.h"
12 11
13namespace OpenGL { 12namespace OpenGL {
14 13
@@ -29,20 +28,10 @@ public:
29 } 28 }
30 29
31 /// Creates a new internal OpenGL resource and stores the handle 30 /// Creates a new internal OpenGL resource and stores the handle
32 void Create() { 31 void Create();
33 if (handle != 0)
34 return;
35 glGenTextures(1, &handle);
36 }
37 32
38 /// Deletes the internal OpenGL resource 33 /// Deletes the internal OpenGL resource
39 void Release() { 34 void Release();
40 if (handle == 0)
41 return;
42 glDeleteTextures(1, &handle);
43 OpenGLState::GetCurState().UnbindTexture(handle).Apply();
44 handle = 0;
45 }
46 35
47 GLuint handle = 0; 36 GLuint handle = 0;
48}; 37};
@@ -64,20 +53,10 @@ public:
64 } 53 }
65 54
66 /// Creates a new internal OpenGL resource and stores the handle 55 /// Creates a new internal OpenGL resource and stores the handle
67 void Create() { 56 void Create();
68 if (handle != 0)
69 return;
70 glGenSamplers(1, &handle);
71 }
72 57
73 /// Deletes the internal OpenGL resource 58 /// Deletes the internal OpenGL resource
74 void Release() { 59 void Release();
75 if (handle == 0)
76 return;
77 glDeleteSamplers(1, &handle);
78 OpenGLState::GetCurState().ResetSampler(handle).Apply();
79 handle = 0;
80 }
81 60
82 GLuint handle = 0; 61 GLuint handle = 0;
83}; 62};
@@ -98,20 +77,9 @@ public:
98 return *this; 77 return *this;
99 } 78 }
100 79
101 void Create(const char* source, GLenum type) { 80 void Create(const char* source, GLenum type);
102 if (handle != 0)
103 return;
104 if (source == nullptr)
105 return;
106 handle = GLShader::LoadShader(source, type);
107 }
108 81
109 void Release() { 82 void Release();
110 if (handle == 0)
111 return;
112 glDeleteShader(handle);
113 handle = 0;
114 }
115 83
116 GLuint handle = 0; 84 GLuint handle = 0;
117}; 85};
@@ -141,25 +109,10 @@ public:
141 109
142 /// Creates a new internal OpenGL resource and stores the handle 110 /// Creates a new internal OpenGL resource and stores the handle
143 void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, 111 void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader,
144 bool separable_program = false) { 112 bool separable_program = false);
145 OGLShader vert, geo, frag;
146 if (vert_shader)
147 vert.Create(vert_shader, GL_VERTEX_SHADER);
148 if (geo_shader)
149 geo.Create(geo_shader, GL_GEOMETRY_SHADER);
150 if (frag_shader)
151 frag.Create(frag_shader, GL_FRAGMENT_SHADER);
152 Create(separable_program, vert.handle, geo.handle, frag.handle);
153 }
154 113
155 /// Deletes the internal OpenGL resource 114 /// Deletes the internal OpenGL resource
156 void Release() { 115 void Release();
157 if (handle == 0)
158 return;
159 glDeleteProgram(handle);
160 OpenGLState::GetCurState().ResetProgram(handle).Apply();
161 handle = 0;
162 }
163 116
164 GLuint handle = 0; 117 GLuint handle = 0;
165}; 118};
@@ -178,20 +131,10 @@ public:
178 } 131 }
179 132
180 /// Creates a new internal OpenGL resource and stores the handle 133 /// Creates a new internal OpenGL resource and stores the handle
181 void Create() { 134 void Create();
182 if (handle != 0)
183 return;
184 glGenProgramPipelines(1, &handle);
185 }
186 135
187 /// Deletes the internal OpenGL resource 136 /// Deletes the internal OpenGL resource
188 void Release() { 137 void Release();
189 if (handle == 0)
190 return;
191 glDeleteProgramPipelines(1, &handle);
192 OpenGLState::GetCurState().ResetPipeline(handle).Apply();
193 handle = 0;
194 }
195 138
196 GLuint handle = 0; 139 GLuint handle = 0;
197}; 140};
@@ -213,20 +156,10 @@ public:
213 } 156 }
214 157
215 /// Creates a new internal OpenGL resource and stores the handle 158 /// Creates a new internal OpenGL resource and stores the handle
216 void Create() { 159 void Create();
217 if (handle != 0)
218 return;
219 glGenBuffers(1, &handle);
220 }
221 160
222 /// Deletes the internal OpenGL resource 161 /// Deletes the internal OpenGL resource
223 void Release() { 162 void Release();
224 if (handle == 0)
225 return;
226 glDeleteBuffers(1, &handle);
227 OpenGLState::GetCurState().ResetBuffer(handle).Apply();
228 handle = 0;
229 }
230 163
231 GLuint handle = 0; 164 GLuint handle = 0;
232}; 165};
@@ -247,19 +180,10 @@ public:
247 } 180 }
248 181
249 /// Creates a new internal OpenGL resource and stores the handle 182 /// Creates a new internal OpenGL resource and stores the handle
250 void Create() { 183 void Create();
251 if (handle != 0)
252 return;
253 handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
254 }
255 184
256 /// Deletes the internal OpenGL resource 185 /// Deletes the internal OpenGL resource
257 void Release() { 186 void Release();
258 if (handle == 0)
259 return;
260 glDeleteSync(handle);
261 handle = 0;
262 }
263 187
264 GLsync handle = 0; 188 GLsync handle = 0;
265}; 189};
@@ -281,20 +205,10 @@ public:
281 } 205 }
282 206
283 /// Creates a new internal OpenGL resource and stores the handle 207 /// Creates a new internal OpenGL resource and stores the handle
284 void Create() { 208 void Create();
285 if (handle != 0)
286 return;
287 glGenVertexArrays(1, &handle);
288 }
289 209
290 /// Deletes the internal OpenGL resource 210 /// Deletes the internal OpenGL resource
291 void Release() { 211 void Release();
292 if (handle == 0)
293 return;
294 glDeleteVertexArrays(1, &handle);
295 OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
296 handle = 0;
297 }
298 212
299 GLuint handle = 0; 213 GLuint handle = 0;
300}; 214};
@@ -316,20 +230,10 @@ public:
316 } 230 }
317 231
318 /// Creates a new internal OpenGL resource and stores the handle 232 /// Creates a new internal OpenGL resource and stores the handle
319 void Create() { 233 void Create();
320 if (handle != 0)
321 return;
322 glGenFramebuffers(1, &handle);
323 }
324 234
325 /// Deletes the internal OpenGL resource 235 /// Deletes the internal OpenGL resource
326 void Release() { 236 void Release();
327 if (handle == 0)
328 return;
329 glDeleteFramebuffers(1, &handle);
330 OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
331 handle = 0;
332 }
333 237
334 GLuint handle = 0; 238 GLuint handle = 0;
335}; 239};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 9522fd344..038b25c75 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -6,10 +6,10 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "core/memory.h" 7#include "core/memory.h"
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_opengl/gl_rasterizer.h"
9#include "video_core/renderer_opengl/gl_shader_cache.h" 10#include "video_core/renderer_opengl/gl_shader_cache.h"
10#include "video_core/renderer_opengl/gl_shader_manager.h" 11#include "video_core/renderer_opengl/gl_shader_manager.h"
11#include "video_core/renderer_opengl/utils.h" 12#include "video_core/renderer_opengl/utils.h"
12#include "video_core/utils.h"
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15
@@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
84 } 84 }
85 85
86 entries = program_result.second; 86 entries = program_result.second;
87 shader_length = entries.shader_length;
87 88
88 if (program_type != Maxwell::ShaderProgram::Geometry) { 89 if (program_type != Maxwell::ShaderProgram::Geometry) {
89 OGLShader shader; 90 OGLShader shader;
@@ -121,12 +122,16 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
121} 122}
122 123
123GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, 124GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
124 const std::string& glsl_topology, 125 const std::string& glsl_topology, u32 max_vertices,
125 const std::string& debug_name) { 126 const std::string& debug_name) {
126 if (target_program.handle != 0) { 127 if (target_program.handle != 0) {
127 return target_program.handle; 128 return target_program.handle;
128 } 129 }
129 const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"}; 130 std::string source = "#version 430 core\n";
131 source += "layout (" + glsl_topology + ") in;\n";
132 source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n';
133 source += geometry_programs.code;
134
130 OGLShader shader; 135 OGLShader shader;
131 shader.Create(source.c_str(), GL_GEOMETRY_SHADER); 136 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
132 target_program.Create(true, shader.handle); 137 target_program.Create(true, shader.handle);
@@ -135,6 +140,8 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
135 return target_program.handle; 140 return target_program.handle;
136}; 141};
137 142
143ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {}
144
138Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 145Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
139 const VAddr program_addr{GetShaderAddress(program)}; 146 const VAddr program_addr{GetShaderAddress(program)};
140 147
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index a210f1731..08f470de3 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -16,6 +16,8 @@
16namespace OpenGL { 16namespace OpenGL {
17 17
18class CachedShader; 18class CachedShader;
19class RasterizerOpenGL;
20
19using Shader = std::shared_ptr<CachedShader>; 21using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 22using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 23
@@ -28,7 +30,7 @@ public:
28 } 30 }
29 31
30 std::size_t GetSizeInBytes() const override { 32 std::size_t GetSizeInBytes() const override {
31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 33 return shader_length;
32 } 34 }
33 35
34 // We do not have to flush this cache as things in it are never modified by us. 36 // We do not have to flush this cache as things in it are never modified by us.
@@ -46,22 +48,23 @@ public:
46 } 48 }
47 switch (primitive_mode) { 49 switch (primitive_mode) {
48 case GL_POINTS: 50 case GL_POINTS:
49 return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints"); 51 return LazyGeometryProgram(geometry_programs.points, "points", 1, "ShaderPoints");
50 case GL_LINES: 52 case GL_LINES:
51 case GL_LINE_STRIP: 53 case GL_LINE_STRIP:
52 return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines"); 54 return LazyGeometryProgram(geometry_programs.lines, "lines", 2, "ShaderLines");
53 case GL_LINES_ADJACENCY: 55 case GL_LINES_ADJACENCY:
54 case GL_LINE_STRIP_ADJACENCY: 56 case GL_LINE_STRIP_ADJACENCY:
55 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 57 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency", 4,
56 "ShaderLinesAdjacency"); 58 "ShaderLinesAdjacency");
57 case GL_TRIANGLES: 59 case GL_TRIANGLES:
58 case GL_TRIANGLE_STRIP: 60 case GL_TRIANGLE_STRIP:
59 case GL_TRIANGLE_FAN: 61 case GL_TRIANGLE_FAN:
60 return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles"); 62 return LazyGeometryProgram(geometry_programs.triangles, "triangles", 3,
63 "ShaderTriangles");
61 case GL_TRIANGLES_ADJACENCY: 64 case GL_TRIANGLES_ADJACENCY:
62 case GL_TRIANGLE_STRIP_ADJACENCY: 65 case GL_TRIANGLE_STRIP_ADJACENCY:
63 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency", 66 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
64 "ShaderLines"); 67 6, "ShaderTrianglesAdjacency");
65 default: 68 default:
66 UNREACHABLE_MSG("Unknown primitive mode."); 69 UNREACHABLE_MSG("Unknown primitive mode.");
67 } 70 }
@@ -76,9 +79,10 @@ public:
76private: 79private:
77 /// Generates a geometry shader or returns one that already exists. 80 /// Generates a geometry shader or returns one that already exists.
78 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, 81 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
79 const std::string& debug_name); 82 u32 max_vertices, const std::string& debug_name);
80 83
81 VAddr addr; 84 VAddr addr;
85 std::size_t shader_length;
82 Maxwell::ShaderProgram program_type; 86 Maxwell::ShaderProgram program_type;
83 GLShader::ShaderSetup setup; 87 GLShader::ShaderSetup setup;
84 GLShader::ShaderEntries entries; 88 GLShader::ShaderEntries entries;
@@ -104,6 +108,8 @@ private:
104 108
105class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 109class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
106public: 110public:
111 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer);
112
107 /// Gets the current specified shader stage program 113 /// Gets the current specified shader stage program
108 Shader GetStageProgram(Maxwell::ShaderProgram program); 114 Shader GetStageProgram(Maxwell::ShaderProgram program);
109}; 115};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 09b003c59..97b9028c5 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -34,6 +34,17 @@ constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
34constexpr u32 MAX_GEOMETRY_BUFFERS = 6; 34constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested 35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
36 36
37static const char* INTERNAL_FLAG_NAMES[] = {"zero_flag", "sign_flag", "carry_flag",
38 "overflow_flag"};
39
40enum class InternalFlag : u64 {
41 ZeroFlag = 0,
42 SignFlag = 1,
43 CarryFlag = 2,
44 OverflowFlag = 3,
45 Amount
46};
47
37class DecompileFail : public std::runtime_error { 48class DecompileFail : public std::runtime_error {
38public: 49public:
39 using std::runtime_error::runtime_error; 50 using std::runtime_error::runtime_error;
@@ -49,8 +60,7 @@ static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
49 case Tegra::Shader::OutputTopology::TriangleStrip: 60 case Tegra::Shader::OutputTopology::TriangleStrip:
50 return "triangle_strip"; 61 return "triangle_strip";
51 default: 62 default:
52 LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); 63 UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology));
53 UNREACHABLE();
54 return "points"; 64 return "points";
55 } 65 }
56} 66}
@@ -85,7 +95,8 @@ struct Subroutine {
85class ControlFlowAnalyzer { 95class ControlFlowAnalyzer {
86public: 96public:
87 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix) 97 ControlFlowAnalyzer(const ProgramCode& program_code, u32 main_offset, const std::string& suffix)
88 : program_code(program_code) { 98 : program_code(program_code), shader_coverage_begin(main_offset),
99 shader_coverage_end(main_offset + 1) {
89 100
90 // Recursively finds all subroutines. 101 // Recursively finds all subroutines.
91 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix); 102 const Subroutine& program_main = AddSubroutine(main_offset, PROGRAM_END, suffix);
@@ -97,10 +108,16 @@ public:
97 return std::move(subroutines); 108 return std::move(subroutines);
98 } 109 }
99 110
111 std::size_t GetShaderLength() const {
112 return shader_coverage_end * sizeof(u64);
113 }
114
100private: 115private:
101 const ProgramCode& program_code; 116 const ProgramCode& program_code;
102 std::set<Subroutine> subroutines; 117 std::set<Subroutine> subroutines;
103 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map; 118 std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
119 u32 shader_coverage_begin;
120 u32 shader_coverage_end;
104 121
105 /// Adds and analyzes a new subroutine if it is not added yet. 122 /// Adds and analyzes a new subroutine if it is not added yet.
106 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) { 123 const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
@@ -142,6 +159,9 @@ private:
142 return exit_method; 159 return exit_method;
143 160
144 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 161 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
162 shader_coverage_begin = std::min(shader_coverage_begin, offset);
163 shader_coverage_end = std::max(shader_coverage_end, offset + 1);
164
145 const Instruction instr = {program_code[offset]}; 165 const Instruction instr = {program_code[offset]};
146 if (const auto opcode = OpCode::Decode(instr)) { 166 if (const auto opcode = OpCode::Decode(instr)) {
147 switch (opcode->get().GetId()) { 167 switch (opcode->get().GetId()) {
@@ -167,8 +187,8 @@ private:
167 case OpCode::Id::SSY: 187 case OpCode::Id::SSY:
168 case OpCode::Id::PBK: { 188 case OpCode::Id::PBK: {
169 // The SSY and PBK use a similar encoding as the BRA instruction. 189 // The SSY and PBK use a similar encoding as the BRA instruction.
170 ASSERT_MSG(instr.bra.constant_buffer == 0, 190 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
171 "Constant buffer branching is not supported"); 191 "Constant buffer branching is not supported");
172 const u32 target = offset + instr.bra.GetBranchTarget(); 192 const u32 target = offset + instr.bra.GetBranchTarget();
173 labels.insert(target); 193 labels.insert(target);
174 // Continue scanning for an exit method. 194 // Continue scanning for an exit method.
@@ -258,14 +278,6 @@ private:
258 const std::string& suffix; 278 const std::string& suffix;
259}; 279};
260 280
261enum class InternalFlag : u64 {
262 ZeroFlag = 0,
263 CarryFlag = 1,
264 OverflowFlag = 2,
265 NaNFlag = 3,
266 Amount
267};
268
269/** 281/**
270 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state 282 * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state
271 * of all registers (e.g. whether they are currently being used as Floats or Integers), and 283 * of all registers (e.g. whether they are currently being used as Floats or Integers), and
@@ -299,8 +311,7 @@ public:
299 // Default - do nothing 311 // Default - do nothing
300 return value; 312 return value;
301 default: 313 default:
302 LOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size)); 314 UNIMPLEMENTED_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
303 UNREACHABLE();
304 } 315 }
305 } 316 }
306 317
@@ -363,7 +374,7 @@ public:
363 u64 value_num_components, bool is_saturated = false, 374 u64 value_num_components, bool is_saturated = false,
364 u64 dest_elem = 0, Register::Size size = Register::Size::Word, 375 u64 dest_elem = 0, Register::Size size = Register::Size::Word,
365 bool sets_cc = false) { 376 bool sets_cc = false) {
366 ASSERT_MSG(!is_saturated, "Unimplemented"); 377 UNIMPLEMENTED_IF(is_saturated);
367 378
368 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; 379 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
369 380
@@ -373,7 +384,7 @@ public:
373 if (sets_cc) { 384 if (sets_cc) {
374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; 385 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); 386 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
376 LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); 387 LOG_WARNING(HW_GPU, "Condition codes implementation is incomplete.");
377 } 388 }
378 } 389 }
379 390
@@ -392,7 +403,7 @@ public:
392 Tegra::Shader::HalfMerge merge, u64 dest_num_components, 403 Tegra::Shader::HalfMerge merge, u64 dest_num_components,
393 u64 value_num_components, bool is_saturated = false, 404 u64 value_num_components, bool is_saturated = false,
394 u64 dest_elem = 0) { 405 u64 dest_elem = 0) {
395 ASSERT_MSG(!is_saturated, "Unimplemented"); 406 UNIMPLEMENTED_IF(is_saturated);
396 407
397 const std::string result = [&]() { 408 const std::string result = [&]() {
398 switch (merge) { 409 switch (merge) {
@@ -456,24 +467,25 @@ public:
456 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); 467 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");");
457 } 468 }
458 469
459 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { 470 std::string GetConditionCode(const Tegra::Shader::ConditionCode cc) const {
460 switch (cc) { 471 switch (cc) {
461 case Tegra::Shader::ControlCode::NEU: 472 case Tegra::Shader::ConditionCode::NEU:
462 return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; 473 return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')';
463 default: 474 default:
464 LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); 475 UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc));
465 UNREACHABLE();
466 return "false"; 476 return "false";
467 } 477 }
468 } 478 }
469 479
470 std::string GetInternalFlag(const InternalFlag ii) const { 480 std::string GetInternalFlag(const InternalFlag flag) const {
471 const u32 code = static_cast<u32>(ii); 481 const auto index = static_cast<u32>(flag);
472 return "internalFlag_" + std::to_string(code) + suffix; 482 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
483
484 return std::string(INTERNAL_FLAG_NAMES[index]) + '_' + suffix;
473 } 485 }
474 486
475 void SetInternalFlag(const InternalFlag ii, const std::string& value) const { 487 void SetInternalFlag(const InternalFlag flag, const std::string& value) const {
476 shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); 488 shader.AddLine(GetInternalFlag(flag) + " = " + value + ';');
477 } 489 }
478 490
479 /** 491 /**
@@ -494,10 +506,10 @@ public:
494 // instruction for now. 506 // instruction for now.
495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { 507 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry 508 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
497 // shader. These instructions use a dirty register as buffer index. To avoid some 509 // shader. These instructions use a dirty register as buffer index, to avoid some
498 // drivers from complaining for the out of boundary writes, guard them. 510 // drivers from complaining about out of boundary writes, guard them.
499 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " + 511 const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
500 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'}; 512 std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
501 shader.AddLine("amem[" + buf_index + "][" + 513 shader.AddLine("amem[" + buf_index + "][" +
502 std::to_string(static_cast<u32>(attribute)) + ']' + 514 std::to_string(static_cast<u32>(attribute)) + ']' +
503 GetSwizzle(elem) + " = " + src + ';'); 515 GetSwizzle(elem) + " = " + src + ';');
@@ -624,8 +636,8 @@ private:
624 636
625 /// Generates declarations for internal flags. 637 /// Generates declarations for internal flags.
626 void GenerateInternalFlags() { 638 void GenerateInternalFlags() {
627 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 639 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
628 const InternalFlag code = static_cast<InternalFlag>(ii); 640 const InternalFlag code = static_cast<InternalFlag>(flag);
629 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 641 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
630 } 642 }
631 declarations.AddNewLine(); 643 declarations.AddNewLine();
@@ -761,8 +773,7 @@ private:
761 u64 dest_num_components, u64 value_num_components, u64 dest_elem, 773 u64 dest_num_components, u64 value_num_components, u64 dest_elem,
762 bool precise) { 774 bool precise) {
763 if (reg == Register::ZeroIndex) { 775 if (reg == Register::ZeroIndex) {
764 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); 776 // Setting RZ is a nop in hardware.
765 UNREACHABLE();
766 return; 777 return;
767 } 778 }
768 779
@@ -811,7 +822,11 @@ private:
811 std::optional<Register> vertex = {}) { 822 std::optional<Register> vertex = {}) {
812 auto GeometryPass = [&](const std::string& name) { 823 auto GeometryPass = [&](const std::string& name) {
813 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { 824 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
814 return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']'; 825 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games set
826 // an 0x80000000 index for those and the shader fails to build. Find out why this
827 // happens and what's its intent.
828 return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) +
829 " % MAX_VERTEX_INPUT]";
815 } 830 }
816 return name; 831 return name;
817 }; 832 };
@@ -843,16 +858,13 @@ private:
843 if (declr_input_attribute.count(attribute) == 0) { 858 if (declr_input_attribute.count(attribute) == 0) {
844 declr_input_attribute[attribute] = input_mode; 859 declr_input_attribute[attribute] = input_mode;
845 } else { 860 } else {
846 if (declr_input_attribute[attribute] != input_mode) { 861 UNIMPLEMENTED_IF_MSG(declr_input_attribute[attribute] != input_mode,
847 LOG_CRITICAL(HW_GPU, "Same Input multiple input modes"); 862 "Multiple input modes for the same attribute");
848 UNREACHABLE();
849 }
850 } 863 }
851 return GeometryPass("input_attribute_" + std::to_string(index)); 864 return GeometryPass("input_attribute_" + std::to_string(index));
852 } 865 }
853 866
854 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 867 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
855 UNREACHABLE();
856 } 868 }
857 869
858 return "vec4(0, 0, 0, 0)"; 870 return "vec4(0, 0, 0, 0)";
@@ -878,24 +890,20 @@ private:
878 break; 890 break;
879 } 891 }
880 default: { 892 default: {
881 LOG_CRITICAL(HW_GPU, "Unhandled Ipa InterpMode: {}", static_cast<u32>(interp_mode)); 893 UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode));
882 UNREACHABLE();
883 } 894 }
884 } 895 }
885 switch (sample_mode) { 896 switch (sample_mode) {
886 case Tegra::Shader::IpaSampleMode::Centroid: { 897 case Tegra::Shader::IpaSampleMode::Centroid:
887 // Note not implemented, it can be implemented with the "centroid " keyword in glsl; 898 // It can be implemented with the "centroid " keyword in glsl
888 LOG_CRITICAL(HW_GPU, "Ipa Sampler Mode: centroid, not implemented"); 899 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid");
889 UNREACHABLE();
890 break; 900 break;
891 } 901 case Tegra::Shader::IpaSampleMode::Default:
892 case Tegra::Shader::IpaSampleMode::Default: {
893 // Default, n/a 902 // Default, n/a
894 break; 903 break;
895 }
896 default: { 904 default: {
897 LOG_CRITICAL(HW_GPU, "Unhandled Ipa SampleMode: {}", static_cast<u32>(sample_mode)); 905 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode));
898 UNREACHABLE(); 906 break;
899 } 907 }
900 } 908 }
901 return out; 909 return out;
@@ -916,8 +924,7 @@ private:
916 return "output_attribute_" + std::to_string(index); 924 return "output_attribute_" + std::to_string(index);
917 } 925 }
918 926
919 LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); 927 UNIMPLEMENTED_MSG("Unhandled output attribute={}", index);
920 UNREACHABLE();
921 return {}; 928 return {};
922 } 929 }
923 } 930 }
@@ -947,9 +954,10 @@ private:
947class GLSLGenerator { 954class GLSLGenerator {
948public: 955public:
949 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code, 956 GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
950 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) 957 u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix,
958 std::size_t shader_length)
951 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 959 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
952 stage(stage), suffix(suffix) { 960 stage(stage), suffix(suffix), shader_length(shader_length) {
953 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); 961 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
954 local_memory_size = header.GetLocalMemorySize(); 962 local_memory_size = header.GetLocalMemorySize();
955 regs.SetLocalMemory(local_memory_size); 963 regs.SetLocalMemory(local_memory_size);
@@ -962,7 +970,7 @@ public:
962 970
963 /// Returns entries in the shader that are useful for external functions 971 /// Returns entries in the shader that are useful for external functions
964 ShaderEntries GetEntries() const { 972 ShaderEntries GetEntries() const {
965 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers()}; 973 return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length};
966 } 974 }
967 975
968private: 976private:
@@ -1067,19 +1075,26 @@ private:
1067 const std::string& op_a, const std::string& op_b) const { 1075 const std::string& op_a, const std::string& op_b) const {
1068 using Tegra::Shader::PredCondition; 1076 using Tegra::Shader::PredCondition;
1069 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = { 1077 static const std::unordered_map<PredCondition, const char*> PredicateComparisonStrings = {
1070 {PredCondition::LessThan, "<"}, {PredCondition::Equal, "=="}, 1078 {PredCondition::LessThan, "<"},
1071 {PredCondition::LessEqual, "<="}, {PredCondition::GreaterThan, ">"}, 1079 {PredCondition::Equal, "=="},
1072 {PredCondition::NotEqual, "!="}, {PredCondition::GreaterEqual, ">="}, 1080 {PredCondition::LessEqual, "<="},
1073 {PredCondition::LessThanWithNan, "<"}, {PredCondition::NotEqualWithNan, "!="}, 1081 {PredCondition::GreaterThan, ">"},
1074 {PredCondition::GreaterThanWithNan, ">"}, {PredCondition::GreaterEqualWithNan, ">="}}; 1082 {PredCondition::NotEqual, "!="},
1083 {PredCondition::GreaterEqual, ">="},
1084 {PredCondition::LessThanWithNan, "<"},
1085 {PredCondition::NotEqualWithNan, "!="},
1086 {PredCondition::LessEqualWithNan, "<="},
1087 {PredCondition::GreaterThanWithNan, ">"},
1088 {PredCondition::GreaterEqualWithNan, ">="}};
1075 1089
1076 const auto& comparison{PredicateComparisonStrings.find(condition)}; 1090 const auto& comparison{PredicateComparisonStrings.find(condition)};
1077 ASSERT_MSG(comparison != PredicateComparisonStrings.end(), 1091 UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonStrings.end(),
1078 "Unknown predicate comparison operation"); 1092 "Unknown predicate comparison operation");
1079 1093
1080 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'}; 1094 std::string predicate{'(' + op_a + ") " + comparison->second + " (" + op_b + ')'};
1081 if (condition == PredCondition::LessThanWithNan || 1095 if (condition == PredCondition::LessThanWithNan ||
1082 condition == PredCondition::NotEqualWithNan || 1096 condition == PredCondition::NotEqualWithNan ||
1097 condition == PredCondition::LessEqualWithNan ||
1083 condition == PredCondition::GreaterThanWithNan || 1098 condition == PredCondition::GreaterThanWithNan ||
1084 condition == PredCondition::GreaterEqualWithNan) { 1099 condition == PredCondition::GreaterEqualWithNan) {
1085 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')'; 1100 predicate += " || isnan(" + op_a + ") || isnan(" + op_b + ')';
@@ -1103,7 +1118,7 @@ private:
1103 }; 1118 };
1104 1119
1105 auto op = PredicateOperationStrings.find(operation); 1120 auto op = PredicateOperationStrings.find(operation);
1106 ASSERT_MSG(op != PredicateOperationStrings.end(), "Unknown predicate operation"); 1121 UNIMPLEMENTED_IF_MSG(op == PredicateOperationStrings.end(), "Unknown predicate operation");
1107 return op->second; 1122 return op->second;
1108 } 1123 }
1109 1124
@@ -1201,8 +1216,7 @@ private:
1201 break; 1216 break;
1202 } 1217 }
1203 default: 1218 default:
1204 LOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op)); 1219 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(logic_op));
1205 UNREACHABLE();
1206 } 1220 }
1207 1221
1208 if (dest != Tegra::Shader::Register::ZeroIndex) { 1222 if (dest != Tegra::Shader::Register::ZeroIndex) {
@@ -1220,9 +1234,8 @@ private:
1220 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0"); 1234 SetPredicate(static_cast<u64>(predicate), '(' + result + ") != 0");
1221 break; 1235 break;
1222 default: 1236 default:
1223 LOG_CRITICAL(HW_GPU, "Unimplemented predicate result mode: {}", 1237 UNIMPLEMENTED_MSG("Unimplemented predicate result mode: {}",
1224 static_cast<u32>(predicate_mode)); 1238 static_cast<u32>(predicate_mode));
1225 UNREACHABLE();
1226 } 1239 }
1227 } 1240 }
1228 1241
@@ -1253,14 +1266,7 @@ private:
1253 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1); 1266 regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
1254 } 1267 }
1255 1268
1256 void WriteTexsInstruction(const Instruction& instr, const std::string& coord, 1269 void WriteTexsInstruction(const Instruction& instr, const std::string& texture) {
1257 const std::string& texture) {
1258 // Add an extra scope and declare the texture coords inside to prevent
1259 // overwriting them in case they are used as outputs of the texs instruction.
1260 shader.AddLine('{');
1261 ++shader.scope;
1262 shader.AddLine(coord);
1263
1264 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle 1270 // TEXS has two destination registers and a swizzle. The first two elements in the swizzle
1265 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 1271 // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
1266 1272
@@ -1283,26 +1289,19 @@ private:
1283 1289
1284 ++written_components; 1290 ++written_components;
1285 } 1291 }
1286
1287 --shader.scope;
1288 shader.AddLine('}');
1289 } 1292 }
1290 1293
1291 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) { 1294 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
1292 switch (texture_type) { 1295 switch (texture_type) {
1293 case Tegra::Shader::TextureType::Texture1D: { 1296 case Tegra::Shader::TextureType::Texture1D:
1294 return 1; 1297 return 1;
1295 } 1298 case Tegra::Shader::TextureType::Texture2D:
1296 case Tegra::Shader::TextureType::Texture2D: {
1297 return 2; 1299 return 2;
1298 }
1299 case Tegra::Shader::TextureType::Texture3D: 1300 case Tegra::Shader::TextureType::Texture3D:
1300 case Tegra::Shader::TextureType::TextureCube: { 1301 case Tegra::Shader::TextureType::TextureCube:
1301 return 3; 1302 return 3;
1302 }
1303 default: 1303 default:
1304 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type)); 1304 UNIMPLEMENTED_MSG("Unhandled texture type: {}", static_cast<u32>(texture_type));
1305 UNREACHABLE();
1306 return 0; 1305 return 0;
1307 } 1306 }
1308 } 1307 }
@@ -1338,7 +1337,7 @@ private:
1338 void EmitFragmentOutputsWrite() { 1337 void EmitFragmentOutputsWrite() {
1339 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); 1338 ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment);
1340 1339
1341 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1340 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Samplemask write is unimplemented");
1342 1341
1343 shader.AddLine("if (alpha_test[0] != 0) {"); 1342 shader.AddLine("if (alpha_test[0] != 0) {");
1344 ++shader.scope; 1343 ++shader.scope;
@@ -1404,7 +1403,7 @@ private:
1404 case Tegra::Shader::VideoType::Size32: 1403 case Tegra::Shader::VideoType::Size32:
1405 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when 1404 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1406 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better 1405 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1407 // explanation is found: assert. 1406 // explanation is found: abort.
1408 UNIMPLEMENTED(); 1407 UNIMPLEMENTED();
1409 return zero; 1408 return zero;
1410 case Tegra::Shader::VideoType::Invalid: 1409 case Tegra::Shader::VideoType::Invalid:
@@ -1460,8 +1459,7 @@ private:
1460 1459
1461 // Decoding failure 1460 // Decoding failure
1462 if (!opcode) { 1461 if (!opcode) {
1463 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); 1462 UNIMPLEMENTED_MSG("Unhandled instruction: {0:x}", instr.value);
1464 UNREACHABLE();
1465 return offset + 1; 1463 return offset + 1;
1466 } 1464 }
1467 1465
@@ -1469,8 +1467,8 @@ private:
1469 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); 1467 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value));
1470 1468
1471 using Tegra::Shader::Pred; 1469 using Tegra::Shader::Pred;
1472 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1470 UNIMPLEMENTED_IF_MSG(instr.pred.full_pred == Pred::NeverExecute,
1473 "NeverExecute predicate not implemented"); 1471 "NeverExecute predicate not implemented");
1474 1472
1475 // Some instructions (like SSY) don't have a predicate field, they are always 1473 // Some instructions (like SSY) don't have a predicate field, they are always
1476 // unconditionally executed. 1474 // unconditionally executed.
@@ -1513,37 +1511,36 @@ private:
1513 case OpCode::Id::FMUL_R: 1511 case OpCode::Id::FMUL_R:
1514 case OpCode::Id::FMUL_IMM: { 1512 case OpCode::Id::FMUL_IMM: {
1515 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit. 1513 // FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
1516 ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented", 1514 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0,
1517 instr.fmul.tab5cb8_2.Value()); 1515 "FMUL tab5cb8_2({}) is not implemented",
1518 ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented", 1516 instr.fmul.tab5cb8_2.Value());
1519 instr.fmul.tab5c68_1.Value()); 1517 UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0,
1520 ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented", 1518 "FMUL tab5cb8_1({}) is not implemented",
1521 instr.fmul.tab5c68_0 1519 instr.fmul.tab5c68_1.Value());
1522 .Value()); // SMO typical sends 1 here which seems to be the default 1520 UNIMPLEMENTED_IF_MSG(
1523 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); 1521 instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented",
1522 instr.fmul.tab5c68_0
1523 .Value()); // SMO typical sends 1 here which seems to be the default
1524 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1525 "Condition codes generation in FMUL is not implemented");
1524 1526
1525 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1527 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1526 1528
1527 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1529 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1528 instr.alu.saturate_d, 0, true); 1530 instr.alu.saturate_d, 0, true);
1529 if (instr.generates_cc) {
1530 LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
1531 UNREACHABLE();
1532 }
1533 break; 1531 break;
1534 } 1532 }
1535 case OpCode::Id::FADD_C: 1533 case OpCode::Id::FADD_C:
1536 case OpCode::Id::FADD_R: 1534 case OpCode::Id::FADD_R:
1537 case OpCode::Id::FADD_IMM: { 1535 case OpCode::Id::FADD_IMM: {
1536 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1537 "Condition codes generation in FADD is not implemented");
1538
1538 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1539 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1539 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1540 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1540 1541
1541 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1542 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1542 instr.alu.saturate_d, 0, true); 1543 instr.alu.saturate_d, 0, true);
1543 if (instr.generates_cc) {
1544 LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
1545 UNREACHABLE();
1546 }
1547 break; 1544 break;
1548 } 1545 }
1549 case OpCode::Id::MUFU: { 1546 case OpCode::Id::MUFU: {
@@ -1578,15 +1575,17 @@ private:
1578 instr.alu.saturate_d, 0, true); 1575 instr.alu.saturate_d, 0, true);
1579 break; 1576 break;
1580 default: 1577 default:
1581 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 1578 UNIMPLEMENTED_MSG("Unhandled MUFU sub op={0:x}",
1582 static_cast<unsigned>(instr.sub_op.Value())); 1579 static_cast<unsigned>(instr.sub_op.Value()));
1583 UNREACHABLE();
1584 } 1580 }
1585 break; 1581 break;
1586 } 1582 }
1587 case OpCode::Id::FMNMX_C: 1583 case OpCode::Id::FMNMX_C:
1588 case OpCode::Id::FMNMX_R: 1584 case OpCode::Id::FMNMX_R:
1589 case OpCode::Id::FMNMX_IMM: { 1585 case OpCode::Id::FMNMX_IMM: {
1586 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1587 "Condition codes generation in FMNMX is not implemented");
1588
1590 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1589 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1591 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1590 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1592 1591
@@ -1597,10 +1596,6 @@ private:
1597 '(' + condition + ") ? min(" + parameters + ") : max(" + 1596 '(' + condition + ") ? min(" + parameters + ") : max(" +
1598 parameters + ')', 1597 parameters + ')',
1599 1, 1, false, 0, true); 1598 1, 1, false, 0, true);
1600 if (instr.generates_cc) {
1601 LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
1602 UNREACHABLE();
1603 }
1604 break; 1599 break;
1605 } 1600 }
1606 case OpCode::Id::RRO_C: 1601 case OpCode::Id::RRO_C:
@@ -1613,9 +1608,7 @@ private:
1613 break; 1608 break;
1614 } 1609 }
1615 default: { 1610 default: {
1616 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", 1611 UNIMPLEMENTED_MSG("Unhandled arithmetic instruction: {}", opcode->get().GetName());
1617 opcode->get().GetName());
1618 UNREACHABLE();
1619 } 1612 }
1620 } 1613 }
1621 break; 1614 break;
@@ -1627,17 +1620,19 @@ private:
1627 break; 1620 break;
1628 } 1621 }
1629 case OpCode::Id::FMUL32_IMM: { 1622 case OpCode::Id::FMUL32_IMM: {
1623 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1624 "Condition codes generation in FMUL32 is not implemented");
1625
1630 regs.SetRegisterToFloat(instr.gpr0, 0, 1626 regs.SetRegisterToFloat(instr.gpr0, 0,
1631 regs.GetRegisterAsFloat(instr.gpr8) + " * " + 1627 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1632 GetImmediate32(instr), 1628 GetImmediate32(instr),
1633 1, 1, instr.fmul32.saturate, 0, true); 1629 1, 1, instr.fmul32.saturate, 0, true);
1634 if (instr.op_32.generates_cc) {
1635 LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
1636 UNREACHABLE();
1637 }
1638 break; 1630 break;
1639 } 1631 }
1640 case OpCode::Id::FADD32I: { 1632 case OpCode::Id::FADD32I: {
1633 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1634 "Condition codes generation in FADD32I is not implemented");
1635
1641 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1636 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1642 std::string op_b = GetImmediate32(instr); 1637 std::string op_b = GetImmediate32(instr);
1643 1638
@@ -1658,23 +1653,22 @@ private:
1658 } 1653 }
1659 1654
1660 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); 1655 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
1661 if (instr.op_32.generates_cc) {
1662 LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
1663 UNREACHABLE();
1664 }
1665 break; 1656 break;
1666 } 1657 }
1667 } 1658 }
1668 break; 1659 break;
1669 } 1660 }
1670 case OpCode::Type::Bfe: { 1661 case OpCode::Type::Bfe: {
1671 ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented"); 1662 UNIMPLEMENTED_IF(instr.bfe.negate_b);
1672 1663
1673 std::string op_a = instr.bfe.negate_a ? "-" : ""; 1664 std::string op_a = instr.bfe.negate_a ? "-" : "";
1674 op_a += regs.GetRegisterAsInteger(instr.gpr8); 1665 op_a += regs.GetRegisterAsInteger(instr.gpr8);
1675 1666
1676 switch (opcode->get().GetId()) { 1667 switch (opcode->get().GetId()) {
1677 case OpCode::Id::BFE_IMM: { 1668 case OpCode::Id::BFE_IMM: {
1669 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1670 "Condition codes generation in BFE is not implemented");
1671
1678 std::string inner_shift = 1672 std::string inner_shift =
1679 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; 1673 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
1680 std::string outer_shift = 1674 std::string outer_shift =
@@ -1682,15 +1676,10 @@ private:
1682 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1676 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1683 1677
1684 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1678 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
1685 if (instr.generates_cc) {
1686 LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
1687 UNREACHABLE();
1688 }
1689 break; 1679 break;
1690 } 1680 }
1691 default: { 1681 default: {
1692 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); 1682 UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName());
1693 UNREACHABLE();
1694 } 1683 }
1695 } 1684 }
1696 1685
@@ -1715,6 +1704,9 @@ private:
1715 case OpCode::Id::SHR_C: 1704 case OpCode::Id::SHR_C:
1716 case OpCode::Id::SHR_R: 1705 case OpCode::Id::SHR_R:
1717 case OpCode::Id::SHR_IMM: { 1706 case OpCode::Id::SHR_IMM: {
1707 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1708 "Condition codes generation in SHR is not implemented");
1709
1718 if (!instr.shift.is_signed) { 1710 if (!instr.shift.is_signed) {
1719 // Logical shift right 1711 // Logical shift right
1720 op_a = "uint(" + op_a + ')'; 1712 op_a = "uint(" + op_a + ')';
@@ -1723,24 +1715,17 @@ private:
1723 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1715 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
1724 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1716 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1725 1, 1); 1717 1, 1);
1726 if (instr.generates_cc) {
1727 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1728 UNREACHABLE();
1729 }
1730 break; 1718 break;
1731 } 1719 }
1732 case OpCode::Id::SHL_C: 1720 case OpCode::Id::SHL_C:
1733 case OpCode::Id::SHL_R: 1721 case OpCode::Id::SHL_R:
1734 case OpCode::Id::SHL_IMM: 1722 case OpCode::Id::SHL_IMM:
1723 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1724 "Condition codes generation in SHL is not implemented");
1735 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1725 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
1736 if (instr.generates_cc) {
1737 LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
1738 UNREACHABLE();
1739 }
1740 break; 1726 break;
1741 default: { 1727 default: {
1742 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); 1728 UNIMPLEMENTED_MSG("Unhandled shift instruction: {}", opcode->get().GetName());
1743 UNREACHABLE();
1744 } 1729 }
1745 } 1730 }
1746 break; 1731 break;
@@ -1751,17 +1736,19 @@ private:
1751 1736
1752 switch (opcode->get().GetId()) { 1737 switch (opcode->get().GetId()) {
1753 case OpCode::Id::IADD32I: 1738 case OpCode::Id::IADD32I:
1739 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1740 "Condition codes generation in IADD32I is not implemented");
1741
1754 if (instr.iadd32i.negate_a) 1742 if (instr.iadd32i.negate_a)
1755 op_a = "-(" + op_a + ')'; 1743 op_a = "-(" + op_a + ')';
1756 1744
1757 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1745 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1758 instr.iadd32i.saturate != 0); 1746 instr.iadd32i.saturate != 0);
1759 if (instr.op_32.generates_cc) {
1760 LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
1761 UNREACHABLE();
1762 }
1763 break; 1747 break;
1764 case OpCode::Id::LOP32I: { 1748 case OpCode::Id::LOP32I: {
1749 UNIMPLEMENTED_IF_MSG(instr.op_32.generates_cc,
1750 "Condition codes generation in LOP32I is not implemented");
1751
1765 if (instr.alu.lop32i.invert_a) 1752 if (instr.alu.lop32i.invert_a)
1766 op_a = "~(" + op_a + ')'; 1753 op_a = "~(" + op_a + ')';
1767 1754
@@ -1771,16 +1758,11 @@ private:
1771 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1758 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1772 Tegra::Shader::PredicateResultMode::None, 1759 Tegra::Shader::PredicateResultMode::None,
1773 Tegra::Shader::Pred::UnusedIndex); 1760 Tegra::Shader::Pred::UnusedIndex);
1774 if (instr.op_32.generates_cc) {
1775 LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
1776 UNREACHABLE();
1777 }
1778 break; 1761 break;
1779 } 1762 }
1780 default: { 1763 default: {
1781 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", 1764 UNIMPLEMENTED_MSG("Unhandled ArithmeticIntegerImmediate instruction: {}",
1782 opcode->get().GetName()); 1765 opcode->get().GetName());
1783 UNREACHABLE();
1784 } 1766 }
1785 } 1767 }
1786 break; 1768 break;
@@ -1803,6 +1785,9 @@ private:
1803 case OpCode::Id::IADD_C: 1785 case OpCode::Id::IADD_C:
1804 case OpCode::Id::IADD_R: 1786 case OpCode::Id::IADD_R:
1805 case OpCode::Id::IADD_IMM: { 1787 case OpCode::Id::IADD_IMM: {
1788 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1789 "Condition codes generation in IADD is not implemented");
1790
1806 if (instr.alu_integer.negate_a) 1791 if (instr.alu_integer.negate_a)
1807 op_a = "-(" + op_a + ')'; 1792 op_a = "-(" + op_a + ')';
1808 1793
@@ -1811,15 +1796,14 @@ private:
1811 1796
1812 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1797 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1813 instr.alu.saturate_d); 1798 instr.alu.saturate_d);
1814 if (instr.generates_cc) {
1815 LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
1816 UNREACHABLE();
1817 }
1818 break; 1799 break;
1819 } 1800 }
1820 case OpCode::Id::IADD3_C: 1801 case OpCode::Id::IADD3_C:
1821 case OpCode::Id::IADD3_R: 1802 case OpCode::Id::IADD3_R:
1822 case OpCode::Id::IADD3_IMM: { 1803 case OpCode::Id::IADD3_IMM: {
1804 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1805 "Condition codes generation in IADD3 is not implemented");
1806
1823 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1807 std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1824 1808
1825 auto apply_height = [](auto height, auto& oprand) { 1809 auto apply_height = [](auto height, auto& oprand) {
@@ -1833,9 +1817,8 @@ private:
1833 oprand = "((" + oprand + ") >> 16)"; 1817 oprand = "((" + oprand + ") >> 16)";
1834 break; 1818 break;
1835 default: 1819 default:
1836 LOG_CRITICAL(HW_GPU, "Unhandled IADD3 height: {}", 1820 UNIMPLEMENTED_MSG("Unhandled IADD3 height: {}",
1837 static_cast<u32>(height.Value())); 1821 static_cast<u32>(height.Value()));
1838 UNREACHABLE();
1839 } 1822 }
1840 }; 1823 };
1841 1824
@@ -1876,16 +1859,14 @@ private:
1876 } 1859 }
1877 1860
1878 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1861 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
1879
1880 if (instr.generates_cc) {
1881 LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
1882 UNREACHABLE();
1883 }
1884 break; 1862 break;
1885 } 1863 }
1886 case OpCode::Id::ISCADD_C: 1864 case OpCode::Id::ISCADD_C:
1887 case OpCode::Id::ISCADD_R: 1865 case OpCode::Id::ISCADD_R:
1888 case OpCode::Id::ISCADD_IMM: { 1866 case OpCode::Id::ISCADD_IMM: {
1867 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1868 "Condition codes generation in ISCADD is not implemented");
1869
1889 if (instr.alu_integer.negate_a) 1870 if (instr.alu_integer.negate_a)
1890 op_a = "-(" + op_a + ')'; 1871 op_a = "-(" + op_a + ')';
1891 1872
@@ -1896,10 +1877,6 @@ private:
1896 1877
1897 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1878 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1898 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1879 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1899 if (instr.generates_cc) {
1900 LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
1901 UNREACHABLE();
1902 }
1903 break; 1880 break;
1904 } 1881 }
1905 case OpCode::Id::POPC_C: 1882 case OpCode::Id::POPC_C:
@@ -1923,6 +1900,9 @@ private:
1923 case OpCode::Id::LOP_C: 1900 case OpCode::Id::LOP_C:
1924 case OpCode::Id::LOP_R: 1901 case OpCode::Id::LOP_R:
1925 case OpCode::Id::LOP_IMM: { 1902 case OpCode::Id::LOP_IMM: {
1903 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1904 "Condition codes generation in LOP is not implemented");
1905
1926 if (instr.alu.lop.invert_a) 1906 if (instr.alu.lop.invert_a)
1927 op_a = "~(" + op_a + ')'; 1907 op_a = "~(" + op_a + ')';
1928 1908
@@ -1931,15 +1911,14 @@ private:
1931 1911
1932 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1912 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1933 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1913 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1934 if (instr.generates_cc) {
1935 LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
1936 UNREACHABLE();
1937 }
1938 break; 1914 break;
1939 } 1915 }
1940 case OpCode::Id::LOP3_C: 1916 case OpCode::Id::LOP3_C:
1941 case OpCode::Id::LOP3_R: 1917 case OpCode::Id::LOP3_R:
1942 case OpCode::Id::LOP3_IMM: { 1918 case OpCode::Id::LOP3_IMM: {
1919 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1920 "Condition codes generation in LOP3 is not implemented");
1921
1943 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1922 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1944 std::string lut; 1923 std::string lut;
1945 1924
@@ -1950,17 +1929,15 @@ private:
1950 } 1929 }
1951 1930
1952 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1931 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
1953 if (instr.generates_cc) {
1954 LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
1955 UNREACHABLE();
1956 }
1957 break; 1932 break;
1958 } 1933 }
1959 case OpCode::Id::IMNMX_C: 1934 case OpCode::Id::IMNMX_C:
1960 case OpCode::Id::IMNMX_R: 1935 case OpCode::Id::IMNMX_R:
1961 case OpCode::Id::IMNMX_IMM: { 1936 case OpCode::Id::IMNMX_IMM: {
1962 ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, 1937 UNIMPLEMENTED_IF(instr.imnmx.exchange != Tegra::Shader::IMinMaxExchange::None);
1963 "Unimplemented"); 1938 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
1939 "Condition codes generation in IMNMX is not implemented");
1940
1964 const std::string condition = 1941 const std::string condition =
1965 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); 1942 GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0);
1966 const std::string parameters = op_a + ',' + op_b; 1943 const std::string parameters = op_a + ',' + op_b;
@@ -1968,10 +1945,6 @@ private:
1968 '(' + condition + ") ? min(" + parameters + ") : max(" + 1945 '(' + condition + ") ? min(" + parameters + ") : max(" +
1969 parameters + ')', 1946 parameters + ')',
1970 1, 1); 1947 1, 1);
1971 if (instr.generates_cc) {
1972 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1973 UNREACHABLE();
1974 }
1975 break; 1948 break;
1976 } 1949 }
1977 case OpCode::Id::LEA_R2: 1950 case OpCode::Id::LEA_R2:
@@ -2026,24 +1999,19 @@ private:
2026 op_b = regs.GetRegisterAsInteger(instr.gpr8); 1999 op_b = regs.GetRegisterAsInteger(instr.gpr8);
2027 op_a = std::to_string(instr.lea.imm.entry_a); 2000 op_a = std::to_string(instr.lea.imm.entry_a);
2028 op_c = std::to_string(instr.lea.imm.entry_b); 2001 op_c = std::to_string(instr.lea.imm.entry_b);
2029 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", 2002 UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName());
2030 opcode->get().GetName());
2031 UNREACHABLE();
2032 }
2033 } 2003 }
2034 if (instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex)) {
2035 LOG_ERROR(HW_GPU, "Unhandled LEA Predicate");
2036 UNREACHABLE();
2037 } 2004 }
2005 UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
2006 "Unhandled LEA Predicate");
2038 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))"; 2007 const std::string value = '(' + op_a + " + (" + op_b + "*(1 << " + op_c + ")))";
2039 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1); 2008 regs.SetRegisterToInteger(instr.gpr0, true, 0, value, 1, 1);
2040 2009
2041 break; 2010 break;
2042 } 2011 }
2043 default: { 2012 default: {
2044 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 2013 UNIMPLEMENTED_MSG("Unhandled ArithmeticInteger instruction: {}",
2045 opcode->get().GetName()); 2014 opcode->get().GetName());
2046 UNREACHABLE();
2047 } 2015 }
2048 } 2016 }
2049 2017
@@ -2052,7 +2020,7 @@ private:
2052 case OpCode::Type::ArithmeticHalf: { 2020 case OpCode::Type::ArithmeticHalf: {
2053 if (opcode->get().GetId() == OpCode::Id::HADD2_C || 2021 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
2054 opcode->get().GetId() == OpCode::Id::HADD2_R) { 2022 opcode->get().GetId() == OpCode::Id::HADD2_R) {
2055 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); 2023 UNIMPLEMENTED_IF(instr.alu_half.ftz != 0);
2056 } 2024 }
2057 const bool negate_a = 2025 const bool negate_a =
2058 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; 2026 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
@@ -2090,9 +2058,8 @@ private:
2090 case OpCode::Id::HMUL2_R: 2058 case OpCode::Id::HMUL2_R:
2091 return '(' + op_a + " * " + op_b + ')'; 2059 return '(' + op_a + " * " + op_b + ')';
2092 default: 2060 default:
2093 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", 2061 UNIMPLEMENTED_MSG("Unhandled half float instruction: {}",
2094 opcode->get().GetName()); 2062 opcode->get().GetName());
2095 UNREACHABLE();
2096 return std::string("0"); 2063 return std::string("0");
2097 } 2064 }
2098 }(); 2065 }();
@@ -2103,10 +2070,10 @@ private:
2103 } 2070 }
2104 case OpCode::Type::ArithmeticHalfImmediate: { 2071 case OpCode::Type::ArithmeticHalfImmediate: {
2105 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { 2072 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
2106 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); 2073 UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0);
2107 } else { 2074 } else {
2108 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, 2075 UNIMPLEMENTED_IF(instr.alu_half_imm.precision !=
2109 "Unimplemented"); 2076 Tegra::Shader::HalfPrecision::None);
2110 } 2077 }
2111 2078
2112 const std::string op_a = GetHalfFloat( 2079 const std::string op_a = GetHalfFloat(
@@ -2136,11 +2103,14 @@ private:
2136 std::string op_b = instr.ffma.negate_b ? "-" : ""; 2103 std::string op_b = instr.ffma.negate_b ? "-" : "";
2137 std::string op_c = instr.ffma.negate_c ? "-" : ""; 2104 std::string op_c = instr.ffma.negate_c ? "-" : "";
2138 2105
2139 ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented"); 2106 UNIMPLEMENTED_IF_MSG(instr.ffma.cc != 0, "FFMA cc not implemented");
2140 ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented", 2107 UNIMPLEMENTED_IF_MSG(
2141 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO 2108 instr.ffma.tab5980_0 != 1, "FFMA tab5980_0({}) not implemented",
2142 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", 2109 instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO
2143 instr.ffma.tab5980_1.Value()); 2110 UNIMPLEMENTED_IF_MSG(instr.ffma.tab5980_1 != 0, "FFMA tab5980_1({}) not implemented",
2111 instr.ffma.tab5980_1.Value());
2112 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2113 "Condition codes generation in FFMA is not implemented");
2144 2114
2145 switch (opcode->get().GetId()) { 2115 switch (opcode->get().GetId()) {
2146 case OpCode::Id::FFMA_CR: { 2116 case OpCode::Id::FFMA_CR: {
@@ -2166,27 +2136,19 @@ private:
2166 break; 2136 break;
2167 } 2137 }
2168 default: { 2138 default: {
2169 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); 2139 UNIMPLEMENTED_MSG("Unhandled FFMA instruction: {}", opcode->get().GetName());
2170 UNREACHABLE();
2171 } 2140 }
2172 } 2141 }
2173 2142
2174 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', 2143 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
2175 1, 1, instr.alu.saturate_d, 0, true); 2144 1, 1, instr.alu.saturate_d, 0, true);
2176 if (instr.generates_cc) {
2177 LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
2178 UNREACHABLE();
2179 }
2180
2181 break; 2145 break;
2182 } 2146 }
2183 case OpCode::Type::Hfma2: { 2147 case OpCode::Type::Hfma2: {
2184 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { 2148 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) {
2185 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, 2149 UNIMPLEMENTED_IF(instr.hfma2.rr.precision != Tegra::Shader::HalfPrecision::None);
2186 "Unimplemented");
2187 } else { 2150 } else {
2188 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, 2151 UNIMPLEMENTED_IF(instr.hfma2.precision != Tegra::Shader::HalfPrecision::None);
2189 "Unimplemented");
2190 } 2152 }
2191 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR 2153 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR
2192 ? instr.hfma2.rr.saturate != 0 2154 ? instr.hfma2.rr.saturate != 0
@@ -2236,7 +2198,7 @@ private:
2236 case OpCode::Type::Conversion: { 2198 case OpCode::Type::Conversion: {
2237 switch (opcode->get().GetId()) { 2199 switch (opcode->get().GetId()) {
2238 case OpCode::Id::I2I_R: { 2200 case OpCode::Id::I2I_R: {
2239 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2201 UNIMPLEMENTED_IF(instr.conversion.selector);
2240 2202
2241 std::string op_a = regs.GetRegisterAsInteger( 2203 std::string op_a = regs.GetRegisterAsInteger(
2242 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size); 2204 instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
@@ -2256,8 +2218,10 @@ private:
2256 } 2218 }
2257 case OpCode::Id::I2F_R: 2219 case OpCode::Id::I2F_R:
2258 case OpCode::Id::I2F_C: { 2220 case OpCode::Id::I2F_C: {
2259 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2221 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2260 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2222 UNIMPLEMENTED_IF(instr.conversion.selector);
2223 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2224 "Condition codes generation in I2F is not implemented");
2261 2225
2262 std::string op_a{}; 2226 std::string op_a{};
2263 2227
@@ -2282,16 +2246,13 @@ private:
2282 } 2246 }
2283 2247
2284 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2248 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2285
2286 if (instr.generates_cc) {
2287 LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
2288 UNREACHABLE();
2289 }
2290 break; 2249 break;
2291 } 2250 }
2292 case OpCode::Id::F2F_R: { 2251 case OpCode::Id::F2F_R: {
2293 ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented"); 2252 UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word);
2294 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2253 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2254 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2255 "Condition codes generation in F2F is not implemented");
2295 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20); 2256 std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
2296 2257
2297 if (instr.conversion.abs_a) { 2258 if (instr.conversion.abs_a) {
@@ -2318,23 +2279,19 @@ private:
2318 op_a = "trunc(" + op_a + ')'; 2279 op_a = "trunc(" + op_a + ')';
2319 break; 2280 break;
2320 default: 2281 default:
2321 LOG_CRITICAL(HW_GPU, "Unimplemented f2f rounding mode {}", 2282 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
2322 static_cast<u32>(instr.conversion.f2f.rounding.Value())); 2283 static_cast<u32>(instr.conversion.f2f.rounding.Value()));
2323 UNREACHABLE();
2324 break; 2284 break;
2325 } 2285 }
2326 2286
2327 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2287 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
2328
2329 if (instr.generates_cc) {
2330 LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
2331 UNREACHABLE();
2332 }
2333 break; 2288 break;
2334 } 2289 }
2335 case OpCode::Id::F2I_R: 2290 case OpCode::Id::F2I_R:
2336 case OpCode::Id::F2I_C: { 2291 case OpCode::Id::F2I_C: {
2337 ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented"); 2292 UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word);
2293 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
2294 "Condition codes generation in F2I is not implemented");
2338 std::string op_a{}; 2295 std::string op_a{};
2339 2296
2340 if (instr.is_b_gpr) { 2297 if (instr.is_b_gpr) {
@@ -2365,9 +2322,8 @@ private:
2365 op_a = "trunc(" + op_a + ')'; 2322 op_a = "trunc(" + op_a + ')';
2366 break; 2323 break;
2367 default: 2324 default:
2368 LOG_CRITICAL(HW_GPU, "Unimplemented f2i rounding mode {}", 2325 UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}",
2369 static_cast<u32>(instr.conversion.f2i.rounding.Value())); 2326 static_cast<u32>(instr.conversion.f2i.rounding.Value()));
2370 UNREACHABLE();
2371 break; 2327 break;
2372 } 2328 }
2373 2329
@@ -2379,16 +2335,10 @@ private:
2379 2335
2380 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2336 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
2381 1, false, 0, instr.conversion.dest_size); 2337 1, false, 0, instr.conversion.dest_size);
2382 if (instr.generates_cc) {
2383 LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
2384 UNREACHABLE();
2385 }
2386 break; 2338 break;
2387 } 2339 }
2388 default: { 2340 default: {
2389 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", 2341 UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName());
2390 opcode->get().GetName());
2391 UNREACHABLE();
2392 } 2342 }
2393 } 2343 }
2394 break; 2344 break;
@@ -2397,10 +2347,10 @@ private:
2397 switch (opcode->get().GetId()) { 2347 switch (opcode->get().GetId()) {
2398 case OpCode::Id::LD_A: { 2348 case OpCode::Id::LD_A: {
2399 // Note: Shouldn't this be interp mode flat? As in no interpolation made. 2349 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
2400 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2350 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2401 "Indirect attribute loads are not supported"); 2351 "Indirect attribute loads are not supported");
2402 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2352 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2403 "Unaligned attribute loads are not supported"); 2353 "Unaligned attribute loads are not supported");
2404 2354
2405 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, 2355 Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective,
2406 Tegra::Shader::IpaSampleMode::Default}; 2356 Tegra::Shader::IpaSampleMode::Default};
@@ -2427,7 +2377,7 @@ private:
2427 break; 2377 break;
2428 } 2378 }
2429 case OpCode::Id::LD_C: { 2379 case OpCode::Id::LD_C: {
2430 ASSERT_MSG(instr.ld_c.unknown == 0, "Unimplemented"); 2380 UNIMPLEMENTED_IF(instr.ld_c.unknown != 0);
2431 2381
2432 // Add an extra scope and declare the index register inside to prevent 2382 // Add an extra scope and declare the index register inside to prevent
2433 // overwriting it in case it is used as an output of the LD instruction. 2383 // overwriting it in case it is used as an output of the LD instruction.
@@ -2455,9 +2405,8 @@ private:
2455 break; 2405 break;
2456 } 2406 }
2457 default: 2407 default:
2458 LOG_CRITICAL(HW_GPU, "Unhandled type: {}", 2408 UNIMPLEMENTED_MSG("Unhandled type: {}",
2459 static_cast<unsigned>(instr.ld_c.type.Value())); 2409 static_cast<unsigned>(instr.ld_c.type.Value()));
2460 UNREACHABLE();
2461 } 2410 }
2462 2411
2463 --shader.scope; 2412 --shader.scope;
@@ -2465,6 +2414,9 @@ private:
2465 break; 2414 break;
2466 } 2415 }
2467 case OpCode::Id::LD_L: { 2416 case OpCode::Id::LD_L: {
2417 UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}",
2418 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2419
2468 // Add an extra scope and declare the index register inside to prevent 2420 // Add an extra scope and declare the index register inside to prevent
2469 // overwriting it in case it is used as an output of the LD instruction. 2421 // overwriting it in case it is used as an output of the LD instruction.
2470 shader.AddLine('{'); 2422 shader.AddLine('{');
@@ -2477,20 +2429,13 @@ private:
2477 2429
2478 const std::string op_a = regs.GetLocalMemoryAsFloat("index"); 2430 const std::string op_a = regs.GetLocalMemoryAsFloat("index");
2479 2431
2480 if (instr.ld_l.unknown != 1) {
2481 LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}",
2482 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2483 UNREACHABLE();
2484 }
2485
2486 switch (instr.ldst_sl.type.Value()) { 2432 switch (instr.ldst_sl.type.Value()) {
2487 case Tegra::Shader::StoreType::Bytes32: 2433 case Tegra::Shader::StoreType::Bytes32:
2488 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2434 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2489 break; 2435 break;
2490 default: 2436 default:
2491 LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", 2437 UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
2492 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2438 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2493 UNREACHABLE();
2494 } 2439 }
2495 2440
2496 --shader.scope; 2441 --shader.scope;
@@ -2498,10 +2443,10 @@ private:
2498 break; 2443 break;
2499 } 2444 }
2500 case OpCode::Id::ST_A: { 2445 case OpCode::Id::ST_A: {
2501 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2446 UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex,
2502 "Indirect attribute loads are not supported"); 2447 "Indirect attribute loads are not supported");
2503 ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, 2448 UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
2504 "Unaligned attribute loads are not supported"); 2449 "Unaligned attribute loads are not supported");
2505 2450
2506 u64 next_element = instr.attribute.fmt20.element; 2451 u64 next_element = instr.attribute.fmt20.element;
2507 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); 2452 u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
@@ -2526,6 +2471,9 @@ private:
2526 break; 2471 break;
2527 } 2472 }
2528 case OpCode::Id::ST_L: { 2473 case OpCode::Id::ST_L: {
2474 UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}",
2475 static_cast<unsigned>(instr.st_l.unknown.Value()));
2476
2529 // Add an extra scope and declare the index register inside to prevent 2477 // Add an extra scope and declare the index register inside to prevent
2530 // overwriting it in case it is used as an output of the LD instruction. 2478 // overwriting it in case it is used as an output of the LD instruction.
2531 shader.AddLine('{'); 2479 shader.AddLine('{');
@@ -2536,20 +2484,13 @@ private:
2536 2484
2537 shader.AddLine("uint index = (" + op + " / 4);"); 2485 shader.AddLine("uint index = (" + op + " / 4);");
2538 2486
2539 if (instr.st_l.unknown != 0) {
2540 LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}",
2541 static_cast<unsigned>(instr.st_l.unknown.Value()));
2542 UNREACHABLE();
2543 }
2544
2545 switch (instr.ldst_sl.type.Value()) { 2487 switch (instr.ldst_sl.type.Value()) {
2546 case Tegra::Shader::StoreType::Bytes32: 2488 case Tegra::Shader::StoreType::Bytes32:
2547 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); 2489 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0));
2548 break; 2490 break;
2549 default: 2491 default:
2550 LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", 2492 UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
2551 static_cast<unsigned>(instr.ldst_sl.type.Value())); 2493 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2552 UNREACHABLE();
2553 } 2494 }
2554 2495
2555 --shader.scope; 2496 --shader.scope;
@@ -2561,10 +2502,10 @@ private:
2561 std::string coord; 2502 std::string coord;
2562 const bool is_array = instr.tex.array != 0; 2503 const bool is_array = instr.tex.array != 0;
2563 2504
2564 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2505 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2565 "NODEP is not implemented"); 2506 "NODEP is not implemented");
2566 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2507 UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2567 "AOFFI is not implemented"); 2508 "AOFFI is not implemented");
2568 2509
2569 const bool depth_compare = 2510 const bool depth_compare =
2570 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2511 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2630,9 +2571,8 @@ private:
2630 break; 2571 break;
2631 } 2572 }
2632 default: 2573 default:
2633 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2574 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2634 static_cast<u32>(num_coordinates)); 2575 static_cast<u32>(num_coordinates));
2635 UNREACHABLE();
2636 2576
2637 // Fallback to interpreting as a 2D texture for now 2577 // Fallback to interpreting as a 2D texture for now
2638 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2578 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
@@ -2642,7 +2582,6 @@ private:
2642 } 2582 }
2643 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias 2583 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
2644 // or lod. 2584 // or lod.
2645 std::string op_c;
2646 2585
2647 const std::string sampler = 2586 const std::string sampler =
2648 GetSampler(instr.sampler, texture_type, is_array, depth_compare); 2587 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
@@ -2665,34 +2604,41 @@ private:
2665 } 2604 }
2666 case Tegra::Shader::TextureProcessMode::LB: 2605 case Tegra::Shader::TextureProcessMode::LB:
2667 case Tegra::Shader::TextureProcessMode::LBA: { 2606 case Tegra::Shader::TextureProcessMode::LBA: {
2668 if (depth_compare) { 2607 const std::string bias = [&]() {
2669 if (is_array) 2608 if (depth_compare) {
2670 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); 2609 if (is_array)
2671 else 2610 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2672 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2611 else
2673 } else { 2612 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2674 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2613 } else {
2675 } 2614 return regs.GetRegisterAsFloat(instr.gpr20);
2615 }
2616 }();
2617 shader.AddLine("float bias = " + bias + ';');
2618
2676 // TODO: Figure if A suffix changes the equation at all. 2619 // TODO: Figure if A suffix changes the equation at all.
2677 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2620 texture = "texture(" + sampler + ", coords, bias)";
2678 break; 2621 break;
2679 } 2622 }
2680 case Tegra::Shader::TextureProcessMode::LL: 2623 case Tegra::Shader::TextureProcessMode::LL:
2681 case Tegra::Shader::TextureProcessMode::LLA: { 2624 case Tegra::Shader::TextureProcessMode::LLA: {
2682 if (num_coordinates <= 2) { 2625 const std::string lod = [&]() {
2683 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2626 if (num_coordinates <= 2) {
2684 } else { 2627 return regs.GetRegisterAsFloat(instr.gpr20);
2685 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2628 } else {
2686 } 2629 return regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2630 }
2631 }();
2632 shader.AddLine("float lod = " + lod + ';');
2633
2687 // TODO: Figure if A suffix changes the equation at all. 2634 // TODO: Figure if A suffix changes the equation at all.
2688 texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; 2635 texture = "textureLod(" + sampler + ", coords, lod)";
2689 break; 2636 break;
2690 } 2637 }
2691 default: { 2638 default: {
2692 texture = "texture(" + sampler + ", coords)"; 2639 texture = "texture(" + sampler + ", coords)";
2693 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2640 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2694 static_cast<u32>(instr.tex.GetTextureProcessMode())); 2641 static_cast<u32>(instr.tex.GetTextureProcessMode()));
2695 UNREACHABLE();
2696 } 2642 }
2697 } 2643 }
2698 if (!depth_compare) { 2644 if (!depth_compare) {
@@ -2713,12 +2659,11 @@ private:
2713 break; 2659 break;
2714 } 2660 }
2715 case OpCode::Id::TEXS: { 2661 case OpCode::Id::TEXS: {
2716 std::string coord;
2717 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; 2662 Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
2718 bool is_array{instr.texs.IsArrayTexture()}; 2663 bool is_array{instr.texs.IsArrayTexture()};
2719 2664
2720 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2665 UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2721 "NODEP is not implemented"); 2666 "NODEP is not implemented");
2722 2667
2723 const bool depth_compare = 2668 const bool depth_compare =
2724 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2669 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2726,45 +2671,48 @@ private:
2726 if (depth_compare) 2671 if (depth_compare)
2727 num_coordinates += 1; 2672 num_coordinates += 1;
2728 2673
2674 // Scope to avoid variable name overlaps.
2675 shader.AddLine('{');
2676 ++shader.scope;
2677
2729 switch (num_coordinates) { 2678 switch (num_coordinates) {
2730 case 2: { 2679 case 2: {
2731 if (is_array) { 2680 if (is_array) {
2732 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2681 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2733 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2682 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2734 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2683 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2735 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; 2684 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + index + ");");
2736 } else { 2685 } else {
2737 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2686 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2738 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2687 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2739 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2688 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2740 } 2689 }
2741 break; 2690 break;
2742 } 2691 }
2743 case 3: { 2692 case 3: {
2744 if (is_array) { 2693 if (is_array) {
2745 UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented"); 2694 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2746 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2695 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2747 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2696 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2748 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2697 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2749 texture_type = Tegra::Shader::TextureType::Texture2D; 2698 shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
2750 is_array = false; 2699 index + ");");
2751 } else { 2700 } else {
2752 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2701 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2753 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2702 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2754 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2703 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2755 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2704 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2756 } 2705 }
2757 break; 2706 break;
2758 } 2707 }
2759 default: 2708 default:
2760 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2709 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2761 static_cast<u32>(num_coordinates)); 2710 static_cast<u32>(num_coordinates));
2762 UNREACHABLE();
2763 2711
2764 // Fallback to interpreting as a 2D texture for now 2712 // Fallback to interpreting as a 2D texture for now
2765 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2713 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2766 const std::string y = regs.GetRegisterAsFloat(instr.gpr20); 2714 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2767 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2715 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2768 texture_type = Tegra::Shader::TextureType::Texture2D; 2716 texture_type = Tegra::Shader::TextureType::Texture2D;
2769 is_array = false; 2717 is_array = false;
2770 } 2718 }
@@ -2777,7 +2725,11 @@ private:
2777 break; 2725 break;
2778 } 2726 }
2779 case Tegra::Shader::TextureProcessMode::LZ: { 2727 case Tegra::Shader::TextureProcessMode::LZ: {
2780 texture = "textureLod(" + sampler + ", coords, 0.0)"; 2728 if (depth_compare && is_array) {
2729 texture = "texture(" + sampler + ", coords)";
2730 } else {
2731 texture = "textureLod(" + sampler + ", coords, 0.0)";
2732 }
2781 break; 2733 break;
2782 } 2734 }
2783 case Tegra::Shader::TextureProcessMode::LL: { 2735 case Tegra::Shader::TextureProcessMode::LL: {
@@ -2787,57 +2739,57 @@ private:
2787 } 2739 }
2788 default: { 2740 default: {
2789 texture = "texture(" + sampler + ", coords)"; 2741 texture = "texture(" + sampler + ", coords)";
2790 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2742 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2791 static_cast<u32>(instr.texs.GetTextureProcessMode())); 2743 static_cast<u32>(instr.texs.GetTextureProcessMode()));
2792 UNREACHABLE();
2793 } 2744 }
2794 } 2745 }
2795 if (!depth_compare) { 2746 if (!depth_compare) {
2796 WriteTexsInstruction(instr, coord, texture); 2747 WriteTexsInstruction(instr, texture);
2797 } else { 2748 } else {
2798 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 2749 WriteTexsInstruction(instr, "vec4(" + texture + ')');
2799 } 2750 }
2751
2752 shader.AddLine('}');
2753 --shader.scope;
2800 break; 2754 break;
2801 } 2755 }
2802 case OpCode::Id::TLDS: { 2756 case OpCode::Id::TLDS: {
2803 std::string coord;
2804 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; 2757 const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()};
2805 const bool is_array{instr.tlds.IsArrayTexture()}; 2758 const bool is_array{instr.tlds.IsArrayTexture()};
2806 2759
2807 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); 2760 ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D);
2808 ASSERT(is_array == false); 2761 ASSERT(is_array == false);
2809 2762
2810 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2763 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2811 "NODEP is not implemented"); 2764 "NODEP is not implemented");
2812 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2765 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2813 "AOFFI is not implemented"); 2766 "AOFFI is not implemented");
2814 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2767 UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2815 "MZ is not implemented"); 2768 "MZ is not implemented");
2816 2769
2817 u32 op_c_offset = 0; 2770 u32 extra_op_offset = 0;
2771
2772 // Scope to avoid variable name overlaps.
2773 shader.AddLine('{');
2774 ++shader.scope;
2818 2775
2819 switch (texture_type) { 2776 switch (texture_type) {
2820 case Tegra::Shader::TextureType::Texture1D: { 2777 case Tegra::Shader::TextureType::Texture1D: {
2821 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2778 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2822 coord = "int coords = " + x + ';'; 2779 shader.AddLine("int coords = " + x + ';');
2823 break; 2780 break;
2824 } 2781 }
2825 case Tegra::Shader::TextureType::Texture2D: { 2782 case Tegra::Shader::TextureType::Texture2D: {
2826 if (is_array) { 2783 UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture");
2827 LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture"); 2784
2828 UNREACHABLE(); 2785 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2829 } else { 2786 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2830 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2787 shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
2831 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2788 extra_op_offset = 1;
2832 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2833 op_c_offset = 1;
2834 }
2835 break; 2789 break;
2836 } 2790 }
2837 default: 2791 default:
2838 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2792 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
2839 static_cast<u32>(texture_type));
2840 UNREACHABLE();
2841 } 2793 }
2842 const std::string sampler = 2794 const std::string sampler =
2843 GetSampler(instr.sampler, texture_type, is_array, false); 2795 GetSampler(instr.sampler, texture_type, is_array, false);
@@ -2848,19 +2800,22 @@ private:
2848 break; 2800 break;
2849 } 2801 }
2850 case Tegra::Shader::TextureProcessMode::LL: { 2802 case Tegra::Shader::TextureProcessMode::LL: {
2851 const std::string op_c = 2803 shader.AddLine(
2852 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset); 2804 "float lod = " +
2853 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2805 regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
2806 texture = "texelFetch(" + sampler + ", coords, lod)";
2854 break; 2807 break;
2855 } 2808 }
2856 default: { 2809 default: {
2857 texture = "texelFetch(" + sampler + ", coords, 0)"; 2810 texture = "texelFetch(" + sampler + ", coords, 0)";
2858 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2811 UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
2859 static_cast<u32>(instr.tlds.GetTextureProcessMode())); 2812 static_cast<u32>(instr.tlds.GetTextureProcessMode()));
2860 UNREACHABLE();
2861 } 2813 }
2862 } 2814 }
2863 WriteTexsInstruction(instr, coord, texture); 2815 WriteTexsInstruction(instr, texture);
2816
2817 --shader.scope;
2818 shader.AddLine('}');
2864 break; 2819 break;
2865 } 2820 }
2866 case OpCode::Id::TLD4: { 2821 case OpCode::Id::TLD4: {
@@ -2868,14 +2823,14 @@ private:
2868 ASSERT(instr.tld4.array == 0); 2823 ASSERT(instr.tld4.array == 0);
2869 std::string coord; 2824 std::string coord;
2870 2825
2871 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2826 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2872 "NODEP is not implemented"); 2827 "NODEP is not implemented");
2873 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2828 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2874 "AOFFI is not implemented"); 2829 "AOFFI is not implemented");
2875 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2830 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2876 "NDV is not implemented"); 2831 "NDV is not implemented");
2877 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), 2832 UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2878 "PTP is not implemented"); 2833 "PTP is not implemented");
2879 const bool depth_compare = 2834 const bool depth_compare =
2880 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2835 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2881 auto texture_type = instr.tld4.texture_type.Value(); 2836 auto texture_type = instr.tld4.texture_type.Value();
@@ -2883,37 +2838,37 @@ private:
2883 if (depth_compare) 2838 if (depth_compare)
2884 num_coordinates += 1; 2839 num_coordinates += 1;
2885 2840
2841 // Add an extra scope and declare the texture coords inside to prevent
2842 // overwriting them in case they are used as outputs of the texs instruction.
2843 shader.AddLine('{');
2844 ++shader.scope;
2845
2886 switch (num_coordinates) { 2846 switch (num_coordinates) {
2887 case 2: { 2847 case 2: {
2888 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2848 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2889 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2849 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2890 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2850 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2891 break; 2851 break;
2892 } 2852 }
2893 case 3: { 2853 case 3: {
2894 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2854 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2895 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2855 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2896 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); 2856 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2897 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2857 shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");");
2898 break; 2858 break;
2899 } 2859 }
2900 default: 2860 default:
2901 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}", 2861 UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
2902 static_cast<u32>(num_coordinates)); 2862 static_cast<u32>(num_coordinates));
2903 UNREACHABLE();
2904 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2863 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2905 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2864 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2906 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2865 shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
2907 texture_type = Tegra::Shader::TextureType::Texture2D; 2866 texture_type = Tegra::Shader::TextureType::Texture2D;
2908 } 2867 }
2909 2868
2910 const std::string sampler = 2869 const std::string sampler =
2911 GetSampler(instr.sampler, texture_type, false, depth_compare); 2870 GetSampler(instr.sampler, texture_type, false, depth_compare);
2912 // Add an extra scope and declare the texture coords inside to prevent 2871
2913 // overwriting them in case they are used as outputs of the texs instruction.
2914 shader.AddLine("{");
2915 ++shader.scope;
2916 shader.AddLine(coord);
2917 const std::string texture = "textureGather(" + sampler + ", coords, " + 2872 const std::string texture = "textureGather(" + sampler + ", coords, " +
2918 std::to_string(instr.tld4.component) + ')'; 2873 std::to_string(instr.tld4.component) + ')';
2919 if (!depth_compare) { 2874 if (!depth_compare) {
@@ -2930,14 +2885,20 @@ private:
2930 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); 2885 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
2931 } 2886 }
2932 --shader.scope; 2887 --shader.scope;
2933 shader.AddLine("}"); 2888 shader.AddLine('}');
2934 break; 2889 break;
2935 } 2890 }
2936 case OpCode::Id::TLD4S: { 2891 case OpCode::Id::TLD4S: {
2937 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2892 UNIMPLEMENTED_IF_MSG(
2938 "NODEP is not implemented"); 2893 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2939 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2894 "NODEP is not implemented");
2940 "AOFFI is not implemented"); 2895 UNIMPLEMENTED_IF_MSG(
2896 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2897 "AOFFI is not implemented");
2898
2899 // Scope to avoid variable name overlaps.
2900 shader.AddLine('{');
2901 ++shader.scope;
2941 2902
2942 const bool depth_compare = 2903 const bool depth_compare =
2943 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); 2904 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
@@ -2946,28 +2907,33 @@ private:
2946 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 2907 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
2947 const std::string sampler = GetSampler( 2908 const std::string sampler = GetSampler(
2948 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); 2909 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
2949 std::string coord;
2950 if (!depth_compare) { 2910 if (!depth_compare) {
2951 coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 2911 shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");");
2952 } else { 2912 } else {
2953 // Note: TLD4S coordinate encoding works just like TEXS's 2913 // Note: TLD4S coordinate encoding works just like TEXS's
2954 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2914 shader.AddLine(
2955 coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");"; 2915 "float op_y = " + regs.GetRegisterAsFloat(instr.gpr8.Value() + 1) + ';');
2916 shader.AddLine("vec3 coords = vec3(" + op_a + ", op_y, " + op_b + ");");
2956 } 2917 }
2957 const std::string texture = "textureGather(" + sampler + ", coords, " + 2918 const std::string texture = "textureGather(" + sampler + ", coords, " +
2958 std::to_string(instr.tld4s.component) + ')'; 2919 std::to_string(instr.tld4s.component) + ')';
2959 2920
2960 if (!depth_compare) { 2921 if (!depth_compare) {
2961 WriteTexsInstruction(instr, coord, texture); 2922 WriteTexsInstruction(instr, texture);
2962 } else { 2923 } else {
2963 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')'); 2924 WriteTexsInstruction(instr, "vec4(" + texture + ')');
2964 } 2925 }
2926
2927 --shader.scope;
2928 shader.AddLine('}');
2965 break; 2929 break;
2966 } 2930 }
2967 case OpCode::Id::TXQ: { 2931 case OpCode::Id::TXQ: {
2968 ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2932 UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2969 "NODEP is not implemented"); 2933 "NODEP is not implemented");
2970 2934
2935 ++shader.scope;
2936 shader.AddLine('{');
2971 // TODO: the new commits on the texture refactor, change the way samplers work. 2937 // TODO: the new commits on the texture refactor, change the way samplers work.
2972 // Sadly, not all texture instructions specify the type of texture their sampler 2938 // Sadly, not all texture instructions specify the type of texture their sampler
2973 // uses. This must be fixed at a later instance. 2939 // uses. This must be fixed at a later instance.
@@ -2975,23 +2941,30 @@ private:
2975 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false); 2941 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
2976 switch (instr.txq.query_type) { 2942 switch (instr.txq.query_type) {
2977 case Tegra::Shader::TextureQueryType::Dimension: { 2943 case Tegra::Shader::TextureQueryType::Dimension: {
2978 const std::string texture = "textureQueryLevels(" + sampler + ')'; 2944 const std::string texture = "textureSize(" + sampler + ", " +
2979 regs.SetRegisterToInteger(instr.gpr0, true, 0, texture, 1, 1); 2945 regs.GetRegisterAsInteger(instr.gpr8) + ')';
2946 const std::string mip_level = "textureQueryLevels(" + sampler + ')';
2947 shader.AddLine("ivec2 sizes = " + texture + ';');
2948 regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1);
2949 regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1);
2950 regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1);
2951 regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1);
2980 break; 2952 break;
2981 } 2953 }
2982 default: { 2954 default: {
2983 LOG_CRITICAL(HW_GPU, "Unhandled texture query type: {}", 2955 UNIMPLEMENTED_MSG("Unhandled texture query type: {}",
2984 static_cast<u32>(instr.txq.query_type.Value())); 2956 static_cast<u32>(instr.txq.query_type.Value()));
2985 UNREACHABLE();
2986 } 2957 }
2987 } 2958 }
2959 --shader.scope;
2960 shader.AddLine('}');
2988 break; 2961 break;
2989 } 2962 }
2990 case OpCode::Id::TMML: { 2963 case OpCode::Id::TMML: {
2991 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2964 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2992 "NODEP is not implemented"); 2965 "NODEP is not implemented");
2993 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2966 UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2994 "NDV is not implemented"); 2967 "NDV is not implemented");
2995 2968
2996 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2969 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2997 const bool is_array = instr.tmml.array != 0; 2970 const bool is_array = instr.tmml.array != 0;
@@ -3013,9 +2986,7 @@ private:
3013 break; 2986 break;
3014 } 2987 }
3015 default: 2988 default:
3016 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2989 UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type));
3017 static_cast<u32>(texture_type));
3018 UNREACHABLE();
3019 2990
3020 // Fallback to interpreting as a 2D texture for now 2991 // Fallback to interpreting as a 2D texture for now
3021 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2992 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -3038,8 +3009,7 @@ private:
3038 break; 3009 break;
3039 } 3010 }
3040 default: { 3011 default: {
3041 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); 3012 UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
3042 UNREACHABLE();
3043 } 3013 }
3044 } 3014 }
3045 break; 3015 break;
@@ -3125,7 +3095,7 @@ private:
3125 break; 3095 break;
3126 } 3096 }
3127 case OpCode::Type::HalfSetPredicate: { 3097 case OpCode::Type::HalfSetPredicate: {
3128 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented"); 3098 UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0);
3129 3099
3130 const std::string op_a = 3100 const std::string op_a =
3131 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a, 3101 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
@@ -3170,6 +3140,9 @@ private:
3170 break; 3140 break;
3171 } 3141 }
3172 case OpCode::Type::PredicateSetRegister: { 3142 case OpCode::Type::PredicateSetRegister: {
3143 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3144 "Condition codes generation in PSET is not implemented");
3145
3173 const std::string op_a = 3146 const std::string op_a =
3174 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 3147 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
3175 const std::string op_b = 3148 const std::string op_b =
@@ -3190,12 +3163,6 @@ private:
3190 const std::string value = '(' + result + ") ? 1.0 : 0.0"; 3163 const std::string value = '(' + result + ") ? 1.0 : 0.0";
3191 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3164 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
3192 } 3165 }
3193
3194 if (instr.generates_cc) {
3195 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3196 UNREACHABLE();
3197 }
3198
3199 break; 3166 break;
3200 } 3167 }
3201 case OpCode::Type::PredicateSetPredicate: { 3168 case OpCode::Type::PredicateSetPredicate: {
@@ -3233,21 +3200,19 @@ private:
3233 const std::string pred = 3200 const std::string pred =
3234 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 3201 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
3235 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 3202 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
3236 const std::string control_code = regs.GetControlCode(instr.csetp.cc); 3203 const std::string condition_code = regs.GetConditionCode(instr.csetp.cc);
3237 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 3204 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
3238 SetPredicate(instr.csetp.pred3, 3205 SetPredicate(instr.csetp.pred3,
3239 '(' + control_code + ") " + combiner + " (" + pred + ')'); 3206 '(' + condition_code + ") " + combiner + " (" + pred + ')');
3240 } 3207 }
3241 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 3208 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3242 SetPredicate(instr.csetp.pred0, 3209 SetPredicate(instr.csetp.pred0,
3243 "!(" + control_code + ") " + combiner + " (" + pred + ')'); 3210 "!(" + condition_code + ") " + combiner + " (" + pred + ')');
3244 } 3211 }
3245 break; 3212 break;
3246 } 3213 }
3247 default: { 3214 default: {
3248 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", 3215 UNIMPLEMENTED_MSG("Unhandled predicate instruction: {}", opcode->get().GetName());
3249 opcode->get().GetName());
3250 UNREACHABLE();
3251 } 3216 }
3252 } 3217 }
3253 break; 3218 break;
@@ -3327,7 +3292,7 @@ private:
3327 break; 3292 break;
3328 } 3293 }
3329 case OpCode::Type::HalfSet: { 3294 case OpCode::Type::HalfSet: {
3330 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented"); 3295 UNIMPLEMENTED_IF(instr.hset2.ftz != 0);
3331 3296
3332 const std::string op_a = 3297 const std::string op_a =
3333 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a, 3298 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
@@ -3371,15 +3336,17 @@ private:
3371 break; 3336 break;
3372 } 3337 }
3373 case OpCode::Type::Xmad: { 3338 case OpCode::Type::Xmad: {
3374 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3339 UNIMPLEMENTED_IF(instr.xmad.sign_a);
3375 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3340 UNIMPLEMENTED_IF(instr.xmad.sign_b);
3341 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3342 "Condition codes generation in XMAD is not implemented");
3376 3343
3377 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)}; 3344 std::string op_a{regs.GetRegisterAsInteger(instr.gpr8, 0, instr.xmad.sign_a)};
3378 std::string op_b; 3345 std::string op_b;
3379 std::string op_c; 3346 std::string op_c;
3380 3347
3381 // TODO(bunnei): Needs to be fixed once op_a or op_b is signed 3348 // TODO(bunnei): Needs to be fixed once op_a or op_b is signed
3382 ASSERT_MSG(instr.xmad.sign_a == instr.xmad.sign_b, "Unimplemented"); 3349 UNIMPLEMENTED_IF(instr.xmad.sign_a != instr.xmad.sign_b);
3383 const bool is_signed{instr.xmad.sign_a == 1}; 3350 const bool is_signed{instr.xmad.sign_a == 1};
3384 3351
3385 bool is_merge{}; 3352 bool is_merge{};
@@ -3412,8 +3379,7 @@ private:
3412 break; 3379 break;
3413 } 3380 }
3414 default: { 3381 default: {
3415 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); 3382 UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName());
3416 UNREACHABLE();
3417 } 3383 }
3418 } 3384 }
3419 3385
@@ -3449,9 +3415,8 @@ private:
3449 op_c = "((" + op_c + ") + (" + src2 + "<< 16))"; 3415 op_c = "((" + op_c + ") + (" + src2 + "<< 16))";
3450 break; 3416 break;
3451 default: { 3417 default: {
3452 LOG_CRITICAL(HW_GPU, "Unhandled XMAD mode: {}", 3418 UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}",
3453 static_cast<u32>(instr.xmad.mode.Value())); 3419 static_cast<u32>(instr.xmad.mode.Value()));
3454 UNREACHABLE();
3455 } 3420 }
3456 } 3421 }
3457 3422
@@ -3461,25 +3426,19 @@ private:
3461 } 3426 }
3462 3427
3463 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3428 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
3464 if (instr.generates_cc) {
3465 LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
3466 UNREACHABLE();
3467 }
3468 break; 3429 break;
3469 } 3430 }
3470 default: { 3431 default: {
3471 switch (opcode->get().GetId()) { 3432 switch (opcode->get().GetId()) {
3472 case OpCode::Id::EXIT: { 3433 case OpCode::Id::EXIT: {
3434 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3435 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3436 "EXIT condition code used: {}", static_cast<u32>(cc));
3437
3473 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { 3438 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
3474 EmitFragmentOutputsWrite(); 3439 EmitFragmentOutputsWrite();
3475 } 3440 }
3476 3441
3477 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3478 if (cc != Tegra::Shader::ControlCode::T) {
3479 LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
3480 UNREACHABLE();
3481 }
3482
3483 switch (instr.flow.cond) { 3442 switch (instr.flow.cond) {
3484 case Tegra::Shader::FlowCondition::Always: 3443 case Tegra::Shader::FlowCondition::Always:
3485 shader.AddLine("return true;"); 3444 shader.AddLine("return true;");
@@ -3494,26 +3453,24 @@ private:
3494 case Tegra::Shader::FlowCondition::Fcsm_Tr: 3453 case Tegra::Shader::FlowCondition::Fcsm_Tr:
3495 // TODO(bunnei): What is this used for? If we assume this conditon is not 3454 // TODO(bunnei): What is this used for? If we assume this conditon is not
3496 // satisifed, dual vertex shaders in Farming Simulator make more sense 3455 // satisifed, dual vertex shaders in Farming Simulator make more sense
3497 LOG_CRITICAL(HW_GPU, "Skipping unknown FlowCondition::Fcsm_Tr"); 3456 UNIMPLEMENTED_MSG("Skipping unknown FlowCondition::Fcsm_Tr");
3498 break; 3457 break;
3499 3458
3500 default: 3459 default:
3501 LOG_CRITICAL(HW_GPU, "Unhandled flow condition: {}", 3460 UNIMPLEMENTED_MSG("Unhandled flow condition: {}",
3502 static_cast<u32>(instr.flow.cond.Value())); 3461 static_cast<u32>(instr.flow.cond.Value()));
3503 UNREACHABLE();
3504 } 3462 }
3505 break; 3463 break;
3506 } 3464 }
3507 case OpCode::Id::KIL: { 3465 case OpCode::Id::KIL: {
3508 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3466 UNIMPLEMENTED_IF(instr.flow.cond != Tegra::Shader::FlowCondition::Always);
3467
3468 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3469 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3470 "KIL condition code used: {}", static_cast<u32>(cc));
3509 3471
3510 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3472 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
3511 // about unexecuted instructions that may follow this. 3473 // about unexecuted instructions that may follow this.
3512 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3513 if (cc != Tegra::Shader::ControlCode::T) {
3514 LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
3515 UNREACHABLE();
3516 }
3517 shader.AddLine("if (true) {"); 3474 shader.AddLine("if (true) {");
3518 ++shader.scope; 3475 ++shader.scope;
3519 shader.AddLine("discard;"); 3476 shader.AddLine("discard;");
@@ -3523,7 +3480,8 @@ private:
3523 break; 3480 break;
3524 } 3481 }
3525 case OpCode::Id::OUT_R: { 3482 case OpCode::Id::OUT_R: {
3526 ASSERT(instr.gpr20.Value() == Register::ZeroIndex); 3483 UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex,
3484 "Stream buffer is not supported");
3527 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3485 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3528 "OUT is expected to be used in a geometry shader."); 3486 "OUT is expected to be used in a geometry shader.");
3529 3487
@@ -3550,18 +3508,17 @@ private:
3550 break; 3508 break;
3551 } 3509 }
3552 default: { 3510 default: {
3553 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", 3511 UNIMPLEMENTED_MSG("Unhandled system move: {}",
3554 static_cast<u32>(instr.sys20.Value())); 3512 static_cast<u32>(instr.sys20.Value()));
3555 UNREACHABLE();
3556 } 3513 }
3557 } 3514 }
3558 break; 3515 break;
3559 } 3516 }
3560 case OpCode::Id::ISBERD: { 3517 case OpCode::Id::ISBERD: {
3561 ASSERT(instr.isberd.o == 0); 3518 UNIMPLEMENTED_IF(instr.isberd.o != 0);
3562 ASSERT(instr.isberd.skew == 0); 3519 UNIMPLEMENTED_IF(instr.isberd.skew != 0);
3563 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); 3520 UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None);
3564 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); 3521 UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None);
3565 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, 3522 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3566 "ISBERD is expected to be used in a geometry shader."); 3523 "ISBERD is expected to be used in a geometry shader.");
3567 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); 3524 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
@@ -3569,13 +3526,13 @@ private:
3569 break; 3526 break;
3570 } 3527 }
3571 case OpCode::Id::BRA: { 3528 case OpCode::Id::BRA: {
3572 ASSERT_MSG(instr.bra.constant_buffer == 0, 3529 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3573 "BRA with constant buffers are not implemented"); 3530 "BRA with constant buffers are not implemented");
3574 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3531
3575 if (cc != Tegra::Shader::ControlCode::T) { 3532 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3576 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc)); 3533 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3577 UNREACHABLE(); 3534 "BRA condition code used: {}", static_cast<u32>(cc));
3578 } 3535
3579 const u32 target = offset + instr.bra.GetBranchTarget(); 3536 const u32 target = offset + instr.bra.GetBranchTarget();
3580 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3537 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
3581 break; 3538 break;
@@ -3598,7 +3555,8 @@ private:
3598 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3555 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
3599 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3556 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
3600 // has a similar structure to the BRA opcode. 3557 // has a similar structure to the BRA opcode.
3601 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported"); 3558 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3559 "Constant buffer flow is not supported");
3602 3560
3603 const u32 target = offset + instr.bra.GetBranchTarget(); 3561 const u32 target = offset + instr.bra.GetBranchTarget();
3604 EmitPushToFlowStack(target); 3562 EmitPushToFlowStack(target);
@@ -3608,29 +3566,28 @@ private:
3608 // PBK pushes to a stack the address where BRK will jump to. This shares stack with 3566 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3609 // SSY but using SYNC on a PBK address will kill the shader execution. We don't 3567 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3610 // emulate this because it's very unlikely a driver will emit such invalid shader. 3568 // emulate this because it's very unlikely a driver will emit such invalid shader.
3611 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported"); 3569 UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
3570 "Constant buffer PBK is not supported");
3612 3571
3613 const u32 target = offset + instr.bra.GetBranchTarget(); 3572 const u32 target = offset + instr.bra.GetBranchTarget();
3614 EmitPushToFlowStack(target); 3573 EmitPushToFlowStack(target);
3615 break; 3574 break;
3616 } 3575 }
3617 case OpCode::Id::SYNC: { 3576 case OpCode::Id::SYNC: {
3577 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3578 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3579 "SYNC condition code used: {}", static_cast<u32>(cc));
3580
3618 // The SYNC opcode jumps to the address previously set by the SSY opcode 3581 // The SYNC opcode jumps to the address previously set by the SSY opcode
3619 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3620 if (cc != Tegra::Shader::ControlCode::T) {
3621 LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
3622 UNREACHABLE();
3623 }
3624 EmitPopFromFlowStack(); 3582 EmitPopFromFlowStack();
3625 break; 3583 break;
3626 } 3584 }
3627 case OpCode::Id::BRK: { 3585 case OpCode::Id::BRK: {
3628 // The BRK opcode jumps to the address previously set by the PBK opcode 3586 // The BRK opcode jumps to the address previously set by the PBK opcode
3629 const Tegra::Shader::ControlCode cc = instr.flow_control_code; 3587 const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
3630 if (cc != Tegra::Shader::ControlCode::T) { 3588 UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T,
3631 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc)); 3589 "BRK condition code used: {}", static_cast<u32>(cc));
3632 UNREACHABLE(); 3590
3633 }
3634 EmitPopFromFlowStack(); 3591 EmitPopFromFlowStack();
3635 break; 3592 break;
3636 } 3593 }
@@ -3641,6 +3598,9 @@ private:
3641 break; 3598 break;
3642 } 3599 }
3643 case OpCode::Id::VMAD: { 3600 case OpCode::Id::VMAD: {
3601 UNIMPLEMENTED_IF_MSG(instr.generates_cc,
3602 "Condition codes generation in VMAD is not implemented");
3603
3644 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1; 3604 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3645 const std::string op_a = GetVideoOperandA(instr); 3605 const std::string op_a = GetVideoOperandA(instr);
3646 const std::string op_b = GetVideoOperandB(instr); 3606 const std::string op_b = GetVideoOperandB(instr);
@@ -3660,11 +3620,6 @@ private:
3660 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3620 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3661 instr.vmad.saturate == 1, 0, Register::Size::Word, 3621 instr.vmad.saturate == 1, 0, Register::Size::Word,
3662 instr.vmad.cc); 3622 instr.vmad.cc);
3663 if (instr.generates_cc) {
3664 LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
3665 UNREACHABLE();
3666 }
3667
3668 break; 3623 break;
3669 } 3624 }
3670 case OpCode::Id::VSETP: { 3625 case OpCode::Id::VSETP: {
@@ -3691,10 +3646,7 @@ private:
3691 } 3646 }
3692 break; 3647 break;
3693 } 3648 }
3694 default: { 3649 default: { UNIMPLEMENTED_MSG("Unhandled instruction: {}", opcode->get().GetName()); }
3695 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
3696 UNREACHABLE();
3697 }
3698 } 3650 }
3699 3651
3700 break; 3652 break;
@@ -3819,6 +3771,7 @@ private:
3819 Maxwell3D::Regs::ShaderStage stage; 3771 Maxwell3D::Regs::ShaderStage stage;
3820 const std::string& suffix; 3772 const std::string& suffix;
3821 u64 local_memory_size; 3773 u64 local_memory_size;
3774 std::size_t shader_length;
3822 3775
3823 ShaderWriter shader; 3776 ShaderWriter shader;
3824 ShaderWriter declarations; 3777 ShaderWriter declarations;
@@ -3837,9 +3790,10 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
3837 Maxwell3D::Regs::ShaderStage stage, 3790 Maxwell3D::Regs::ShaderStage stage,
3838 const std::string& suffix) { 3791 const std::string& suffix) {
3839 try { 3792 try {
3840 const auto subroutines = 3793 ControlFlowAnalyzer analyzer(program_code, main_offset, suffix);
3841 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 3794 const auto subroutines = analyzer.GetSubroutines();
3842 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix); 3795 GLSLGenerator generator(subroutines, program_code, main_offset, stage, suffix,
3796 analyzer.GetShaderLength());
3843 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; 3797 return ProgramResult{generator.GetShaderCode(), generator.GetEntries()};
3844 } catch (const DecompileFail& exception) { 3798 } catch (const DecompileFail& exception) {
3845 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 3799 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 9d17edd63..eea090e52 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -82,8 +82,8 @@ void main() {
82} 82}
83 83
84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
85 std::string out = "#version 430 core\n"; 85 // Version is intentionally skipped in shader generation, it's added by the lazy compilation.
86 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 86 std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n";
87 out += Decompiler::GetCommonDeclarations(); 87 out += Decompiler::GetCommonDeclarations();
88 out += "bool exec_geometry();\n"; 88 out += "bool exec_geometry();\n";
89 89
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 520b9d4e3..b425d98ae 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -163,6 +163,7 @@ private:
163struct ShaderEntries { 163struct ShaderEntries {
164 std::vector<ConstBufferEntry> const_buffer_entries; 164 std::vector<ConstBufferEntry> const_buffer_entries;
165 std::vector<SamplerEntry> texture_samplers; 165 std::vector<SamplerEntry> texture_samplers;
166 std::size_t shader_length;
166}; 167};
167 168
168using ProgramResult = std::pair<std::string, ShaderEntries>; 169using ProgramResult = std::pair<std::string, ShaderEntries>;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 36fe1f04c..9a5d7e289 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -7,6 +7,7 @@
7#include <glad/glad.h> 7#include <glad/glad.h>
8 8
9#include "video_core/renderer_opengl/gl_resource_manager.h" 9#include "video_core/renderer_opengl/gl_resource_manager.h"
10#include "video_core/renderer_opengl/gl_state.h"
10#include "video_core/renderer_opengl/maxwell_to_gl.h" 11#include "video_core/renderer_opengl/maxwell_to_gl.h"
11 12
12namespace OpenGL::GLShader { 13namespace OpenGL::GLShader {
@@ -66,6 +67,7 @@ public:
66 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); 67 glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
67 state.draw.shader_program = 0; 68 state.draw.shader_program = 0;
68 state.draw.program_pipeline = pipeline.handle; 69 state.draw.program_pipeline = pipeline.handle;
70 state.geometry_shaders.enabled = (gs != 0);
69 } 71 }
70 72
71private: 73private:
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index b6b426f34..934f4db78 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
14bool OpenGLState::s_rgb_used; 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 geometry_shaders.enabled = false;
17 framebuffer_srgb.enabled = false; 18 framebuffer_srgb.enabled = false;
19 multisample_control.alpha_to_coverage = false;
20 multisample_control.alpha_to_one = false;
18 cull.enabled = false; 21 cull.enabled = false;
19 cull.mode = GL_BACK; 22 cull.mode = GL_BACK;
20 cull.front_face = GL_CCW; 23 cull.front_face = GL_CCW;
@@ -22,17 +25,15 @@ OpenGLState::OpenGLState() {
22 depth.test_enabled = false; 25 depth.test_enabled = false;
23 depth.test_func = GL_LESS; 26 depth.test_func = GL_LESS;
24 depth.write_mask = GL_TRUE; 27 depth.write_mask = GL_TRUE;
25 depth.depth_range_near = 0.0f;
26 depth.depth_range_far = 1.0f;
27 28
28 primitive_restart.enabled = false; 29 primitive_restart.enabled = false;
29 primitive_restart.index = 0; 30 primitive_restart.index = 0;
30 31 for (auto& item : color_mask) {
31 color_mask.red_enabled = GL_TRUE; 32 item.red_enabled = GL_TRUE;
32 color_mask.green_enabled = GL_TRUE; 33 item.green_enabled = GL_TRUE;
33 color_mask.blue_enabled = GL_TRUE; 34 item.blue_enabled = GL_TRUE;
34 color_mask.alpha_enabled = GL_TRUE; 35 item.alpha_enabled = GL_TRUE;
35 36 }
36 stencil.test_enabled = false; 37 stencil.test_enabled = false;
37 auto reset_stencil = [](auto& config) { 38 auto reset_stencil = [](auto& config) {
38 config.test_func = GL_ALWAYS; 39 config.test_func = GL_ALWAYS;
@@ -45,19 +46,33 @@ OpenGLState::OpenGLState() {
45 }; 46 };
46 reset_stencil(stencil.front); 47 reset_stencil(stencil.front);
47 reset_stencil(stencil.back); 48 reset_stencil(stencil.back);
48 49 for (auto& item : viewports) {
49 blend.enabled = true; 50 item.x = 0;
50 blend.rgb_equation = GL_FUNC_ADD; 51 item.y = 0;
51 blend.a_equation = GL_FUNC_ADD; 52 item.width = 0;
52 blend.src_rgb_func = GL_ONE; 53 item.height = 0;
53 blend.dst_rgb_func = GL_ZERO; 54 item.depth_range_near = 0.0f;
54 blend.src_a_func = GL_ONE; 55 item.depth_range_far = 1.0f;
55 blend.dst_a_func = GL_ZERO; 56 item.scissor.enabled = false;
56 blend.color.red = 0.0f; 57 item.scissor.x = 0;
57 blend.color.green = 0.0f; 58 item.scissor.y = 0;
58 blend.color.blue = 0.0f; 59 item.scissor.width = 0;
59 blend.color.alpha = 0.0f; 60 item.scissor.height = 0;
60 61 }
62 for (auto& item : blend) {
63 item.enabled = true;
64 item.rgb_equation = GL_FUNC_ADD;
65 item.a_equation = GL_FUNC_ADD;
66 item.src_rgb_func = GL_ONE;
67 item.dst_rgb_func = GL_ZERO;
68 item.src_a_func = GL_ONE;
69 item.dst_a_func = GL_ZERO;
70 }
71 independant_blend.enabled = false;
72 blend_color.red = 0.0f;
73 blend_color.green = 0.0f;
74 blend_color.blue = 0.0f;
75 blend_color.alpha = 0.0f;
61 logic_op.enabled = false; 76 logic_op.enabled = false;
62 logic_op.operation = GL_COPY; 77 logic_op.operation = GL_COPY;
63 78
@@ -73,20 +88,10 @@ OpenGLState::OpenGLState() {
73 draw.shader_program = 0; 88 draw.shader_program = 0;
74 draw.program_pipeline = 0; 89 draw.program_pipeline = 0;
75 90
76 scissor.enabled = false;
77 scissor.x = 0;
78 scissor.y = 0;
79 scissor.width = 0;
80 scissor.height = 0;
81
82 viewport.x = 0;
83 viewport.y = 0;
84 viewport.width = 0;
85 viewport.height = 0;
86
87 clip_distance = {}; 91 clip_distance = {};
88 92
89 point.size = 1; 93 point.size = 1;
94 fragment_color_clamp.enabled = false;
90} 95}
91 96
92void OpenGLState::ApplyDefaultState() { 97void OpenGLState::ApplyDefaultState() {
@@ -134,6 +139,32 @@ void OpenGLState::ApplyCulling() const {
134 } 139 }
135} 140}
136 141
142void OpenGLState::ApplyColorMask() const {
143 if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
144 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
145 const auto& updated = color_mask[i];
146 const auto& current = cur_state.color_mask[i];
147 if (updated.red_enabled != current.red_enabled ||
148 updated.green_enabled != current.green_enabled ||
149 updated.blue_enabled != current.blue_enabled ||
150 updated.alpha_enabled != current.alpha_enabled) {
151 glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
152 updated.blue_enabled, updated.alpha_enabled);
153 }
154 }
155 } else {
156 const auto& updated = color_mask[0];
157 const auto& current = cur_state.color_mask[0];
158 if (updated.red_enabled != current.red_enabled ||
159 updated.green_enabled != current.green_enabled ||
160 updated.blue_enabled != current.blue_enabled ||
161 updated.alpha_enabled != current.alpha_enabled) {
162 glColorMask(updated.red_enabled, updated.green_enabled, updated.blue_enabled,
163 updated.alpha_enabled);
164 }
165 }
166}
167
137void OpenGLState::ApplyDepth() const { 168void OpenGLState::ApplyDepth() const {
138 // Depth test 169 // Depth test
139 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled; 170 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
@@ -152,11 +183,6 @@ void OpenGLState::ApplyDepth() const {
152 if (depth.write_mask != cur_state.depth.write_mask) { 183 if (depth.write_mask != cur_state.depth.write_mask) {
153 glDepthMask(depth.write_mask); 184 glDepthMask(depth.write_mask);
154 } 185 }
155 // Depth range
156 if (depth.depth_range_near != cur_state.depth.depth_range_near ||
157 depth.depth_range_far != cur_state.depth.depth_range_far) {
158 glDepthRange(depth.depth_range_near, depth.depth_range_far);
159 }
160} 186}
161 187
162void OpenGLState::ApplyPrimitiveRestart() const { 188void OpenGLState::ApplyPrimitiveRestart() const {
@@ -207,53 +233,160 @@ void OpenGLState::ApplyStencilTest() const {
207 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 233 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
208 } 234 }
209} 235}
236// Viewport does not affects glClearBuffer so emulate viewport using scissor test
237void OpenGLState::EmulateViewportWithScissor() {
238 auto& current = viewports[0];
239 if (current.scissor.enabled) {
240 const GLint left = std::max(current.x, current.scissor.x);
241 const GLint right =
242 std::max(current.x + current.width, current.scissor.x + current.scissor.width);
243 const GLint bottom = std::max(current.y, current.scissor.y);
244 const GLint top =
245 std::max(current.y + current.height, current.scissor.y + current.scissor.height);
246 current.scissor.x = std::max(left, 0);
247 current.scissor.y = std::max(bottom, 0);
248 current.scissor.width = std::max(right - left, 0);
249 current.scissor.height = std::max(top - bottom, 0);
250 } else {
251 current.scissor.enabled = true;
252 current.scissor.x = current.x;
253 current.scissor.y = current.y;
254 current.scissor.width = current.width;
255 current.scissor.height = current.height;
256 }
257}
210 258
211void OpenGLState::ApplyScissorTest() const { 259void OpenGLState::ApplyViewport() const {
212 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled; 260 if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
213 if (scissor_changed) { 261 for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
214 if (scissor.enabled) { 262 i++) {
215 glEnable(GL_SCISSOR_TEST); 263 const auto& current = cur_state.viewports[i];
216 } else { 264 const auto& updated = viewports[i];
217 glDisable(GL_SCISSOR_TEST); 265 if (updated.x != current.x || updated.y != current.y ||
266 updated.width != current.width || updated.height != current.height) {
267 glViewportIndexedf(
268 i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
269 static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height));
270 }
271 if (updated.depth_range_near != current.depth_range_near ||
272 updated.depth_range_far != current.depth_range_far) {
273 glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
274 }
275 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
276 if (scissor_changed) {
277 if (updated.scissor.enabled) {
278 glEnablei(GL_SCISSOR_TEST, i);
279 } else {
280 glDisablei(GL_SCISSOR_TEST, i);
281 }
282 }
283 if (updated.scissor.enabled &&
284 (scissor_changed || updated.scissor.x != current.scissor.x ||
285 updated.scissor.y != current.scissor.y ||
286 updated.scissor.width != current.scissor.width ||
287 updated.scissor.height != current.scissor.height)) {
288 glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
289 updated.scissor.height);
290 }
291 }
292 } else {
293 const auto& current = cur_state.viewports[0];
294 const auto& updated = viewports[0];
295 if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
296 updated.height != current.height) {
297 glViewport(updated.x, updated.y, updated.width, updated.height);
298 }
299 if (updated.depth_range_near != current.depth_range_near ||
300 updated.depth_range_far != current.depth_range_far) {
301 glDepthRange(updated.depth_range_near, updated.depth_range_far);
302 }
303 const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
304 if (scissor_changed) {
305 if (updated.scissor.enabled) {
306 glEnable(GL_SCISSOR_TEST);
307 } else {
308 glDisable(GL_SCISSOR_TEST);
309 }
310 }
311 if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
312 updated.scissor.y != current.scissor.y ||
313 updated.scissor.width != current.scissor.width ||
314 updated.scissor.height != current.scissor.height)) {
315 glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
316 updated.scissor.height);
218 } 317 }
219 }
220 if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x ||
221 scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width ||
222 scissor.height != cur_state.scissor.height) {
223 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
224 } 318 }
225} 319}
226 320
227void OpenGLState::ApplyBlending() const { 321void OpenGLState::ApplyGlobalBlending() const {
228 const bool blend_changed = blend.enabled != cur_state.blend.enabled; 322 const Blend& current = cur_state.blend[0];
323 const Blend& updated = blend[0];
324 const bool blend_changed = updated.enabled != current.enabled;
229 if (blend_changed) { 325 if (blend_changed) {
230 if (blend.enabled) { 326 if (updated.enabled) {
231 ASSERT(!logic_op.enabled);
232 glEnable(GL_BLEND); 327 glEnable(GL_BLEND);
233 } else { 328 } else {
234 glDisable(GL_BLEND); 329 glDisable(GL_BLEND);
235 } 330 }
236 } 331 }
237 if (blend.enabled) { 332 if (!updated.enabled) {
238 if (blend_changed || blend.color.red != cur_state.blend.color.red || 333 return;
239 blend.color.green != cur_state.blend.color.green || 334 }
240 blend.color.blue != cur_state.blend.color.blue || 335 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
241 blend.color.alpha != cur_state.blend.color.alpha) { 336 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
242 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); 337 updated.dst_a_func != current.dst_a_func) {
243 } 338 glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
339 updated.dst_a_func);
340 }
341
342 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
343 updated.a_equation != current.a_equation) {
344 glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
345 }
346}
244 347
245 if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func || 348void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
246 blend.dst_rgb_func != cur_state.blend.dst_rgb_func || 349 const Blend& updated = blend[target];
247 blend.src_a_func != cur_state.blend.src_a_func || 350 const Blend& current = cur_state.blend[target];
248 blend.dst_a_func != cur_state.blend.dst_a_func) { 351 const bool blend_changed = updated.enabled != current.enabled || force;
249 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, 352 if (blend_changed) {
250 blend.dst_a_func); 353 if (updated.enabled) {
354 glEnablei(GL_BLEND, static_cast<GLuint>(target));
355 } else {
356 glDisablei(GL_BLEND, static_cast<GLuint>(target));
251 } 357 }
358 }
359 if (!updated.enabled) {
360 return;
361 }
362 if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
363 updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
364 updated.dst_a_func != current.dst_a_func) {
365 glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
366 updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
367 }
252 368
253 if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation || 369 if (blend_changed || updated.rgb_equation != current.rgb_equation ||
254 blend.a_equation != cur_state.blend.a_equation) { 370 updated.a_equation != current.a_equation) {
255 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); 371 glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
372 updated.a_equation);
373 }
374}
375
376void OpenGLState::ApplyBlending() const {
377 if (independant_blend.enabled) {
378 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
379 ApplyTargetBlending(i,
380 independant_blend.enabled != cur_state.independant_blend.enabled);
256 } 381 }
382 } else {
383 ApplyGlobalBlending();
384 }
385 if (blend_color.red != cur_state.blend_color.red ||
386 blend_color.green != cur_state.blend_color.green ||
387 blend_color.blue != cur_state.blend_color.blue ||
388 blend_color.alpha != cur_state.blend_color.alpha) {
389 glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
257 } 390 }
258} 391}
259 392
@@ -261,7 +394,6 @@ void OpenGLState::ApplyLogicOp() const {
261 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled; 394 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
262 if (logic_op_changed) { 395 if (logic_op_changed) {
263 if (logic_op.enabled) { 396 if (logic_op.enabled) {
264 ASSERT(!blend.enabled);
265 glEnable(GL_COLOR_LOGIC_OP); 397 glEnable(GL_COLOR_LOGIC_OP);
266 } else { 398 } else {
267 glDisable(GL_COLOR_LOGIC_OP); 399 glDisable(GL_COLOR_LOGIC_OP);
@@ -315,7 +447,7 @@ void OpenGLState::ApplySamplers() const {
315 } 447 }
316} 448}
317 449
318void OpenGLState::Apply() const { 450void OpenGLState::ApplyFramebufferState() const {
319 // Framebuffer 451 // Framebuffer
320 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 452 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
321 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 453 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -323,7 +455,9 @@ void OpenGLState::Apply() const {
323 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { 455 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
324 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); 456 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
325 } 457 }
458}
326 459
460void OpenGLState::ApplyVertexBufferState() const {
327 // Vertex array 461 // Vertex array
328 if (draw.vertex_array != cur_state.draw.vertex_array) { 462 if (draw.vertex_array != cur_state.draw.vertex_array) {
329 glBindVertexArray(draw.vertex_array); 463 glBindVertexArray(draw.vertex_array);
@@ -333,7 +467,11 @@ void OpenGLState::Apply() const {
333 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { 467 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
334 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); 468 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
335 } 469 }
470}
336 471
472void OpenGLState::Apply() const {
473 ApplyFramebufferState();
474 ApplyVertexBufferState();
337 // Uniform buffer 475 // Uniform buffer
338 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) { 476 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
339 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer); 477 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
@@ -348,12 +486,6 @@ void OpenGLState::Apply() const {
348 if (draw.program_pipeline != cur_state.draw.program_pipeline) { 486 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
349 glBindProgramPipeline(draw.program_pipeline); 487 glBindProgramPipeline(draw.program_pipeline);
350 } 488 }
351 // Viewport
352 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
353 viewport.width != cur_state.viewport.width ||
354 viewport.height != cur_state.viewport.height) {
355 glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
356 }
357 // Clip distance 489 // Clip distance
358 for (std::size_t i = 0; i < clip_distance.size(); ++i) { 490 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
359 if (clip_distance[i] != cur_state.clip_distance[i]) { 491 if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -364,19 +496,33 @@ void OpenGLState::Apply() const {
364 } 496 }
365 } 497 }
366 } 498 }
367 // Color mask
368 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
369 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
370 color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
371 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
372 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
373 color_mask.alpha_enabled);
374 }
375 // Point 499 // Point
376 if (point.size != cur_state.point.size) { 500 if (point.size != cur_state.point.size) {
377 glPointSize(point.size); 501 glPointSize(point.size);
378 } 502 }
379 ApplyScissorTest(); 503 if (GLAD_GL_ARB_color_buffer_float) {
504 if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
505 glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
506 fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
507 }
508 }
509 if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
510 if (multisample_control.alpha_to_coverage) {
511 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
512 } else {
513 glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
514 }
515 }
516 if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
517 if (multisample_control.alpha_to_one) {
518 glEnable(GL_SAMPLE_ALPHA_TO_ONE);
519 } else {
520 glDisable(GL_SAMPLE_ALPHA_TO_ONE);
521 }
522 }
523
524 ApplyColorMask();
525 ApplyViewport();
380 ApplyStencilTest(); 526 ApplyStencilTest();
381 ApplySRgb(); 527 ApplySRgb();
382 ApplyCulling(); 528 ApplyCulling();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index fe648aff6..032fc43f0 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -40,17 +40,28 @@ public:
40 } framebuffer_srgb; 40 } framebuffer_srgb;
41 41
42 struct { 42 struct {
43 bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
44 bool alpha_to_one; // GL_ALPHA_TO_ONE
45 } multisample_control;
46
47 struct {
48 bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
49 } fragment_color_clamp;
50
51 struct {
52 bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
53 } geometry_shaders;
54
55 struct {
43 bool enabled; // GL_CULL_FACE 56 bool enabled; // GL_CULL_FACE
44 GLenum mode; // GL_CULL_FACE_MODE 57 GLenum mode; // GL_CULL_FACE_MODE
45 GLenum front_face; // GL_FRONT_FACE 58 GLenum front_face; // GL_FRONT_FACE
46 } cull; 59 } cull;
47 60
48 struct { 61 struct {
49 bool test_enabled; // GL_DEPTH_TEST 62 bool test_enabled; // GL_DEPTH_TEST
50 GLenum test_func; // GL_DEPTH_FUNC 63 GLenum test_func; // GL_DEPTH_FUNC
51 GLboolean write_mask; // GL_DEPTH_WRITEMASK 64 GLboolean write_mask; // GL_DEPTH_WRITEMASK
52 GLfloat depth_range_near; // GL_DEPTH_RANGE
53 GLfloat depth_range_far; // GL_DEPTH_RANGE
54 } depth; 65 } depth;
55 66
56 struct { 67 struct {
@@ -58,13 +69,14 @@ public:
58 GLuint index; 69 GLuint index;
59 } primitive_restart; // GL_PRIMITIVE_RESTART 70 } primitive_restart; // GL_PRIMITIVE_RESTART
60 71
61 struct { 72 struct ColorMask {
62 GLboolean red_enabled; 73 GLboolean red_enabled;
63 GLboolean green_enabled; 74 GLboolean green_enabled;
64 GLboolean blue_enabled; 75 GLboolean blue_enabled;
65 GLboolean alpha_enabled; 76 GLboolean alpha_enabled;
66 } color_mask; // GL_COLOR_WRITEMASK 77 };
67 78 std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
79 color_mask; // GL_COLOR_WRITEMASK
68 struct { 80 struct {
69 bool test_enabled; // GL_STENCIL_TEST 81 bool test_enabled; // GL_STENCIL_TEST
70 struct { 82 struct {
@@ -78,7 +90,7 @@ public:
78 } front, back; 90 } front, back;
79 } stencil; 91 } stencil;
80 92
81 struct { 93 struct Blend {
82 bool enabled; // GL_BLEND 94 bool enabled; // GL_BLEND
83 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB 95 GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
84 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA 96 GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
@@ -86,14 +98,19 @@ public:
86 GLenum dst_rgb_func; // GL_BLEND_DST_RGB 98 GLenum dst_rgb_func; // GL_BLEND_DST_RGB
87 GLenum src_a_func; // GL_BLEND_SRC_ALPHA 99 GLenum src_a_func; // GL_BLEND_SRC_ALPHA
88 GLenum dst_a_func; // GL_BLEND_DST_ALPHA 100 GLenum dst_a_func; // GL_BLEND_DST_ALPHA
101 };
102 std::array<Blend, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> blend;
89 103
90 struct { 104 struct {
91 GLclampf red; 105 bool enabled;
92 GLclampf green; 106 } independant_blend;
93 GLclampf blue; 107
94 GLclampf alpha; 108 struct {
95 } color; // GL_BLEND_COLOR 109 GLclampf red;
96 } blend; 110 GLclampf green;
111 GLclampf blue;
112 GLclampf alpha;
113 } blend_color; // GL_BLEND_COLOR
97 114
98 struct { 115 struct {
99 bool enabled; // GL_LOGIC_OP_MODE 116 bool enabled; // GL_LOGIC_OP_MODE
@@ -138,20 +155,22 @@ public:
138 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING 155 GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
139 } draw; 156 } draw;
140 157
141 struct { 158 struct viewport {
142 bool enabled; // GL_SCISSOR_TEST
143 GLint x; 159 GLint x;
144 GLint y; 160 GLint y;
145 GLsizei width; 161 GLint width;
146 GLsizei height; 162 GLint height;
147 } scissor; 163 GLfloat depth_range_near; // GL_DEPTH_RANGE
148 164 GLfloat depth_range_far; // GL_DEPTH_RANGE
149 struct { 165 struct {
150 GLint x; 166 bool enabled; // GL_SCISSOR_TEST
151 GLint y; 167 GLint x;
152 GLsizei width; 168 GLint y;
153 GLsizei height; 169 GLsizei width;
154 } viewport; 170 GLsizei height;
171 } scissor;
172 };
173 std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
155 174
156 struct { 175 struct {
157 float size; // GL_POINT_SIZE 176 float size; // GL_POINT_SIZE
@@ -173,6 +192,10 @@ public:
173 } 192 }
174 /// Apply this state as the current OpenGL state 193 /// Apply this state as the current OpenGL state
175 void Apply() const; 194 void Apply() const;
195 /// Apply only the state afecting the framebuffer
196 void ApplyFramebufferState() const;
197 /// Apply only the state afecting the vertex buffer
198 void ApplyVertexBufferState() const;
176 /// Set the initial OpenGL state 199 /// Set the initial OpenGL state
177 static void ApplyDefaultState(); 200 static void ApplyDefaultState();
178 /// Resets any references to the given resource 201 /// Resets any references to the given resource
@@ -183,6 +206,7 @@ public:
183 OpenGLState& ResetBuffer(GLuint handle); 206 OpenGLState& ResetBuffer(GLuint handle);
184 OpenGLState& ResetVertexArray(GLuint handle); 207 OpenGLState& ResetVertexArray(GLuint handle);
185 OpenGLState& ResetFramebuffer(GLuint handle); 208 OpenGLState& ResetFramebuffer(GLuint handle);
209 void EmulateViewportWithScissor();
186 210
187private: 211private:
188 static OpenGLState cur_state; 212 static OpenGLState cur_state;
@@ -191,10 +215,13 @@ private:
191 static bool s_rgb_used; 215 static bool s_rgb_used;
192 void ApplySRgb() const; 216 void ApplySRgb() const;
193 void ApplyCulling() const; 217 void ApplyCulling() const;
218 void ApplyColorMask() const;
194 void ApplyDepth() const; 219 void ApplyDepth() const;
195 void ApplyPrimitiveRestart() const; 220 void ApplyPrimitiveRestart() const;
196 void ApplyStencilTest() const; 221 void ApplyStencilTest() const;
197 void ApplyScissorTest() const; 222 void ApplyViewport() const;
223 void ApplyTargetBlending(std::size_t target, bool force) const;
224 void ApplyGlobalBlending() const;
198 void ApplyBlending() const; 225 void ApplyBlending() const;
199 void ApplyLogicOp() const; 226 void ApplyLogicOp() const;
200 void ApplyTextures() const; 227 void ApplyTextures() const;
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index e409228cc..b97b895a4 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -6,9 +6,13 @@
6#include <vector> 6#include <vector>
7#include "common/alignment.h" 7#include "common/alignment.h"
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/microprofile.h"
9#include "video_core/renderer_opengl/gl_state.h" 10#include "video_core/renderer_opengl/gl_state.h"
10#include "video_core/renderer_opengl/gl_stream_buffer.h" 11#include "video_core/renderer_opengl/gl_stream_buffer.h"
11 12
13MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
14 MP_RGB(128, 128, 192));
15
12namespace OpenGL { 16namespace OpenGL {
13 17
14OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) 18OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
@@ -75,6 +79,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
75 } 79 }
76 80
77 if (invalidate || !persistent) { 81 if (invalidate || !persistent) {
82 MICROPROFILE_SCOPE(OpenGL_StreamBuffer);
78 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | 83 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
79 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | 84 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
80 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); 85 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 87d511c38..a8833c06e 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -159,10 +159,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
159 } 159 }
160 } 160 }
161 } 161 }
162 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 162 LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
163 static_cast<u32>(filter_mode)); 163 return GL_LINEAR;
164 UNREACHABLE();
165 return {};
166} 164}
167 165
168inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { 166inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -182,10 +180,15 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
182 return GL_CLAMP_TO_BORDER; 180 return GL_CLAMP_TO_BORDER;
183 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
184 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
183 case Tegra::Texture::WrapMode::MirrorOnceBorder:
184 if (GL_EXT_texture_mirror_clamp) {
185 return GL_MIRROR_CLAMP_TO_BORDER_EXT;
186 } else {
187 return GL_MIRROR_CLAMP_TO_EDGE;
188 }
185 } 189 }
186 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 190 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
187 UNREACHABLE(); 191 return GL_REPEAT;
188 return {};
189} 192}
190 193
191inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 194inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -207,28 +210,31 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
207 case Tegra::Texture::DepthCompareFunc::Always: 210 case Tegra::Texture::DepthCompareFunc::Always:
208 return GL_ALWAYS; 211 return GL_ALWAYS;
209 } 212 }
210 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", 213 LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
211 static_cast<u32>(func)); 214 static_cast<u32>(func));
212 UNREACHABLE(); 215 return GL_GREATER;
213 return {};
214} 216}
215 217
216inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 218inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
217 switch (equation) { 219 switch (equation) {
218 case Maxwell::Blend::Equation::Add: 220 case Maxwell::Blend::Equation::Add:
221 case Maxwell::Blend::Equation::AddGL:
219 return GL_FUNC_ADD; 222 return GL_FUNC_ADD;
220 case Maxwell::Blend::Equation::Subtract: 223 case Maxwell::Blend::Equation::Subtract:
224 case Maxwell::Blend::Equation::SubtractGL:
221 return GL_FUNC_SUBTRACT; 225 return GL_FUNC_SUBTRACT;
222 case Maxwell::Blend::Equation::ReverseSubtract: 226 case Maxwell::Blend::Equation::ReverseSubtract:
227 case Maxwell::Blend::Equation::ReverseSubtractGL:
223 return GL_FUNC_REVERSE_SUBTRACT; 228 return GL_FUNC_REVERSE_SUBTRACT;
224 case Maxwell::Blend::Equation::Min: 229 case Maxwell::Blend::Equation::Min:
230 case Maxwell::Blend::Equation::MinGL:
225 return GL_MIN; 231 return GL_MIN;
226 case Maxwell::Blend::Equation::Max: 232 case Maxwell::Blend::Equation::Max:
233 case Maxwell::Blend::Equation::MaxGL:
227 return GL_MAX; 234 return GL_MAX;
228 } 235 }
229 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 236 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
230 UNREACHABLE(); 237 return GL_FUNC_ADD;
231 return {};
232} 238}
233 239
234inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { 240inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
@@ -291,9 +297,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
291 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 297 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
292 return GL_ONE_MINUS_CONSTANT_ALPHA; 298 return GL_ONE_MINUS_CONSTANT_ALPHA;
293 } 299 }
294 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 300 LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
295 UNREACHABLE(); 301 return GL_ZERO;
296 return {};
297} 302}
298 303
299inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { 304inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
@@ -312,9 +317,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
312 case Tegra::Texture::SwizzleSource::OneFloat: 317 case Tegra::Texture::SwizzleSource::OneFloat:
313 return GL_ONE; 318 return GL_ONE;
314 } 319 }
315 LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 320 LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
316 UNREACHABLE(); 321 return GL_ZERO;
317 return {};
318} 322}
319 323
320inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { 324inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
@@ -344,33 +348,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
344 case Maxwell::ComparisonOp::AlwaysOld: 348 case Maxwell::ComparisonOp::AlwaysOld:
345 return GL_ALWAYS; 349 return GL_ALWAYS;
346 } 350 }
347 LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); 351 LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
348 UNREACHABLE(); 352 return GL_ALWAYS;
349 return {};
350} 353}
351 354
352inline GLenum StencilOp(Maxwell::StencilOp stencil) { 355inline GLenum StencilOp(Maxwell::StencilOp stencil) {
353 switch (stencil) { 356 switch (stencil) {
354 case Maxwell::StencilOp::Keep: 357 case Maxwell::StencilOp::Keep:
358 case Maxwell::StencilOp::KeepOGL:
355 return GL_KEEP; 359 return GL_KEEP;
356 case Maxwell::StencilOp::Zero: 360 case Maxwell::StencilOp::Zero:
361 case Maxwell::StencilOp::ZeroOGL:
357 return GL_ZERO; 362 return GL_ZERO;
358 case Maxwell::StencilOp::Replace: 363 case Maxwell::StencilOp::Replace:
364 case Maxwell::StencilOp::ReplaceOGL:
359 return GL_REPLACE; 365 return GL_REPLACE;
360 case Maxwell::StencilOp::Incr: 366 case Maxwell::StencilOp::Incr:
367 case Maxwell::StencilOp::IncrOGL:
361 return GL_INCR; 368 return GL_INCR;
362 case Maxwell::StencilOp::Decr: 369 case Maxwell::StencilOp::Decr:
370 case Maxwell::StencilOp::DecrOGL:
363 return GL_DECR; 371 return GL_DECR;
364 case Maxwell::StencilOp::Invert: 372 case Maxwell::StencilOp::Invert:
373 case Maxwell::StencilOp::InvertOGL:
365 return GL_INVERT; 374 return GL_INVERT;
366 case Maxwell::StencilOp::IncrWrap: 375 case Maxwell::StencilOp::IncrWrap:
376 case Maxwell::StencilOp::IncrWrapOGL:
367 return GL_INCR_WRAP; 377 return GL_INCR_WRAP;
368 case Maxwell::StencilOp::DecrWrap: 378 case Maxwell::StencilOp::DecrWrap:
379 case Maxwell::StencilOp::DecrWrapOGL:
369 return GL_DECR_WRAP; 380 return GL_DECR_WRAP;
370 } 381 }
371 LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); 382 LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
372 UNREACHABLE(); 383 return GL_KEEP;
373 return {};
374} 384}
375 385
376inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { 386inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
@@ -380,9 +390,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
380 case Maxwell::Cull::FrontFace::CounterClockWise: 390 case Maxwell::Cull::FrontFace::CounterClockWise:
381 return GL_CCW; 391 return GL_CCW;
382 } 392 }
383 LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); 393 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
384 UNREACHABLE(); 394 return GL_CCW;
385 return {};
386} 395}
387 396
388inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { 397inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
@@ -394,9 +403,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
394 case Maxwell::Cull::CullFace::FrontAndBack: 403 case Maxwell::Cull::CullFace::FrontAndBack:
395 return GL_FRONT_AND_BACK; 404 return GL_FRONT_AND_BACK;
396 } 405 }
397 LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); 406 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
398 UNREACHABLE(); 407 return GL_BACK;
399 return {};
400} 408}
401 409
402inline GLenum LogicOp(Maxwell::LogicOperation operation) { 410inline GLenum LogicOp(Maxwell::LogicOperation operation) {
@@ -434,9 +442,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
434 case Maxwell::LogicOperation::Set: 442 case Maxwell::LogicOperation::Set:
435 return GL_SET; 443 return GL_SET;
436 } 444 }
437 LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); 445 LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
438 UNREACHABLE(); 446 return GL_COPY;
439 return {};
440} 447}
441 448
442} // namespace MaxwellToGL 449} // namespace MaxwellToGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index ea38da932..1492e063a 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -304,6 +304,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
304 gl_framebuffer_data.resize(texture.width * texture.height * 4); 304 gl_framebuffer_data.resize(texture.width * texture.height * 4);
305 break; 305 break;
306 default: 306 default:
307 internal_format = GL_RGBA;
308 texture.gl_format = GL_RGBA;
309 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
310 gl_framebuffer_data.resize(texture.width * texture.height * 4);
311 LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}",
312 static_cast<u32>(framebuffer.pixel_format));
307 UNREACHABLE(); 313 UNREACHABLE();
308 } 314 }
309 315
@@ -484,7 +490,7 @@ bool RendererOpenGL::Init() {
484 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model); 490 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
485 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version); 491 Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
486 492
487 if (!GLAD_GL_VERSION_3_3) { 493 if (!GLAD_GL_VERSION_4_3) {
488 return false; 494 return false;
489 } 495 }
490 496
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index d9a97e30b..9582dd2ca 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -19,6 +19,8 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
19 return SurfaceTarget::Texture3D; 19 return SurfaceTarget::Texture3D;
20 case Tegra::Texture::TextureType::TextureCubemap: 20 case Tegra::Texture::TextureType::TextureCubemap:
21 return SurfaceTarget::TextureCubemap; 21 return SurfaceTarget::TextureCubemap;
22 case Tegra::Texture::TextureType::TextureCubeArray:
23 return SurfaceTarget::TextureCubeArray;
22 case Tegra::Texture::TextureType::Texture1DArray: 24 case Tegra::Texture::TextureType::Texture1DArray:
23 return SurfaceTarget::Texture1DArray; 25 return SurfaceTarget::Texture1DArray;
24 case Tegra::Texture::TextureType::Texture2DArray: 26 case Tegra::Texture::TextureType::Texture2DArray:
@@ -39,6 +41,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
39 case SurfaceTarget::Texture1DArray: 41 case SurfaceTarget::Texture1DArray:
40 case SurfaceTarget::Texture2DArray: 42 case SurfaceTarget::Texture2DArray:
41 case SurfaceTarget::TextureCubemap: 43 case SurfaceTarget::TextureCubemap:
44 case SurfaceTarget::TextureCubeArray:
42 return true; 45 return true;
43 default: 46 default:
44 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); 47 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
@@ -297,10 +300,14 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
297 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; 300 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
298 case Tegra::Texture::TextureFormat::ASTC_2D_5X4: 301 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
299 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; 302 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
303 case Tegra::Texture::TextureFormat::ASTC_2D_5X5:
304 return is_srgb ? PixelFormat::ASTC_2D_5X5_SRGB : PixelFormat::ASTC_2D_5X5;
300 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 305 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
301 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; 306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
302 case Tegra::Texture::TextureFormat::ASTC_2D_8X5: 307 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
303 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; 308 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
309 case Tegra::Texture::TextureFormat::ASTC_2D_10X8:
310 return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8;
304 case Tegra::Texture::TextureFormat::R16_G16: 311 case Tegra::Texture::TextureFormat::R16_G16:
305 switch (component_type) { 312 switch (component_type) {
306 case Tegra::Texture::ComponentType::FLOAT: 313 case Tegra::Texture::ComponentType::FLOAT:
@@ -440,12 +447,16 @@ bool IsPixelFormatASTC(PixelFormat format) {
440 switch (format) { 447 switch (format) {
441 case PixelFormat::ASTC_2D_4X4: 448 case PixelFormat::ASTC_2D_4X4:
442 case PixelFormat::ASTC_2D_5X4: 449 case PixelFormat::ASTC_2D_5X4:
450 case PixelFormat::ASTC_2D_5X5:
443 case PixelFormat::ASTC_2D_8X8: 451 case PixelFormat::ASTC_2D_8X8:
444 case PixelFormat::ASTC_2D_8X5: 452 case PixelFormat::ASTC_2D_8X5:
445 case PixelFormat::ASTC_2D_4X4_SRGB: 453 case PixelFormat::ASTC_2D_4X4_SRGB:
446 case PixelFormat::ASTC_2D_5X4_SRGB: 454 case PixelFormat::ASTC_2D_5X4_SRGB:
455 case PixelFormat::ASTC_2D_5X5_SRGB:
447 case PixelFormat::ASTC_2D_8X8_SRGB: 456 case PixelFormat::ASTC_2D_8X8_SRGB:
448 case PixelFormat::ASTC_2D_8X5_SRGB: 457 case PixelFormat::ASTC_2D_8X5_SRGB:
458 case PixelFormat::ASTC_2D_10X8:
459 case PixelFormat::ASTC_2D_10X8_SRGB:
449 return true; 460 return true;
450 default: 461 default:
451 return false; 462 return false;
@@ -453,27 +464,7 @@ bool IsPixelFormatASTC(PixelFormat format) {
453} 464}
454 465
455std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { 466std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
456 switch (format) { 467 return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
457 case PixelFormat::ASTC_2D_4X4:
458 return {4, 4};
459 case PixelFormat::ASTC_2D_5X4:
460 return {5, 4};
461 case PixelFormat::ASTC_2D_8X8:
462 return {8, 8};
463 case PixelFormat::ASTC_2D_8X5:
464 return {8, 5};
465 case PixelFormat::ASTC_2D_4X4_SRGB:
466 return {4, 4};
467 case PixelFormat::ASTC_2D_5X4_SRGB:
468 return {5, 4};
469 case PixelFormat::ASTC_2D_8X8_SRGB:
470 return {8, 8};
471 case PixelFormat::ASTC_2D_8X5_SRGB:
472 return {8, 5};
473 default:
474 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
475 UNREACHABLE();
476 }
477} 468}
478 469
479bool IsFormatBCn(PixelFormat format) { 470bool IsFormatBCn(PixelFormat format) {
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 3232e437f..0dd3eb2e4 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -72,19 +72,23 @@ enum class PixelFormat {
72 ASTC_2D_8X8_SRGB = 54, 72 ASTC_2D_8X8_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55, 73 ASTC_2D_8X5_SRGB = 55,
74 ASTC_2D_5X4_SRGB = 56, 74 ASTC_2D_5X4_SRGB = 56,
75 ASTC_2D_5X5 = 57,
76 ASTC_2D_5X5_SRGB = 58,
77 ASTC_2D_10X8 = 59,
78 ASTC_2D_10X8_SRGB = 60,
75 79
76 MaxColorFormat, 80 MaxColorFormat,
77 81
78 // Depth formats 82 // Depth formats
79 Z32F = 57, 83 Z32F = 61,
80 Z16 = 58, 84 Z16 = 62,
81 85
82 MaxDepthFormat, 86 MaxDepthFormat,
83 87
84 // DepthStencil formats 88 // DepthStencil formats
85 Z24S8 = 59, 89 Z24S8 = 63,
86 S8Z24 = 60, 90 S8Z24 = 64,
87 Z32FS8 = 61, 91 Z32FS8 = 65,
88 92
89 MaxDepthStencilFormat, 93 MaxDepthStencilFormat,
90 94
@@ -118,6 +122,7 @@ enum class SurfaceTarget {
118 Texture1DArray, 122 Texture1DArray,
119 Texture2DArray, 123 Texture2DArray,
120 TextureCubemap, 124 TextureCubemap,
125 TextureCubeArray,
121}; 126};
122 127
123/** 128/**
@@ -188,6 +193,10 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
188 4, // ASTC_2D_8X8_SRGB 193 4, // ASTC_2D_8X8_SRGB
189 4, // ASTC_2D_8X5_SRGB 194 4, // ASTC_2D_8X5_SRGB
190 4, // ASTC_2D_5X4_SRGB 195 4, // ASTC_2D_5X4_SRGB
196 4, // ASTC_2D_5X5
197 4, // ASTC_2D_5X5_SRGB
198 4, // ASTC_2D_10X8
199 4, // ASTC_2D_10X8_SRGB
191 1, // Z32F 200 1, // Z32F
192 1, // Z16 201 1, // Z16
193 1, // Z24S8 202 1, // Z24S8
@@ -199,6 +208,81 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
199 return compression_factor_table[static_cast<std::size_t>(format)]; 208 return compression_factor_table[static_cast<std::size_t>(format)];
200} 209}
201 210
211static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
212 if (format == PixelFormat::Invalid)
213 return 0;
214 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
215 1, // ABGR8U
216 1, // ABGR8S
217 1, // ABGR8UI
218 1, // B5G6R5U
219 1, // A2B10G10R10U
220 1, // A1B5G5R5U
221 1, // R8U
222 1, // R8UI
223 1, // RGBA16F
224 1, // RGBA16U
225 1, // RGBA16UI
226 1, // R11FG11FB10F
227 1, // RGBA32UI
228 4, // DXT1
229 4, // DXT23
230 4, // DXT45
231 4, // DXN1
232 4, // DXN2UNORM
233 4, // DXN2SNORM
234 4, // BC7U
235 4, // BC6H_UF16
236 4, // BC6H_SF16
237 4, // ASTC_2D_4X4
238 1, // G8R8U
239 1, // G8R8S
240 1, // BGRA8
241 1, // RGBA32F
242 1, // RG32F
243 1, // R32F
244 1, // R16F
245 1, // R16U
246 1, // R16S
247 1, // R16UI
248 1, // R16I
249 1, // RG16
250 1, // RG16F
251 1, // RG16UI
252 1, // RG16I
253 1, // RG16S
254 1, // RGB32F
255 1, // RGBA8_SRGB
256 1, // RG8U
257 1, // RG8S
258 1, // RG32UI
259 1, // R32UI
260 8, // ASTC_2D_8X8
261 8, // ASTC_2D_8X5
262 5, // ASTC_2D_5X4
263 1, // BGRA8_SRGB
264 4, // DXT1_SRGB
265 4, // DXT23_SRGB
266 4, // DXT45_SRGB
267 4, // BC7U_SRGB
268 4, // ASTC_2D_4X4_SRGB
269 8, // ASTC_2D_8X8_SRGB
270 8, // ASTC_2D_8X5_SRGB
271 5, // ASTC_2D_5X4_SRGB
272 5, // ASTC_2D_5X5
273 5, // ASTC_2D_5X5_SRGB
274 10, // ASTC_2D_10X8
275 10, // ASTC_2D_10X8_SRGB
276 1, // Z32F
277 1, // Z16
278 1, // Z24S8
279 1, // S8Z24
280 1, // Z32FS8
281 }};
282 ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
283 return block_width_table[static_cast<std::size_t>(format)];
284}
285
202static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { 286static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
203 if (format == PixelFormat::Invalid) 287 if (format == PixelFormat::Invalid)
204 return 0; 288 return 0;
@@ -261,6 +345,10 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
261 8, // ASTC_2D_8X8_SRGB 345 8, // ASTC_2D_8X8_SRGB
262 5, // ASTC_2D_8X5_SRGB 346 5, // ASTC_2D_8X5_SRGB
263 4, // ASTC_2D_5X4_SRGB 347 4, // ASTC_2D_5X4_SRGB
348 5, // ASTC_2D_5X5
349 5, // ASTC_2D_5X5_SRGB
350 8, // ASTC_2D_10X8
351 8, // ASTC_2D_10X8_SRGB
264 1, // Z32F 352 1, // Z32F
265 1, // Z16 353 1, // Z16
266 1, // Z24S8 354 1, // Z24S8
@@ -299,7 +387,7 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
299 128, // BC7U 387 128, // BC7U
300 128, // BC6H_UF16 388 128, // BC6H_UF16
301 128, // BC6H_SF16 389 128, // BC6H_SF16
302 32, // ASTC_2D_4X4 390 128, // ASTC_2D_4X4
303 16, // G8R8U 391 16, // G8R8U
304 16, // G8R8S 392 16, // G8R8S
305 32, // BGRA8 393 32, // BGRA8
@@ -322,18 +410,22 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
322 16, // RG8S 410 16, // RG8S
323 64, // RG32UI 411 64, // RG32UI
324 32, // R32UI 412 32, // R32UI
325 16, // ASTC_2D_8X8 413 128, // ASTC_2D_8X8
326 16, // ASTC_2D_8X5 414 128, // ASTC_2D_8X5
327 32, // ASTC_2D_5X4 415 128, // ASTC_2D_5X4
328 32, // BGRA8_SRGB 416 32, // BGRA8_SRGB
329 64, // DXT1_SRGB 417 64, // DXT1_SRGB
330 128, // DXT23_SRGB 418 128, // DXT23_SRGB
331 128, // DXT45_SRGB 419 128, // DXT45_SRGB
332 128, // BC7U 420 128, // BC7U
333 32, // ASTC_2D_4X4_SRGB 421 128, // ASTC_2D_4X4_SRGB
334 16, // ASTC_2D_8X8_SRGB 422 128, // ASTC_2D_8X8_SRGB
335 16, // ASTC_2D_8X5_SRGB 423 128, // ASTC_2D_8X5_SRGB
336 32, // ASTC_2D_5X4_SRGB 424 128, // ASTC_2D_5X4_SRGB
425 128, // ASTC_2D_5X5
426 128, // ASTC_2D_5X5_SRGB
427 128, // ASTC_2D_10X8
428 128, // ASTC_2D_10X8_SRGB
337 32, // Z32F 429 32, // Z32F
338 16, // Z16 430 16, // Z16
339 32, // Z24S8 431 32, // Z24S8
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b1feacae9..bc50a4876 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1598,27 +1598,29 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1598namespace Tegra::Texture::ASTC { 1598namespace Tegra::Texture::ASTC {
1599 1599
1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
1601 uint32_t block_width, uint32_t block_height) { 1601 uint32_t depth, uint32_t block_width, uint32_t block_height) {
1602 uint32_t blockIdx = 0; 1602 uint32_t blockIdx = 0;
1603 std::vector<uint8_t> outData(height * width * 4); 1603 std::vector<uint8_t> outData(height * width * depth * 4);
1604 for (uint32_t j = 0; j < height; j += block_height) { 1604 for (uint32_t k = 0; k < depth; k++) {
1605 for (uint32_t i = 0; i < width; i += block_width) { 1605 for (uint32_t j = 0; j < height; j += block_height) {
1606 for (uint32_t i = 0; i < width; i += block_width) {
1606 1607
1607 uint8_t* blockPtr = data.data() + blockIdx * 16; 1608 uint8_t* blockPtr = data.data() + blockIdx * 16;
1608 1609
1609 // Blocks can be at most 12x12 1610 // Blocks can be at most 12x12
1610 uint32_t uncompData[144]; 1611 uint32_t uncompData[144];
1611 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); 1612 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
1612 1613
1613 uint32_t decompWidth = std::min(block_width, width - i); 1614 uint32_t decompWidth = std::min(block_width, width - i);
1614 uint32_t decompHeight = std::min(block_height, height - j); 1615 uint32_t decompHeight = std::min(block_height, height - j);
1615 1616
1616 uint8_t* outRow = outData.data() + (j * width + i) * 4; 1617 uint8_t* outRow = outData.data() + (j * width + i) * 4;
1617 for (uint32_t jj = 0; jj < decompHeight; jj++) { 1618 for (uint32_t jj = 0; jj < decompHeight; jj++) {
1618 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); 1619 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
1619 } 1620 }
1620 1621
1621 blockIdx++; 1622 blockIdx++;
1623 }
1622 } 1624 }
1623 } 1625 }
1624 1626
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index f0d7c0e56..d419dd025 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -10,6 +10,6 @@
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
13 uint32_t block_width, uint32_t block_height); 13 uint32_t depth, uint32_t block_width, uint32_t block_height);
14 14
15} // namespace Tegra::Texture::ASTC 15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 550ca856c..7eabd34f1 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -37,25 +37,28 @@ struct alignas(64) SwizzleTable {
37 std::array<std::array<u16, M>, N> values{}; 37 std::array<std::array<u16, M>, N> values{};
38}; 38};
39 39
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr u32 gob_size_x = 64;
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr u32 gob_size_y = 8;
42constexpr u32 gob_size_z = 1;
43constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
44constexpr u32 fast_swizzle_align = 16;
45
46constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
47constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
42 48
43/** 49/**
44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block. 50 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
45 * Instead of going gob by gob, we map the coordinates inside a block and manage from 51 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1. 52 * those. Block_Width is assumed to be 1.
47 */ 53 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 54void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 55 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset, 56 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 57 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) { 58 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
53 std::array<u8*, 2> data_ptrs; 59 std::array<u8*, 2> data_ptrs;
54 u32 z_address = tile_offset; 60 u32 z_address = tile_offset;
55 const u32 gob_size_x = 64; 61
56 const u32 gob_size_y = 8;
57 const u32 gob_size_z = 1;
58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
59 for (u32 z = z_start; z < z_end; z++) { 62 for (u32 z = z_start; z < z_end; z++) {
60 u32 y_address = z_address; 63 u32 y_address = z_address;
61 u32 pixel_base = layer_z * z + y_start * stride_x; 64 u32 pixel_base = layer_z * z + y_start * stride_x;
@@ -81,7 +84,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
81 * Instead of going gob by gob, we map the coordinates inside a block and manage from 84 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1. 85 * those. Block_Width is assumed to be 1.
83 */ 86 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 87void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, 88 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset, 89 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x, 90 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
@@ -90,23 +93,19 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
90 u32 z_address = tile_offset; 93 u32 z_address = tile_offset;
91 const u32 x_startb = x_start * bytes_per_pixel; 94 const u32 x_startb = x_start * bytes_per_pixel;
92 const u32 x_endb = x_end * bytes_per_pixel; 95 const u32 x_endb = x_end * bytes_per_pixel;
93 const u32 copy_size = 16; 96
94 const u32 gob_size_x = 64;
95 const u32 gob_size_y = 8;
96 const u32 gob_size_z = 1;
97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
98 for (u32 z = z_start; z < z_end; z++) { 97 for (u32 z = z_start; z < z_end; z++) {
99 u32 y_address = z_address; 98 u32 y_address = z_address;
100 u32 pixel_base = layer_z * z + y_start * stride_x; 99 u32 pixel_base = layer_z * z + y_start * stride_x;
101 for (u32 y = y_start; y < y_end; y++) { 100 for (u32 y = y_start; y < y_end; y++) {
102 const auto& table = fast_swizzle_table[y % gob_size_y]; 101 const auto& table = fast_swizzle_table[y % gob_size_y];
103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) { 102 for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]}; 103 const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 104 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
106 const u32 pixel_index{out_x + pixel_base}; 105 const u32 pixel_index{out_x + pixel_base};
107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 106 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 107 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 108 std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
110 } 109 }
111 pixel_base += stride_x; 110 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0) 111 if ((y + 1) % gob_size_y == 0)
@@ -126,23 +125,21 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces 125 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */ 126 */
128template <bool fast> 127template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width, 128void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel, 129 const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) { 130 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; 131 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel; 132 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x; 133 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64; 134 const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; 135 constexpr u32 gob_elements_y = gob_size_y;
137 const u32 gob_elements_y = 8; 136 constexpr u32 gob_elements_z = gob_size_z;
138 const u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x; 137 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height; 138 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth; 139 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements); 140 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements); 141 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements); 142 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 xy_block_size = gob_size * block_height; 143 const u32 xy_block_size = gob_size * block_height;
147 const u32 block_size = xy_block_size * block_depth; 144 const u32 block_size = xy_block_size * block_depth;
148 u32 tile_offset = 0; 145 u32 tile_offset = 0;
@@ -171,9 +168,9 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
171} 168}
172 169
173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 170void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
174 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, 171 u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
175 bool unswizzle, u32 block_height, u32 block_depth) { 172 bool unswizzle, u32 block_height, u32 block_depth) {
176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 173 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, 174 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
178 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth); 175 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
179 } else { 176 } else {
@@ -202,6 +199,8 @@ u32 BytesPerPixel(TextureFormat format) {
202 case TextureFormat::ASTC_2D_5X4: 199 case TextureFormat::ASTC_2D_5X4:
203 case TextureFormat::ASTC_2D_8X8: 200 case TextureFormat::ASTC_2D_8X8:
204 case TextureFormat::ASTC_2D_8X5: 201 case TextureFormat::ASTC_2D_8X5:
202 case TextureFormat::ASTC_2D_10X8:
203 case TextureFormat::ASTC_2D_5X5:
205 case TextureFormat::A8R8G8B8: 204 case TextureFormat::A8R8G8B8:
206 case TextureFormat::A2B10G10R10: 205 case TextureFormat::A2B10G10R10:
207 case TextureFormat::BF10GF11RF11: 206 case TextureFormat::BF10GF11RF11:
@@ -227,27 +226,38 @@ u32 BytesPerPixel(TextureFormat format) {
227 } 226 }
228} 227}
229 228
230std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 229void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
231 u32 height, u32 depth, u32 block_height, u32 block_depth) { 230 u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
231 u32 block_depth) {
232 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
233 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
234 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
235 block_height, block_depth);
236}
237
238std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
239 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
240 u32 block_height, u32 block_depth) {
232 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); 241 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
233 CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel, 242 UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
234 Memory::GetPointer(address), unswizzled_data.data(), true, block_height, 243 width, height, depth, block_height, block_depth);
235 block_depth);
236 return unswizzled_data; 244 return unswizzled_data;
237} 245}
238 246
239void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 247void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
240 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, 248 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
241 u32 block_height) { 249 u32 block_height) {
242 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64}; 250 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
251 gob_size_x};
243 for (u32 line = 0; line < subrect_height; ++line) { 252 for (u32 line = 0; line < subrect_height; ++line) {
244 const u32 gob_address_y = 253 const u32 gob_address_y =
245 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs + 254 (line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
246 (line % (8 * block_height) / 8) * 512; 255 ((line % (gob_size_y * block_height)) / gob_size_y) * gob_size;
247 const auto& table = legacy_swizzle_table[line % 8]; 256 const auto& table = legacy_swizzle_table[line % gob_size_y];
248 for (u32 x = 0; x < subrect_width; ++x) { 257 for (u32 x = 0; x < subrect_width; ++x) {
249 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height; 258 const u32 gob_address =
250 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64]; 259 gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
260 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
251 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; 261 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
252 const VAddr dest_addr = swizzled_data + swizzled_offset; 262 const VAddr dest_addr = swizzled_data + swizzled_offset;
253 263
@@ -261,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
261 u32 block_height, u32 offset_x, u32 offset_y) { 271 u32 block_height, u32 offset_x, u32 offset_y) {
262 for (u32 line = 0; line < subrect_height; ++line) { 272 for (u32 line = 0; line < subrect_height; ++line) {
263 const u32 y2 = line + offset_y; 273 const u32 y2 = line + offset_y;
264 const u32 gob_address_y = 274 const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
265 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512; 275 ((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
266 const auto& table = legacy_swizzle_table[y2 % 8]; 276 const auto& table = legacy_swizzle_table[y2 % gob_size_y];
267 for (u32 x = 0; x < subrect_width; ++x) { 277 for (u32 x = 0; x < subrect_width; ++x) {
268 const u32 x2 = (x + offset_x) * bytes_per_pixel; 278 const u32 x2 = (x + offset_x) * bytes_per_pixel;
269 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height; 279 const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
270 const u32 swizzled_offset = gob_address + table[x2 % 64]; 280 const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
271 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; 281 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
272 const VAddr source_addr = swizzled_data + swizzled_offset; 282 const VAddr source_addr = swizzled_data + swizzled_offset;
273 283
@@ -292,6 +302,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
292 case TextureFormat::BC6H_SF16: 302 case TextureFormat::BC6H_SF16:
293 case TextureFormat::ASTC_2D_4X4: 303 case TextureFormat::ASTC_2D_4X4:
294 case TextureFormat::ASTC_2D_8X8: 304 case TextureFormat::ASTC_2D_8X8:
305 case TextureFormat::ASTC_2D_5X5:
306 case TextureFormat::ASTC_2D_10X8:
295 case TextureFormat::A8R8G8B8: 307 case TextureFormat::A8R8G8B8:
296 case TextureFormat::A2B10G10R10: 308 case TextureFormat::A2B10G10R10:
297 case TextureFormat::A1B5G5R5: 309 case TextureFormat::A1B5G5R5:
@@ -319,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
319std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 331std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
320 u32 block_height, u32 block_depth) { 332 u32 block_height, u32 block_depth) {
321 if (tiled) { 333 if (tiled) {
322 const u32 gobs_in_x = 64; 334 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x);
323 const u32 gobs_in_y = 8; 335 const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height);
324 const u32 gobs_in_z = 1; 336 const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth);
325 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
326 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
327 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
328 return aligned_width * aligned_height * aligned_depth; 337 return aligned_width * aligned_height * aligned_depth;
329 } else { 338 } else {
330 return width * height * depth * bytes_per_pixel; 339 return width * height * depth * bytes_per_pixel;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index b390219e4..f4ef7c73e 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -19,8 +19,15 @@ inline std::size_t GetGOBSize() {
19/** 19/**
20 * Unswizzles a swizzled texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 22void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 height, u32 depth, 23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight);
26/**
27 * Unswizzles a swizzled texture without changing its format.
28 */
29std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
30 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight, 31 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight); 32 u32 block_depth = TICEntry::DefaultBlockHeight);
26 33
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index d12d2ecb8..ffa08f5c1 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -168,20 +168,30 @@ struct TICEntry {
168 168
169 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
170 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
171 171 BitField<26, 1, u32> use_header_opt_control;
172 BitField<27, 1, u32> depth_texture;
172 BitField<28, 4, u32> max_mip_level; 173 BitField<28, 4, u32> max_mip_level;
173 }; 174 };
174 union { 175 union {
175 BitField<0, 16, u32> width_minus_1; 176 BitField<0, 16, u32> width_minus_1;
176 BitField<22, 1, u32> srgb_conversion; 177 BitField<22, 1, u32> srgb_conversion;
177 BitField<23, 4, TextureType> texture_type; 178 BitField<23, 4, TextureType> texture_type;
179 BitField<29, 3, u32> border_size;
178 }; 180 };
179 union { 181 union {
180 BitField<0, 16, u32> height_minus_1; 182 BitField<0, 16, u32> height_minus_1;
181 BitField<16, 15, u32> depth_minus_1; 183 BitField<16, 15, u32> depth_minus_1;
182 }; 184 };
185 union {
186 BitField<6, 13, u32> mip_lod_bias;
187 BitField<27, 3, u32> max_anisotropy;
188 };
183 189
184 INSERT_PADDING_BYTES(8); 190 union {
191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level;
193 BitField<12, 12, u32> min_lod_clamp;
194 };
185 195
186 GPUVAddr Address() const { 196 GPUVAddr Address() const {
187 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); 197 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
@@ -275,13 +285,25 @@ struct TSCEntry {
275 BitField<6, 3, WrapMode> wrap_p; 285 BitField<6, 3, WrapMode> wrap_p;
276 BitField<9, 1, u32> depth_compare_enabled; 286 BitField<9, 1, u32> depth_compare_enabled;
277 BitField<10, 3, DepthCompareFunc> depth_compare_func; 287 BitField<10, 3, DepthCompareFunc> depth_compare_func;
288 BitField<13, 1, u32> srgb_conversion;
289 BitField<20, 3, u32> max_anisotropy;
278 }; 290 };
279 union { 291 union {
280 BitField<0, 2, TextureFilter> mag_filter; 292 BitField<0, 2, TextureFilter> mag_filter;
281 BitField<4, 2, TextureFilter> min_filter; 293 BitField<4, 2, TextureFilter> min_filter;
282 BitField<6, 2, TextureMipmapFilter> mip_filter; 294 BitField<6, 2, TextureMipmapFilter> mip_filter;
295 BitField<9, 1, u32> cubemap_interface_filtering;
296 BitField<12, 13, u32> mip_lod_bias;
297 };
298 union {
299 BitField<0, 12, u32> min_lod_clamp;
300 BitField<12, 12, u32> max_lod_clamp;
301 BitField<24, 8, u32> srgb_border_color_r;
302 };
303 union {
304 BitField<12, 8, u32> srgb_border_color_g;
305 BitField<20, 8, u32> srgb_border_color_b;
283 }; 306 };
284 INSERT_PADDING_BYTES(8);
285 float border_color_r; 307 float border_color_r;
286 float border_color_g; 308 float border_color_g;
287 float border_color_b; 309 float border_color_b;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f9ca2948e..cfca8f4a8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -7,6 +7,8 @@ add_executable(yuzu
7 Info.plist 7 Info.plist
8 about_dialog.cpp 8 about_dialog.cpp
9 about_dialog.h 9 about_dialog.h
10 applets/software_keyboard.cpp
11 applets/software_keyboard.h
10 bootmanager.cpp 12 bootmanager.cpp
11 bootmanager.h 13 bootmanager.h
12 compatibility_list.cpp 14 compatibility_list.cpp
@@ -27,8 +29,14 @@ add_executable(yuzu
27 configuration/configure_graphics.h 29 configuration/configure_graphics.h
28 configuration/configure_input.cpp 30 configuration/configure_input.cpp
29 configuration/configure_input.h 31 configuration/configure_input.h
32 configuration/configure_input_player.cpp
33 configuration/configure_input_player.h
34 configuration/configure_mouse_advanced.cpp
35 configuration/configure_mouse_advanced.h
30 configuration/configure_system.cpp 36 configuration/configure_system.cpp
31 configuration/configure_system.h 37 configuration/configure_system.h
38 configuration/configure_touchscreen_advanced.cpp
39 configuration/configure_touchscreen_advanced.h
32 configuration/configure_web.cpp 40 configuration/configure_web.cpp
33 configuration/configure_web.h 41 configuration/configure_web.h
34 debugger/graphics/graphics_breakpoint_observer.cpp 42 debugger/graphics/graphics_breakpoint_observer.cpp
@@ -76,7 +84,10 @@ set(UIS
76 configuration/configure_general.ui 84 configuration/configure_general.ui
77 configuration/configure_graphics.ui 85 configuration/configure_graphics.ui
78 configuration/configure_input.ui 86 configuration/configure_input.ui
87 configuration/configure_input_player.ui
88 configuration/configure_mouse_advanced.ui
79 configuration/configure_system.ui 89 configuration/configure_system.ui
90 configuration/configure_touchscreen_advanced.ui
80 configuration/configure_web.ui 91 configuration/configure_web.ui
81 hotkeys.ui 92 hotkeys.ui
82 main.ui 93 main.ui
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
new file mode 100644
index 000000000..8a26fdff1
--- /dev/null
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -0,0 +1,152 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <mutex>
7#include <QDialogButtonBox>
8#include <QFont>
9#include <QLabel>
10#include <QLineEdit>
11#include <QVBoxLayout>
12#include "core/hle/lock.h"
13#include "yuzu/applets/software_keyboard.h"
14#include "yuzu/main.h"
15
16QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
17 Core::Frontend::SoftwareKeyboardParameters parameters)
18 : parameters(std::move(parameters)) {}
19
20QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
21 if (input.size() > parameters.max_length)
22 return Invalid;
23 if (parameters.disable_space && input.contains(' '))
24 return Invalid;
25 if (parameters.disable_address && input.contains('@'))
26 return Invalid;
27 if (parameters.disable_percent && input.contains('%'))
28 return Invalid;
29 if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
30 return Invalid;
31 if (parameters.disable_number &&
32 std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
33 return Invalid;
34 }
35
36 if (parameters.disable_download_code &&
37 std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
38 return Invalid;
39 }
40
41 return Acceptable;
42}
43
44QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
45 QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
46 : QDialog(parent), parameters(std::move(parameters_)) {
47 layout = new QVBoxLayout;
48
49 header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
50 header_label->setFont({header_label->font().family(), 11, QFont::Bold});
51 if (header_label->text().isEmpty())
52 header_label->setText(tr("Enter text:"));
53
54 sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
55 sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
56 sub_label->font().weight(), true});
57 sub_label->setHidden(parameters.sub_text.empty());
58
59 guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
60 guide_label->setHidden(parameters.guide_text.empty());
61
62 length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
63 length_label->setAlignment(Qt::AlignRight);
64 length_label->setFont({length_label->font().family(), 8});
65
66 line_edit = new QLineEdit;
67 line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
68 line_edit->setMaxLength(static_cast<int>(parameters.max_length));
69 line_edit->setText(QString::fromStdU16String(parameters.initial_text));
70 line_edit->setCursorPosition(
71 parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
72 line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
73
74 connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
75 length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
76 });
77
78 buttons = new QDialogButtonBox;
79 buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
80 buttons->addButton(parameters.submit_text.empty()
81 ? tr("OK")
82 : QString::fromStdU16String(parameters.submit_text),
83 QDialogButtonBox::AcceptRole);
84
85 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
86 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
87 layout->addWidget(header_label);
88 layout->addWidget(sub_label);
89 layout->addWidget(guide_label);
90 layout->addWidget(length_label);
91 layout->addWidget(line_edit);
92 layout->addWidget(buttons);
93 setLayout(layout);
94 setWindowTitle(tr("Software Keyboard"));
95}
96
97QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
98
99void QtSoftwareKeyboardDialog::accept() {
100 ok = true;
101 text = line_edit->text().toStdU16String();
102 QDialog::accept();
103}
104
105void QtSoftwareKeyboardDialog::reject() {
106 ok = false;
107 text.clear();
108 QDialog::reject();
109}
110
111std::u16string QtSoftwareKeyboardDialog::GetText() const {
112 return text;
113}
114
115bool QtSoftwareKeyboardDialog::GetStatus() const {
116 return ok;
117}
118
119QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
120 connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
121 &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
122 connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
123 &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
124 connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
125 &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
126}
127
128QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
129
130void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
131 Core::Frontend::SoftwareKeyboardParameters parameters) const {
132 text_output = std::move(out);
133 emit MainWindowGetText(parameters);
134}
135
136void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
137 std::function<void()> finished_check) const {
138 this->finished_check = std::move(finished_check);
139 emit MainWindowTextCheckDialog(error_message);
140}
141
142void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
143 // Acquire the HLE mutex
144 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
145 text_output(text);
146}
147
148void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
149 // Acquire the HLE mutex
150 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
151 finished_check();
152}
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h
new file mode 100644
index 000000000..c63720ba4
--- /dev/null
+++ b/src/yuzu/applets/software_keyboard.h
@@ -0,0 +1,79 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8#include <QValidator>
9#include "common/assert.h"
10#include "core/frontend/applets/software_keyboard.h"
11
12class GMainWindow;
13class QDialogButtonBox;
14class QLabel;
15class QLineEdit;
16class QVBoxLayout;
17class QtSoftwareKeyboard;
18
19class QtSoftwareKeyboardValidator final : public QValidator {
20public:
21 explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
22 State validate(QString& input, int& pos) const override;
23
24private:
25 Core::Frontend::SoftwareKeyboardParameters parameters;
26};
27
28class QtSoftwareKeyboardDialog final : public QDialog {
29 Q_OBJECT
30
31public:
32 QtSoftwareKeyboardDialog(QWidget* parent,
33 Core::Frontend::SoftwareKeyboardParameters parameters);
34 ~QtSoftwareKeyboardDialog() override;
35
36 void accept() override;
37 void reject() override;
38
39 std::u16string GetText() const;
40 bool GetStatus() const;
41
42private:
43 bool ok = false;
44 std::u16string text;
45
46 QDialogButtonBox* buttons;
47 QLabel* header_label;
48 QLabel* sub_label;
49 QLabel* guide_label;
50 QLabel* length_label;
51 QLineEdit* line_edit;
52 QVBoxLayout* layout;
53
54 Core::Frontend::SoftwareKeyboardParameters parameters;
55};
56
57class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
58 Q_OBJECT
59
60public:
61 explicit QtSoftwareKeyboard(GMainWindow& parent);
62 ~QtSoftwareKeyboard() override;
63
64 void RequestText(std::function<void(std::optional<std::u16string>)> out,
65 Core::Frontend::SoftwareKeyboardParameters parameters) const override;
66 void SendTextCheckDialog(std::u16string error_message,
67 std::function<void()> finished_check) const override;
68
69signals:
70 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
71 void MainWindowTextCheckDialog(std::u16string error_message) const;
72
73private:
74 void MainWindowFinishedText(std::optional<std::u16string> text);
75 void MainWindowFinishedCheckDialog();
76
77 mutable std::function<void(std::optional<std::u16string>)> text_output;
78 mutable std::function<void()> finished_check;
79};
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 39eef8858..384e17921 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -310,7 +310,7 @@ void GRenderWindow::InitRenderTarget() {
310 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 310 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
311 // WA_DontShowOnScreen, WA_DeleteOnClose 311 // WA_DontShowOnScreen, WA_DeleteOnClose
312 QGLFormat fmt; 312 QGLFormat fmt;
313 fmt.setVersion(3, 3); 313 fmt.setVersion(4, 3);
314 fmt.setProfile(QGLFormat::CoreProfile); 314 fmt.setProfile(QGLFormat::CoreProfile);
315 fmt.setSwapInterval(false); 315 fmt.setSwapInterval(false);
316 316
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d4fd60a73..83ebbd1fe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 7#include "core/hle/service/acc/profile_manager.h"
8#include "core/hle/service/hid/controllers/npad.h"
8#include "input_common/main.h" 9#include "input_common/main.h"
9#include "yuzu/configuration/config.h" 10#include "yuzu/configuration/config.h"
10#include "yuzu/ui_settings.h" 11#include "yuzu/ui_settings.h"
@@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
47 }, 48 },
48}}; 49}};
49 50
50void Config::ReadValues() { 51const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
51 qt_config->beginGroup("Controls"); 52 {
53 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
54};
55
56const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
57 0,
58 0,
59 0,
60 0,
61 Qt::Key_A,
62 Qt::Key_B,
63 Qt::Key_C,
64 Qt::Key_D,
65 Qt::Key_E,
66 Qt::Key_F,
67 Qt::Key_G,
68 Qt::Key_H,
69 Qt::Key_I,
70 Qt::Key_J,
71 Qt::Key_K,
72 Qt::Key_L,
73 Qt::Key_M,
74 Qt::Key_N,
75 Qt::Key_O,
76 Qt::Key_P,
77 Qt::Key_Q,
78 Qt::Key_R,
79 Qt::Key_S,
80 Qt::Key_T,
81 Qt::Key_U,
82 Qt::Key_V,
83 Qt::Key_W,
84 Qt::Key_X,
85 Qt::Key_Y,
86 Qt::Key_Z,
87 Qt::Key_1,
88 Qt::Key_2,
89 Qt::Key_3,
90 Qt::Key_4,
91 Qt::Key_5,
92 Qt::Key_6,
93 Qt::Key_7,
94 Qt::Key_8,
95 Qt::Key_9,
96 Qt::Key_0,
97 Qt::Key_Enter,
98 Qt::Key_Escape,
99 Qt::Key_Backspace,
100 Qt::Key_Tab,
101 Qt::Key_Space,
102 Qt::Key_Minus,
103 Qt::Key_Equal,
104 Qt::Key_BracketLeft,
105 Qt::Key_BracketRight,
106 Qt::Key_Backslash,
107 Qt::Key_Dead_Tilde,
108 Qt::Key_Semicolon,
109 Qt::Key_Apostrophe,
110 Qt::Key_Dead_Grave,
111 Qt::Key_Comma,
112 Qt::Key_Period,
113 Qt::Key_Slash,
114 Qt::Key_CapsLock,
115
116 Qt::Key_F1,
117 Qt::Key_F2,
118 Qt::Key_F3,
119 Qt::Key_F4,
120 Qt::Key_F5,
121 Qt::Key_F6,
122 Qt::Key_F7,
123 Qt::Key_F8,
124 Qt::Key_F9,
125 Qt::Key_F10,
126 Qt::Key_F11,
127 Qt::Key_F12,
128
129 Qt::Key_SysReq,
130 Qt::Key_ScrollLock,
131 Qt::Key_Pause,
132 Qt::Key_Insert,
133 Qt::Key_Home,
134 Qt::Key_PageUp,
135 Qt::Key_Delete,
136 Qt::Key_End,
137 Qt::Key_PageDown,
138 Qt::Key_Right,
139 Qt::Key_Left,
140 Qt::Key_Down,
141 Qt::Key_Up,
142
143 Qt::Key_NumLock,
144 Qt::Key_Slash,
145 Qt::Key_Asterisk,
146 Qt::Key_Minus,
147 Qt::Key_Plus,
148 Qt::Key_Enter,
149 Qt::Key_1,
150 Qt::Key_2,
151 Qt::Key_3,
152 Qt::Key_4,
153 Qt::Key_5,
154 Qt::Key_6,
155 Qt::Key_7,
156 Qt::Key_8,
157 Qt::Key_9,
158 Qt::Key_0,
159 Qt::Key_Period,
160
161 0,
162 0,
163 Qt::Key_PowerOff,
164 Qt::Key_Equal,
165
166 Qt::Key_F13,
167 Qt::Key_F14,
168 Qt::Key_F15,
169 Qt::Key_F16,
170 Qt::Key_F17,
171 Qt::Key_F18,
172 Qt::Key_F19,
173 Qt::Key_F20,
174 Qt::Key_F21,
175 Qt::Key_F22,
176 Qt::Key_F23,
177 Qt::Key_F24,
178
179 Qt::Key_Open,
180 Qt::Key_Help,
181 Qt::Key_Menu,
182 0,
183 Qt::Key_Stop,
184 Qt::Key_AudioRepeat,
185 Qt::Key_Undo,
186 Qt::Key_Cut,
187 Qt::Key_Copy,
188 Qt::Key_Paste,
189 Qt::Key_Find,
190 Qt::Key_VolumeMute,
191 Qt::Key_VolumeUp,
192 Qt::Key_VolumeDown,
193 Qt::Key_CapsLock,
194 Qt::Key_NumLock,
195 Qt::Key_ScrollLock,
196 Qt::Key_Comma,
197
198 Qt::Key_ParenLeft,
199 Qt::Key_ParenRight,
200};
201
202const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
203 Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
204 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
205};
206
207void Config::ReadPlayerValues() {
208 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
209 Settings::values.players[p].connected =
210 qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
211
212 Settings::values.players[p].type = static_cast<Settings::ControllerType>(
213 qt_config
214 ->value(QString("player_%1_type").arg(p),
215 static_cast<u8>(Settings::ControllerType::DualJoycon))
216 .toUInt());
217
218 Settings::values.players[p].body_color_left =
219 qt_config
220 ->value(QString("player_%1_body_color_left").arg(p),
221 Settings::JOYCON_BODY_NEON_BLUE)
222 .toUInt();
223 Settings::values.players[p].body_color_right =
224 qt_config
225 ->value(QString("player_%1_body_color_right").arg(p),
226 Settings::JOYCON_BODY_NEON_RED)
227 .toUInt();
228 Settings::values.players[p].button_color_left =
229 qt_config
230 ->value(QString("player_%1_button_color_left").arg(p),
231 Settings::JOYCON_BUTTONS_NEON_BLUE)
232 .toUInt();
233 Settings::values.players[p].button_color_right =
234 qt_config
235 ->value(QString("player_%1_button_color_right").arg(p),
236 Settings::JOYCON_BUTTONS_NEON_RED)
237 .toUInt();
238
239 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
240 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
241 Settings::values.players[p].buttons[i] =
242 qt_config
243 ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
244 QString::fromStdString(default_param))
245 .toString()
246 .toStdString();
247 if (Settings::values.players[p].buttons[i].empty())
248 Settings::values.players[p].buttons[i] = default_param;
249 }
250
251 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
252 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
253 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
254 default_analogs[i][3], default_analogs[i][4], 0.5f);
255 Settings::values.players[p].analogs[i] =
256 qt_config
257 ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
258 QString::fromStdString(default_param))
259 .toString()
260 .toStdString();
261 if (Settings::values.players[p].analogs[i].empty())
262 Settings::values.players[p].analogs[i] = default_param;
263 }
264 }
265
266 std::stable_partition(
267 Settings::values.players.begin(),
268 Settings::values.players.begin() +
269 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
270 [](const auto& player) { return player.connected; });
271}
272
273void Config::ReadDebugValues() {
274 Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
52 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 275 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
53 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 276 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
54 Settings::values.buttons[i] = 277 Settings::values.debug_pad_buttons[i] =
55 qt_config 278 qt_config
56 ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) 279 ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i],
280 QString::fromStdString(default_param))
57 .toString() 281 .toString()
58 .toStdString(); 282 .toStdString();
59 if (Settings::values.buttons[i].empty()) 283 if (Settings::values.debug_pad_buttons[i].empty())
60 Settings::values.buttons[i] = default_param; 284 Settings::values.debug_pad_buttons[i] = default_param;
61 } 285 }
62 286
63 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
64 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
65 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
66 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_analogs[i][4], 0.5f);
67 Settings::values.analogs[i] = 291 Settings::values.debug_pad_analogs[i] =
292 qt_config
293 ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i],
294 QString::fromStdString(default_param))
295 .toString()
296 .toStdString();
297 if (Settings::values.debug_pad_analogs[i].empty())
298 Settings::values.debug_pad_analogs[i] = default_param;
299 }
300}
301
302void Config::ReadKeyboardValues() {
303 Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
304
305 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
306 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
307 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
308 Settings::values.keyboard_keys.begin() +
309 Settings::NativeKeyboard::LeftControlKey,
310 InputCommon::GenerateKeyboardParam);
311 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
312 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
313}
314
315void Config::ReadMouseValues() {
316 Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
317
318 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
319 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
320 Settings::values.mouse_buttons[i] =
68 qt_config 321 qt_config
69 ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) 322 ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i],
323 QString::fromStdString(default_param))
70 .toString() 324 .toString()
71 .toStdString(); 325 .toStdString();
72 if (Settings::values.analogs[i].empty()) 326 if (Settings::values.mouse_buttons[i].empty())
73 Settings::values.analogs[i] = default_param; 327 Settings::values.mouse_buttons[i] = default_param;
74 } 328 }
329}
330
331void Config::ReadTouchscreenValues() {
332 Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
333 Settings::values.touchscreen.device =
334 qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
335
336 Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
337 Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
338 Settings::values.touchscreen.diameter_x =
339 qt_config->value("touchscreen_diameter_x", 15).toUInt();
340 Settings::values.touchscreen.diameter_y =
341 qt_config->value("touchscreen_diameter_y", 15).toUInt();
342 qt_config->endGroup();
343}
344
345void Config::ReadValues() {
346 qt_config->beginGroup("Controls");
347
348 ReadPlayerValues();
349 ReadDebugValues();
350 ReadKeyboardValues();
351 ReadMouseValues();
352 ReadTouchscreenValues();
75 353
76 Settings::values.motion_device = 354 Settings::values.motion_device =
77 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 355 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
78 .toString() 356 .toString()
79 .toStdString(); 357 .toStdString();
80 Settings::values.touch_device =
81 qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
82
83 qt_config->endGroup();
84 358
85 qt_config->beginGroup("Core"); 359 qt_config->beginGroup("Core");
86 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 360 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
@@ -126,6 +400,11 @@ void Config::ReadValues() {
126 .toStdString()); 400 .toStdString());
127 qt_config->endGroup(); 401 qt_config->endGroup();
128 402
403 qt_config->beginGroup("Core");
404 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
405 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
406 qt_config->endGroup();
407
129 qt_config->beginGroup("System"); 408 qt_config->beginGroup("System");
130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 409 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); 410 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
@@ -134,6 +413,14 @@ void Config::ReadValues() {
134 Service::Account::MAX_USERS - 1); 413 Service::Account::MAX_USERS - 1);
135 414
136 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 415 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
416
417 const auto enabled = qt_config->value("rng_seed_enabled", false).toBool();
418 if (enabled) {
419 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong();
420 } else {
421 Settings::values.rng_seed = std::nullopt;
422 }
423
137 qt_config->endGroup(); 424 qt_config->endGroup();
138 425
139 qt_config->beginGroup("Miscellaneous"); 426 qt_config->beginGroup("Miscellaneous");
@@ -145,6 +432,8 @@ void Config::ReadValues() {
145 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 432 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
146 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 433 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
147 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); 434 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
435 Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool();
436 Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
148 qt_config->endGroup(); 437 qt_config->endGroup();
149 438
150 qt_config->beginGroup("WebService"); 439 qt_config->beginGroup("WebService");
@@ -162,6 +451,7 @@ void Config::ReadValues() {
162 451
163 qt_config->beginGroup("UIGameList"); 452 qt_config->beginGroup("UIGameList");
164 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 453 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
454 UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
165 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); 455 UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
166 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); 456 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
167 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); 457 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
@@ -220,18 +510,81 @@ void Config::ReadValues() {
220 qt_config->endGroup(); 510 qt_config->endGroup();
221} 511}
222 512
223void Config::SaveValues() { 513void Config::SavePlayerValues() {
224 qt_config->beginGroup("Controls"); 514 for (int p = 0; p < Settings::values.players.size(); ++p) {
515 qt_config->setValue(QString("player_%1_connected").arg(p),
516 Settings::values.players[p].connected);
517 qt_config->setValue(QString("player_%1_type").arg(p),
518 static_cast<u8>(Settings::values.players[p].type));
519
520 qt_config->setValue(QString("player_%1_body_color_left").arg(p),
521 Settings::values.players[p].body_color_left);
522 qt_config->setValue(QString("player_%1_body_color_right").arg(p),
523 Settings::values.players[p].body_color_right);
524 qt_config->setValue(QString("player_%1_button_color_left").arg(p),
525 Settings::values.players[p].button_color_left);
526 qt_config->setValue(QString("player_%1_button_color_right").arg(p),
527 Settings::values.players[p].button_color_right);
528
529 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
530 qt_config->setValue(QString("player_%1_").arg(p) +
531 QString::fromStdString(Settings::NativeButton::mapping[i]),
532 QString::fromStdString(Settings::values.players[p].buttons[i]));
533 }
534 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
535 qt_config->setValue(QString("player_%1_").arg(p) +
536 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
537 QString::fromStdString(Settings::values.players[p].analogs[i]));
538 }
539 }
540}
541
542void Config::SaveDebugValues() {
543 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
225 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 544 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
226 qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]), 545 qt_config->setValue(QString("debug_pad_") +
227 QString::fromStdString(Settings::values.buttons[i])); 546 QString::fromStdString(Settings::NativeButton::mapping[i]),
547 QString::fromStdString(Settings::values.debug_pad_buttons[i]));
228 } 548 }
229 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 549 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
230 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), 550 qt_config->setValue(QString("debug_pad_") +
231 QString::fromStdString(Settings::values.analogs[i])); 551 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
552 QString::fromStdString(Settings::values.debug_pad_analogs[i]));
553 }
554}
555
556void Config::SaveMouseValues() {
557 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
558
559 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
560 qt_config->setValue(QString("mouse_") +
561 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
562 QString::fromStdString(Settings::values.mouse_buttons[i]));
232 } 563 }
564}
565
566void Config::SaveTouchscreenValues() {
567 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
568 qt_config->setValue("touchscreen_device",
569 QString::fromStdString(Settings::values.touchscreen.device));
570
571 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
572 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
573 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
574 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
575}
576
577void Config::SaveValues() {
578 qt_config->beginGroup("Controls");
579
580 SavePlayerValues();
581 SaveDebugValues();
582 SaveMouseValues();
583 SaveTouchscreenValues();
584
233 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 585 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
234 qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); 586 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
587
235 qt_config->endGroup(); 588 qt_config->endGroup();
236 589
237 qt_config->beginGroup("Core"); 590 qt_config->beginGroup("Core");
@@ -270,8 +623,11 @@ void Config::SaveValues() {
270 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 623 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
271 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 624 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
272 qt_config->setValue("current_user", Settings::values.current_user); 625 qt_config->setValue("current_user", Settings::values.current_user);
273
274 qt_config->setValue("language_index", Settings::values.language_index); 626 qt_config->setValue("language_index", Settings::values.language_index);
627
628 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
629 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0));
630
275 qt_config->endGroup(); 631 qt_config->endGroup();
276 632
277 qt_config->beginGroup("Miscellaneous"); 633 qt_config->beginGroup("Miscellaneous");
@@ -283,6 +639,8 @@ void Config::SaveValues() {
283 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 639 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
284 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 640 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
285 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); 641 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
642 qt_config->setValue("dump_exefs", Settings::values.dump_exefs);
643 qt_config->setValue("dump_nso", Settings::values.dump_nso);
286 qt_config->endGroup(); 644 qt_config->endGroup();
287 645
288 qt_config->beginGroup("WebService"); 646 qt_config->beginGroup("WebService");
@@ -298,6 +656,7 @@ void Config::SaveValues() {
298 656
299 qt_config->beginGroup("UIGameList"); 657 qt_config->beginGroup("UIGameList");
300 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 658 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
659 qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
301 qt_config->setValue("icon_size", UISettings::values.icon_size); 660 qt_config->setValue("icon_size", UISettings::values.icon_size);
302 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); 661 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
303 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); 662 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 9c99c1b75..a1c27bbf9 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -22,10 +22,24 @@ public:
22 22
23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
26 default_mouse_buttons;
27 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
25 29
26private: 30private:
27 void ReadValues(); 31 void ReadValues();
32 void ReadPlayerValues();
33 void ReadDebugValues();
34 void ReadKeyboardValues();
35 void ReadMouseValues();
36 void ReadTouchscreenValues();
37
28 void SaveValues(); 38 void SaveValues();
39 void SavePlayerValues();
40 void SaveDebugValues();
41 void SaveMouseValues();
42 void SaveTouchscreenValues();
29 43
30 std::unique_ptr<QSettings> qt_config; 44 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc; 45 std::string qt_config_loc;
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 9e765fc93..aa7de7b54 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,6 +34,8 @@ void ConfigureDebug::setConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
37} 39}
38 40
39void ConfigureDebug::applyConfiguration() { 41void ConfigureDebug::applyConfiguration() {
@@ -42,6 +44,8 @@ void ConfigureDebug::applyConfiguration() {
42 UISettings::values.show_console = ui->toggle_console->isChecked(); 44 UISettings::values.show_console = ui->toggle_console->isChecked();
43 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 45 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
44 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
47 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
48 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
45 Debugger::ToggleConsole(); 49 Debugger::ToggleConsole();
46 Log::Filter filter; 50 Log::Filter filter;
47 filter.ParseFilterString(Settings::values.log_filter); 51 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index ff4987604..758a92335 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>300</height> 10 <height>357</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -130,6 +130,35 @@
130 </widget> 130 </widget>
131 </item> 131 </item>
132 <item> 132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Dump</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_4">
138 <item>
139 <widget class="QCheckBox" name="dump_decompressed_nso">
140 <property name="whatsThis">
141 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
142 </property>
143 <property name="text">
144 <string>Dump Decompressed NSOs</string>
145 </property>
146 </widget>
147 </item>
148 <item>
149 <widget class="QCheckBox" name="dump_exefs">
150 <property name="whatsThis">
151 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
152 </property>
153 <property name="text">
154 <string>Dump ExeFS</string>
155 </property>
156 </widget>
157 </item>
158 </layout>
159 </widget>
160 </item>
161 <item>
133 <spacer name="verticalSpacer"> 162 <spacer name="verticalSpacer">
134 <property name="orientation"> 163 <property name="orientation">
135 <enum>Qt::Vertical</enum> 164 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
index 8743ce982..ae8cac243 100644
--- a/src/yuzu/configuration/configure_gamelist.cpp
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -36,20 +36,36 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
36 InitializeRowComboBoxes(); 36 InitializeRowComboBoxes();
37 37
38 this->setConfiguration(); 38 this->setConfiguration();
39
40 // Force game list reload if any of the relevant settings are changed.
41 connect(ui->show_unknown, &QCheckBox::stateChanged, this,
42 &ConfigureGameList::RequestGameListUpdate);
43 connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
44 &ConfigureGameList::RequestGameListUpdate);
45 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
46 &ConfigureGameList::RequestGameListUpdate);
47 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
48 &ConfigureGameList::RequestGameListUpdate);
39} 49}
40 50
41ConfigureGameList::~ConfigureGameList() = default; 51ConfigureGameList::~ConfigureGameList() = default;
42 52
43void ConfigureGameList::applyConfiguration() { 53void ConfigureGameList::applyConfiguration() {
44 UISettings::values.show_unknown = ui->show_unknown->isChecked(); 54 UISettings::values.show_unknown = ui->show_unknown->isChecked();
55 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
45 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 56 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
46 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 57 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
47 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 58 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
48 Settings::Apply(); 59 Settings::Apply();
49} 60}
50 61
62void ConfigureGameList::RequestGameListUpdate() {
63 UISettings::values.is_game_list_reload_pending.exchange(true);
64}
65
51void ConfigureGameList::setConfiguration() { 66void ConfigureGameList::setConfiguration() {
52 ui->show_unknown->setChecked(UISettings::values.show_unknown); 67 ui->show_unknown->setChecked(UISettings::values.show_unknown);
68 ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
53 ui->icon_size_combobox->setCurrentIndex( 69 ui->icon_size_combobox->setCurrentIndex(
54 ui->icon_size_combobox->findData(UISettings::values.icon_size)); 70 ui->icon_size_combobox->findData(UISettings::values.icon_size));
55 ui->row_1_text_combobox->setCurrentIndex( 71 ui->row_1_text_combobox->setCurrentIndex(
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
index ff7406c60..bbf7e25f1 100644
--- a/src/yuzu/configuration/configure_gamelist.h
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -21,6 +21,8 @@ public:
21 void applyConfiguration(); 21 void applyConfiguration();
22 22
23private: 23private:
24 void RequestGameListUpdate();
25
24 void setConfiguration(); 26 void setConfiguration();
25 27
26 void changeEvent(QEvent*) override; 28 void changeEvent(QEvent*) override;
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_gamelist.ui
index 7471fdb60..7a69377e7 100644
--- a/src/yuzu/configuration/configure_gamelist.ui
+++ b/src/yuzu/configuration/configure_gamelist.ui
@@ -1,126 +1,133 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureGameList</class> 3 <class>ConfigureGameList</class>
4 <widget class="QWidget" name="ConfigureGeneral"> 4 <widget class="QWidget" name="ConfigureGameList">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>377</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
14 <string>Form</string> 14 <string>Form</string>
15 </property> 15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout"> 16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout"> 18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
19 <item> 27 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox"> 28 <widget class="QCheckBox" name="show_unknown">
21 <property name="title"> 29 <property name="text">
22 <string>General</string> 30 <string>Show files with type 'Unknown'</string>
23 </property> 31 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> 32 </widget>
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item>
28 <widget class="QCheckBox" name="show_unknown">
29 <property name="text">
30 <string>Show files with type 'Unknown'</string>
31 </property>
32 </widget>
33 </item>
34 </layout>
35 </item>
36 </layout>
37 </widget>
38 </item> 33 </item>
39 <item> 34 <item>
40 <widget class="QGroupBox" name="IconSizeGroupBox"> 35 <widget class="QCheckBox" name="show_add_ons">
41 <property name="title"> 36 <property name="text">
42 <string>Icon Size</string> 37 <string>Show Add-Ons Column</string>
43 </property> 38 </property>
44 <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> 39 </widget>
45 <item>
46 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
47 <item>
48 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
49 <item>
50 <widget class="QLabel" name="icon_size_label">
51 <property name="text">
52 <string>Icon Size:</string>
53 </property>
54 </widget>
55 </item>
56 <item>
57 <widget class="QComboBox" name="icon_size_combobox"/>
58 </item>
59 </layout>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item> 40 </item>
41 </layout>
42 </item>
43 </layout>
44 </widget>
45 </item>
46 <item>
47 <widget class="QGroupBox" name="IconSizeGroupBox">
48 <property name="title">
49 <string>Icon Size</string>
50 </property>
51 <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
52 <item>
53 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
66 <item> 54 <item>
67 <widget class="QGroupBox" name="RowGroupBox"> 55 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
68 <property name="title"> 56 <item>
69 <string>Row Text</string> 57 <widget class="QLabel" name="icon_size_label">
58 <property name="text">
59 <string>Icon Size:</string>
70 </property> 60 </property>
71 <layout class="QHBoxLayout" name="RowHorizontalLayout"> 61 </widget>
72 <item> 62 </item>
73 <layout class="QVBoxLayout" name="RowVerticalLayout"> 63 <item>
74 <item> 64 <widget class="QComboBox" name="icon_size_combobox"/>
75 <layout class="QHBoxLayout" name="row_1_qhbox_layout"> 65 </item>
76 <item> 66 </layout>
77 <widget class="QLabel" name="row_1_label">
78 <property name="text">
79 <string>Row 1 Text:</string>
80 </property>
81 </widget>
82 </item>
83 <item>
84 <widget class="QComboBox" name="row_1_text_combobox"/>
85 </item>
86 </layout>
87 </item>
88 <item>
89 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
90 <item>
91 <widget class="QLabel" name="row_2_label">
92 <property name="text">
93 <string>Row 2 Text:</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QComboBox" name="row_2_text_combobox"/>
99 </item>
100 </layout>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </widget>
106 </item> 67 </item>
68 </layout>
69 </item>
70 </layout>
71 </widget>
72 </item>
73 <item>
74 <widget class="QGroupBox" name="RowGroupBox">
75 <property name="title">
76 <string>Row Text</string>
77 </property>
78 <layout class="QHBoxLayout" name="RowHorizontalLayout">
79 <item>
80 <layout class="QVBoxLayout" name="RowVerticalLayout">
107 <item> 81 <item>
108 <spacer name="verticalSpacer"> 82 <layout class="QHBoxLayout" name="row_1_qhbox_layout">
109 <property name="orientation"> 83 <item>
110 <enum>Qt::Vertical</enum> 84 <widget class="QLabel" name="row_1_label">
85 <property name="text">
86 <string>Row 1 Text:</string>
111 </property> 87 </property>
112 <property name="sizeHint" stdset="0"> 88 </widget>
113 <size> 89 </item>
114 <width>20</width> 90 <item>
115 <height>40</height> 91 <widget class="QComboBox" name="row_1_text_combobox"/>
116 </size> 92 </item>
93 </layout>
94 </item>
95 <item>
96 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
97 <item>
98 <widget class="QLabel" name="row_2_label">
99 <property name="text">
100 <string>Row 2 Text:</string>
117 </property> 101 </property>
118 </spacer> 102 </widget>
103 </item>
104 <item>
105 <widget class="QComboBox" name="row_2_text_combobox"/>
106 </item>
107 </layout>
119 </item> 108 </item>
120 </layout> 109 </layout>
121 </item> 110 </item>
111 </layout>
112 </widget>
113 </item>
114 <item>
115 <spacer name="verticalSpacer">
116 <property name="orientation">
117 <enum>Qt::Vertical</enum>
118 </property>
119 <property name="sizeHint" stdset="0">
120 <size>
121 <width>20</width>
122 <height>40</height>
123 </size>
124 </property>
125 </spacer>
126 </item>
122 </layout> 127 </layout>
123 </widget> 128 </item>
129 </layout>
130 </widget>
124 <resources/> 131 <resources/>
125 <connections/> 132 <connections/>
126</ui> 133</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 537d6e576..92a441308 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -19,8 +19,10 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
19 19
20 this->setConfiguration(); 20 this->setConfiguration();
21 21
22 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
23 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
24
22 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 25 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
23 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
24} 26}
25 27
26ConfigureGeneral::~ConfigureGeneral() = default; 28ConfigureGeneral::~ConfigureGeneral() = default;
@@ -30,7 +32,6 @@ void ConfigureGeneral::setConfiguration() {
30 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 32 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 33 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 34 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
34 ui->enable_nfc->setChecked(Settings::values.enable_nfc); 35 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
35} 36}
36 37
@@ -45,6 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
45 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 46 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
46 47
47 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 48 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
48 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
49 Settings::values.enable_nfc = ui->enable_nfc->isChecked(); 49 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
50} 50}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index b82fffde8..bf37446c6 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>407</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -72,13 +72,6 @@
72 <item> 72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item> 74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
81 <item>
82 <widget class="QCheckBox" name="enable_nfc"> 75 <widget class="QCheckBox" name="enable_nfc">
83 <property name="text"> 76 <property name="text">
84 <string>Enable NFC</string> 77 <string>Enable NFC</string>
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 91fcad994..e278cdd05 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -23,31 +23,31 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_2"> 24 <layout class="QVBoxLayout" name="verticalLayout_2">
25 <item> 25 <item>
26 <layout class="QHBoxLayout" name="horizontalLayout_2"> 26 <layout class="QHBoxLayout" name="horizontalLayout_2">
27 <item> 27 <item>
28 <widget class="QCheckBox" name="toggle_frame_limit"> 28 <widget class="QCheckBox" name="toggle_frame_limit">
29 <property name="text"> 29 <property name="text">
30 <string>Limit Speed Percent</string> 30 <string>Limit Speed Percent</string>
31 </property> 31 </property>
32 </widget> 32 </widget>
33 </item> 33 </item>
34 <item> 34 <item>
35 <widget class="QSpinBox" name="frame_limit"> 35 <widget class="QSpinBox" name="frame_limit">
36 <property name="suffix"> 36 <property name="suffix">
37 <string>%</string> 37 <string>%</string>
38 </property> 38 </property>
39 <property name="minimum"> 39 <property name="minimum">
40 <number>1</number> 40 <number>1</number>
41 </property> 41 </property>
42 <property name="maximum"> 42 <property name="maximum">
43 <number>9999</number> 43 <number>9999</number>
44 </property> 44 </property>
45 <property name="value"> 45 <property name="value">
46 <number>100</number> 46 <number>100</number>
47 </property> 47 </property>
48 </widget> 48 </widget>
49 </item> 49 </item>
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
@@ -61,7 +61,7 @@
61 <item> 61 <item>
62 <widget class="QLabel" name="label"> 62 <widget class="QLabel" name="label">
63 <property name="text"> 63 <property name="text">
64 <string>Internal Resolution:(Currently does nothing.)</string> 64 <string>Internal Resolution</string>
65 </property> 65 </property>
66 </widget> 66 </widget>
67 </item> 67 </item>
@@ -96,27 +96,27 @@
96 </item> 96 </item>
97 </layout> 97 </layout>
98 </item> 98 </item>
99 <item> 99 <item>
100 <layout class="QHBoxLayout" name="horizontalLayout_6"> 100 <layout class="QHBoxLayout" name="horizontalLayout_6">
101 <item> 101 <item>
102 <widget class="QLabel" name="bg_label"> 102 <widget class="QLabel" name="bg_label">
103 <property name="text"> 103 <property name="text">
104 <string>Background Color:</string> 104 <string>Background Color:</string>
105 </property> 105 </property>
106 </widget> 106 </widget>
107 </item> 107 </item>
108 <item> 108 <item>
109 <widget class="QPushButton" name="bg_button"> 109 <widget class="QPushButton" name="bg_button">
110 <property name="maximumSize"> 110 <property name="maximumSize">
111 <size> 111 <size>
112 <width>40</width> 112 <width>40</width>
113 <height>16777215</height> 113 <height>16777215</height>
114 </size> 114 </size>
115 </property> 115 </property>
116 </widget> 116 </widget>
117 </item> 117 </item>
118 </layout> 118 </layout>
119 </item> 119 </item>
120 </layout> 120 </layout>
121 </widget> 121 </widget>
122 </item> 122 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 42a7beac6..7ee572761 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -9,334 +9,202 @@
9#include <QMessageBox> 9#include <QMessageBox>
10#include <QTimer> 10#include <QTimer>
11#include "common/param_package.h" 11#include "common/param_package.h"
12#include "configuration/configure_touchscreen_advanced.h"
13#include "core/core.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applet_ae.h"
16#include "core/hle/service/am/applet_oe.h"
17#include "core/hle/service/hid/controllers/npad.h"
18#include "core/hle/service/sm/sm.h"
12#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h"
21#include "ui_configure_input_player.h"
22#include "ui_configure_mouse_advanced.h"
23#include "ui_configure_touchscreen_advanced.h"
13#include "yuzu/configuration/config.h" 24#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_input.h" 25#include "yuzu/configuration/configure_input.h"
26#include "yuzu/configuration/configure_input_player.h"
27#include "yuzu/configuration/configure_mouse_advanced.h"
15 28
16const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 29ConfigureInput::ConfigureInput(QWidget* parent)
17 ConfigureInput::analog_sub_buttons{{ 30 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
18 "up", 31 ui->setupUi(this);
19 "down",
20 "left",
21 "right",
22 "modifier",
23 }};
24
25static QString getKeyName(int key_code) {
26 switch (key_code) {
27 case Qt::Key_Shift:
28 return QObject::tr("Shift");
29 case Qt::Key_Control:
30 return QObject::tr("Ctrl");
31 case Qt::Key_Alt:
32 return QObject::tr("Alt");
33 case Qt::Key_Meta:
34 return "";
35 default:
36 return QKeySequence(key_code).toString();
37 }
38}
39 32
40static void SetAnalogButton(const Common::ParamPackage& input_param, 33 players_controller = {
41 Common::ParamPackage& analog_param, const std::string& button_name) { 34 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
42 if (analog_param.Get("engine", "") != "analog_from_button") { 35 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
43 analog_param = { 36 };
44 {"engine", "analog_from_button"},
45 {"modifier_scale", "0.5"},
46 };
47 }
48 analog_param.Set(button_name, input_param.Serialize());
49}
50 37
51static QString ButtonToText(const Common::ParamPackage& param) { 38 players_configure = {
52 if (!param.Has("engine")) { 39 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
53 return QObject::tr("[not set]"); 40 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
54 } else if (param.Get("engine", "") == "keyboard") { 41 };
55 return getKeyName(param.Get("code", 0));
56 } else if (param.Get("engine", "") == "sdl") {
57 if (param.Has("hat")) {
58 return QString(QObject::tr("Hat %1 %2"))
59 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
60 }
61 if (param.Has("axis")) {
62 return QString(QObject::tr("Axis %1%2"))
63 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
64 }
65 if (param.Has("button")) {
66 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
67 }
68 return QString();
69 } else {
70 return QObject::tr("[unknown]");
71 }
72};
73
74static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
75 if (!param.Has("engine")) {
76 return QObject::tr("[not set]");
77 } else if (param.Get("engine", "") == "analog_from_button") {
78 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
79 } else if (param.Get("engine", "") == "sdl") {
80 if (dir == "modifier") {
81 return QString(QObject::tr("[unused]"));
82 }
83 42
84 if (dir == "left" || dir == "right") { 43 for (auto* controller_box : players_controller) {
85 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); 44 controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
86 } else if (dir == "up" || dir == "down") { 45 "Single Left Joycon"});
87 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
88 }
89 return QString();
90 } else {
91 return QObject::tr("[unknown]");
92 } 46 }
93};
94 47
95ConfigureInput::ConfigureInput(QWidget* parent) 48 this->loadConfiguration();
96 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), 49 updateUIEnabled();
97 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
98 50
99 ui->setupUi(this); 51 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
100 setFocusPolicy(Qt::ClickFocus); 52 &ConfigureInput::restoreDefaults);
101
102 button_map = {
103 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
104 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
105 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
106 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
107 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
108 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
109 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
110 };
111 53
112 analog_map_buttons = {{ 54 for (auto* enabled : players_controller)
113 { 55 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
114 ui->buttonLStickUp, 56 &ConfigureInput::updateUIEnabled);
115 ui->buttonLStickDown, 57 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
116 ui->buttonLStickLeft, 58 connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
117 ui->buttonLStickRight, 59 &ConfigureInput::updateUIEnabled);
118 ui->buttonLStickMod, 60 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
119 }, 61 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
120 { 62 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
121 ui->buttonRStickUp, 63 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
122 ui->buttonRStickDown, 64 &ConfigureInput::updateUIEnabled);
123 ui->buttonRStickLeft,
124 ui->buttonRStickRight,
125 ui->buttonRStickMod,
126 },
127 }};
128
129 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
130
131 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
132 if (!button_map[button_id])
133 continue;
134 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
135 connect(button_map[button_id], &QPushButton::released, [=]() {
136 handleClick(
137 button_map[button_id],
138 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
139 InputCommon::Polling::DeviceType::Button);
140 });
141 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
142 [=](const QPoint& menu_location) {
143 QMenu context_menu;
144 context_menu.addAction(tr("Clear"), [&] {
145 buttons_param[button_id].Clear();
146 button_map[button_id]->setText(tr("[not set]"));
147 });
148 context_menu.addAction(tr("Restore Default"), [&] {
149 buttons_param[button_id] = Common::ParamPackage{
150 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
151 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
152 });
153 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
154 });
155 }
156 65
157 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 66 for (std::size_t i = 0; i < players_configure.size(); ++i) {
158 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 67 connect(players_configure[i], &QPushButton::pressed, this,
159 if (!analog_map_buttons[analog_id][sub_button_id]) 68 [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
160 continue;
161 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
162 Qt::CustomContextMenu);
163 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
164 handleClick(analog_map_buttons[analog_id][sub_button_id],
165 [=](const Common::ParamPackage& params) {
166 SetAnalogButton(params, analogs_param[analog_id],
167 analog_sub_buttons[sub_button_id]);
168 },
169 InputCommon::Polling::DeviceType::Button);
170 });
171 connect(analog_map_buttons[analog_id][sub_button_id],
172 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
173 QMenu context_menu;
174 context_menu.addAction(tr("Clear"), [&] {
175 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
176 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
177 });
178 context_menu.addAction(tr("Restore Default"), [&] {
179 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
180 Config::default_analogs[analog_id][sub_button_id])};
181 SetAnalogButton(params, analogs_param[analog_id],
182 analog_sub_buttons[sub_button_id]);
183 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
184 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
185 });
186 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
187 menu_location));
188 });
189 }
190 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
191 QMessageBox::information(this, tr("Information"),
192 tr("After pressing OK, first move your joystick horizontally, "
193 "and then vertically."));
194 handleClick(
195 analog_map_stick[analog_id],
196 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
197 InputCommon::Polling::DeviceType::Analog);
198 });
199 } 69 }
200 70
201 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); 71 connect(ui->handheld_configure, &QPushButton::pressed, this,
202 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); 72 [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
203
204 timeout_timer->setSingleShot(true);
205 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
206 73
207 connect(poll_timer.get(), &QTimer::timeout, [this]() { 74 connect(ui->debug_configure, &QPushButton::pressed, this,
208 Common::ParamPackage params; 75 [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
209 for (auto& poller : device_pollers) {
210 params = poller->GetNextInput();
211 if (params.Has("engine")) {
212 setPollingResult(params, false);
213 return;
214 }
215 }
216 });
217 76
218 this->loadConfiguration(); 77 connect(ui->mouse_advanced, &QPushButton::pressed, this,
78 [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
219 79
220 // TODO(wwylele): enable this when we actually emulate it 80 connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
221 ui->buttonHome->setEnabled(false); 81 [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
222} 82}
223 83
224void ConfigureInput::applyConfiguration() { 84template <typename Dialog, typename... Args>
225 std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), 85void ConfigureInput::CallConfigureDialog(Args&&... args) {
226 [](const Common::ParamPackage& param) { return param.Serialize(); }); 86 this->applyConfiguration();
227 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), 87 Dialog dialog(this, std::forward<Args>(args)...);
228 [](const Common::ParamPackage& param) { return param.Serialize(); });
229}
230 88
231void ConfigureInput::loadConfiguration() { 89 const auto res = dialog.exec();
232 std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(), 90 if (res == QDialog::Accepted) {
233 buttons_param.begin(), 91 dialog.applyConfiguration();
234 [](const std::string& str) { return Common::ParamPackage(str); }); 92 }
235 std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
236 analogs_param.begin(),
237 [](const std::string& str) { return Common::ParamPackage(str); });
238 updateButtonLabels();
239} 93}
240 94
241void ConfigureInput::restoreDefaults() { 95void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
242 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 96 if (last_state == new_state) {
243 buttons_param[button_id] = Common::ParamPackage{ 97 return;
244 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
245 } 98 }
246 99
247 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 100 Core::System& system{Core::System::GetInstance()};
248 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 101 if (!system.IsPoweredOn()) {
249 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 102 return;
250 Config::default_analogs[analog_id][sub_button_id])};
251 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
252 }
253 } 103 }
254 updateButtonLabels(); 104 Service::SM::ServiceManager& sm = system.ServiceManager();
255}
256 105
257void ConfigureInput::ClearAll() { 106 // Message queue is shared between these services, we just need to signal an operation
258 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 107 // change to one and it will handle both automatically
259 if (button_map[button_id] && button_map[button_id]->isEnabled()) 108 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
260 buttons_param[button_id].Clear(); 109 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
261 } 110 bool has_signalled = false;
262 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
263 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
264 if (analog_map_buttons[analog_id][sub_button_id] &&
265 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 }
268 }
269 updateButtonLabels();
270}
271 111
272void ConfigureInput::updateButtonLabels() { 112 if (applet_oe != nullptr) {
273 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 113 applet_oe->GetMessageQueue()->OperationModeChanged();
274 button_map[button]->setText(ButtonToText(buttons_param[button])); 114 has_signalled = true;
275 } 115 }
276 116
277 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 117 if (applet_ae != nullptr && !has_signalled) {
278 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 118 applet_ae->GetMessageQueue()->OperationModeChanged();
279 if (analog_map_buttons[analog_id][sub_button_id]) {
280 analog_map_buttons[analog_id][sub_button_id]->setText(
281 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
282 }
283 }
284 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
285 } 119 }
286} 120}
287 121
288void ConfigureInput::handleClick(QPushButton* button, 122void ConfigureInput::applyConfiguration() {
289 std::function<void(const Common::ParamPackage&)> new_input_setter, 123 for (std::size_t i = 0; i < players_controller.size(); ++i) {
290 InputCommon::Polling::DeviceType type) { 124 const auto controller_type_index = players_controller[i]->currentIndex();
291 button->setText(tr("[press key]"));
292 button->setFocus();
293
294 input_setter = new_input_setter;
295
296 device_pollers = InputCommon::Polling::GetPollers(type);
297 125
298 // Keyboard keys can only be used as button devices 126 Settings::values.players[i].connected = controller_type_index != 0;
299 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
300 127
301 for (auto& poller : device_pollers) { 128 if (controller_type_index > 0) {
302 poller->Start(); 129 Settings::values.players[i].type =
130 static_cast<Settings::ControllerType>(controller_type_index - 1);
131 } else {
132 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
133 }
303 } 134 }
304 135
305 grabKeyboard(); 136 const bool pre_docked_mode = Settings::values.use_docked_mode;
306 grabMouse(); 137 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
307 timeout_timer->start(5000); // Cancel after 5 seconds 138 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
308 poll_timer->start(200); // Check for new inputs every 200ms 139 Settings::values
140 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
141 .connected = ui->handheld_connected->isChecked();
142 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
143 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
144 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
145 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
309} 146}
310 147
311void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) { 148void ConfigureInput::updateUIEnabled() {
312 releaseKeyboard(); 149 bool hit_disabled = false;
313 releaseMouse(); 150 for (auto* player : players_controller) {
314 timeout_timer->stop(); 151 player->setDisabled(hit_disabled);
315 poll_timer->stop(); 152 if (hit_disabled)
316 for (auto& poller : device_pollers) { 153 player->setCurrentIndex(0);
317 poller->Stop(); 154 if (!hit_disabled && player->currentIndex() == 0)
155 hit_disabled = true;
318 } 156 }
319 157
320 if (!abort) { 158 for (std::size_t i = 0; i < players_controller.size(); ++i) {
321 (*input_setter)(params); 159 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
322 } 160 }
323 161
324 updateButtonLabels(); 162 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
325 input_setter = {}; 163 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
164 !ui->use_docked_mode->isChecked());
165 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
166 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
167 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
326} 168}
327 169
328void ConfigureInput::keyPressEvent(QKeyEvent* event) { 170void ConfigureInput::loadConfiguration() {
329 if (!input_setter || !event) 171 std::stable_partition(
330 return; 172 Settings::values.players.begin(),
173 Settings::values.players.begin() +
174 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
175 [](const auto& player) { return player.connected; });
176
177 for (std::size_t i = 0; i < players_controller.size(); ++i) {
178 const auto connected = Settings::values.players[i].connected;
179 players_controller[i]->setCurrentIndex(
180 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
181 }
182
183 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
184 ui->handheld_connected->setChecked(
185 Settings::values
186 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
187 .connected);
188 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
189 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
190 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
191 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
192
193 updateUIEnabled();
194}
331 195
332 if (event->key() != Qt::Key_Escape) { 196void ConfigureInput::restoreDefaults() {
333 if (want_keyboard_keys) { 197 players_controller[0]->setCurrentIndex(2);
334 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 198
335 false); 199 for (std::size_t i = 1; i < players_controller.size(); ++i) {
336 } else { 200 players_controller[i]->setCurrentIndex(0);
337 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
338 return;
339 }
340 } 201 }
341 setPollingResult({}, true); 202
203 ui->use_docked_mode->setCheckState(Qt::Unchecked);
204 ui->handheld_connected->setCheckState(Qt::Unchecked);
205 ui->mouse_enabled->setCheckState(Qt::Unchecked);
206 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
207 ui->debug_enabled->setCheckState(Qt::Unchecked);
208 ui->touchscreen_enabled->setCheckState(Qt::Checked);
209 updateUIEnabled();
342} 210}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 32c7183f9..29a8a03f8 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -18,6 +18,7 @@
18#include "core/settings.h" 18#include "core/settings.h"
19#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h" 20#include "ui_configure_input.h"
21#include "yuzu/configuration/config.h"
21 22
22class QPushButton; 23class QPushButton;
23class QString; 24class QString;
@@ -37,57 +38,20 @@ public:
37 void applyConfiguration(); 38 void applyConfiguration();
38 39
39private: 40private:
40 std::unique_ptr<Ui::ConfigureInput> ui; 41 void updateUIEnabled();
41
42 std::unique_ptr<QTimer> timeout_timer;
43 std::unique_ptr<QTimer> poll_timer;
44
45 /// This will be the the setting function when an input is awaiting configuration.
46 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
47
48 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
49 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
50
51 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
52
53 /// Each button input is represented by a QPushButton.
54 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
55 42
56 /// A group of five QPushButtons represent one analog input. The buttons each represent up, 43 template <typename Dialog, typename... Args>
57 /// down, left, right, and modifier, respectively. 44 void CallConfigureDialog(Args&&... args);
58 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
59 analog_map_buttons;
60 45
61 /// Analog inputs are also represented each with a single button, used to configure with an 46 void OnDockedModeChanged(bool last_state, bool new_state);
62 /// actual analog stick
63 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
64
65 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
66
67 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
68
69 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
70 /// keyboard events are ignored.
71 bool want_keyboard_keys = false;
72 47
73 /// Load configuration settings. 48 /// Load configuration settings.
74 void loadConfiguration(); 49 void loadConfiguration();
75 /// Restore all buttons to their default values. 50 /// Restore all buttons to their default values.
76 void restoreDefaults(); 51 void restoreDefaults();
77 /// Clear all input configuration
78 void ClearAll();
79 52
80 /// Update UI to reflect current configuration. 53 std::unique_ptr<Ui::ConfigureInput> ui;
81 void updateButtonLabels();
82
83 /// Called when the button was pressed.
84 void handleClick(QPushButton* button,
85 std::function<void(const Common::ParamPackage&)> new_input_setter,
86 InputCommon::Polling::DeviceType type);
87
88 /// Finish polling and configure input using the input_setter
89 void setPollingResult(const Common::ParamPackage& params, bool abort);
90 54
91 /// Handle key press events. 55 std::array<QComboBox*, 8> players_controller;
92 void keyPressEvent(QKeyEvent* event) override; 56 std::array<QPushButton*, 8> players_configure;
93}; 57};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 8a019a693..dae8277bc 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>343</width> 9 <width>473</width>
10 <height>677</height> 10 <height>685</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -15,740 +15,470 @@
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item> 17 <item>
18 <layout class="QGridLayout" name="buttons"> 18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item row="3" column="1"> 19 <item>
20 <widget class="QGroupBox" name="misc"> 20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title"> 21 <property name="title">
22 <string>Misc.</string> 22 <string>Players</string>
23 </property>
24 <property name="flat">
25 <bool>false</bool>
26 </property>
27 <property name="checkable">
28 <bool>false</bool>
29 </property> 23 </property>
30 <layout class="QGridLayout" name="gridLayout_6"> 24 <layout class="QGridLayout" name="gridLayout">
31 <item row="0" column="0"> 25 <item row="1" column="2">
32 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 26 <widget class="QComboBox" name="player1_combobox">
33 <item> 27 <property name="minimumSize">
34 <widget class="QLabel" name="labelMinus"> 28 <size>
35 <property name="text"> 29 <width>110</width>
36 <string>Minus:</string> 30 <height>0</height>
37 </property> 31 </size>
38 </widget> 32 </property>
39 </item> 33 </widget>
40 <item>
41 <widget class="QPushButton" name="buttonMinus">
42 <property name="text">
43 <string/>
44 </property>
45 </widget>
46 </item>
47 </layout>
48 </item> 34 </item>
49 <item row="0" column="1"> 35 <item row="1" column="3">
50 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> 36 <widget class="QPushButton" name="player1_configure">
51 <item> 37 <property name="text">
52 <widget class="QLabel" name="labelPlus"> 38 <string>Configure</string>
53 <property name="text"> 39 </property>
54 <string>Plus:</string> 40 </widget>
55 </property>
56 </widget>
57 </item>
58 <item>
59 <widget class="QPushButton" name="buttonPlus">
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item> 41 </item>
67 <item row="1" column="0"> 42 <item row="0" column="2">
68 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> 43 <widget class="QLabel" name="label">
69 <item> 44 <property name="text">
70 <widget class="QLabel" name="labelHome"> 45 <string>Controller Type</string>
71 <property name="text"> 46 </property>
72 <string>Home:</string> 47 <property name="alignment">
73 </property> 48 <set>Qt::AlignCenter</set>
74 </widget> 49 </property>
75 </item> 50 </widget>
76 <item>
77 <widget class="QPushButton" name="buttonHome">
78 <property name="text">
79 <string/>
80 </property>
81 </widget>
82 </item>
83 </layout>
84 </item> 51 </item>
85 <item row="1" column="1"> 52 <item row="2" column="2">
86 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> 53 <widget class="QComboBox" name="player2_combobox">
87 <item> 54 <property name="minimumSize">
88 <widget class="QLabel" name="labelScrCap"> 55 <size>
89 <property name="text"> 56 <width>110</width>
90 <string>Screen 57 <height>0</height>
91Capture:</string> 58 </size>
92 </property> 59 </property>
93 </widget> 60 </widget>
94 </item>
95 <item>
96 <widget class="QPushButton" name="buttonScreenshot">
97 <property name="text">
98 <string/>
99 </property>
100 </widget>
101 </item>
102 </layout>
103 </item> 61 </item>
104 <item row="2" column="1"> 62 <item row="3" column="2">
105 <spacer name="verticalSpacer"> 63 <widget class="QComboBox" name="player3_combobox">
64 <property name="minimumSize">
65 <size>
66 <width>110</width>
67 <height>0</height>
68 </size>
69 </property>
70 </widget>
71 </item>
72 <item row="4" column="2">
73 <widget class="QComboBox" name="player4_combobox">
74 <property name="minimumSize">
75 <size>
76 <width>110</width>
77 <height>0</height>
78 </size>
79 </property>
80 </widget>
81 </item>
82 <item row="5" column="2">
83 <widget class="QComboBox" name="player5_combobox">
84 <property name="minimumSize">
85 <size>
86 <width>110</width>
87 <height>0</height>
88 </size>
89 </property>
90 </widget>
91 </item>
92 <item row="6" column="2">
93 <widget class="QComboBox" name="player6_combobox">
94 <property name="minimumSize">
95 <size>
96 <width>110</width>
97 <height>0</height>
98 </size>
99 </property>
100 </widget>
101 </item>
102 <item row="7" column="2">
103 <widget class="QComboBox" name="player7_combobox">
104 <property name="minimumSize">
105 <size>
106 <width>110</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="8" column="2">
113 <widget class="QComboBox" name="player8_combobox">
114 <property name="minimumSize">
115 <size>
116 <width>110</width>
117 <height>0</height>
118 </size>
119 </property>
120 </widget>
121 </item>
122 <item row="2" column="3">
123 <widget class="QPushButton" name="player2_configure">
124 <property name="text">
125 <string>Configure</string>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="3">
130 <widget class="QPushButton" name="player3_configure">
131 <property name="text">
132 <string>Configure</string>
133 </property>
134 </widget>
135 </item>
136 <item row="4" column="3">
137 <widget class="QPushButton" name="player4_configure">
138 <property name="text">
139 <string>Configure</string>
140 </property>
141 </widget>
142 </item>
143 <item row="5" column="3">
144 <widget class="QPushButton" name="player5_configure">
145 <property name="text">
146 <string>Configure</string>
147 </property>
148 </widget>
149 </item>
150 <item row="6" column="3">
151 <widget class="QPushButton" name="player6_configure">
152 <property name="text">
153 <string>Configure</string>
154 </property>
155 </widget>
156 </item>
157 <item row="7" column="3">
158 <widget class="QPushButton" name="player7_configure">
159 <property name="text">
160 <string>Configure</string>
161 </property>
162 </widget>
163 </item>
164 <item row="8" column="3">
165 <widget class="QPushButton" name="player8_configure">
166 <property name="text">
167 <string>Configure</string>
168 </property>
169 </widget>
170 </item>
171 <item row="0" column="0">
172 <spacer name="horizontalSpacer">
106 <property name="orientation"> 173 <property name="orientation">
107 <enum>Qt::Vertical</enum> 174 <enum>Qt::Horizontal</enum>
108 </property> 175 </property>
109 <property name="sizeHint" stdset="0"> 176 <property name="sizeHint" stdset="0">
110 <size> 177 <size>
111 <width>20</width> 178 <width>40</width>
112 <height>40</height> 179 <height>20</height>
113 </size> 180 </size>
114 </property> 181 </property>
115 </spacer> 182 </spacer>
116 </item> 183 </item>
117 </layout> 184 <item row="0" column="4">
118 </widget> 185 <spacer name="horizontalSpacer_2">
119 </item> 186 <property name="orientation">
120 <item row="0" column="0"> 187 <enum>Qt::Horizontal</enum>
121 <widget class="QGroupBox" name="faceButtons"> 188 </property>
122 <property name="title"> 189 <property name="sizeHint" stdset="0">
123 <string>Face Buttons</string> 190 <size>
124 </property> 191 <width>40</width>
125 <property name="flat"> 192 <height>20</height>
126 <bool>false</bool> 193 </size>
127 </property> 194 </property>
128 <property name="checkable"> 195 </spacer>
129 <bool>false</bool>
130 </property>
131 <layout class="QGridLayout" name="gridLayout">
132 <item row="0" column="0">
133 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
134 <item>
135 <widget class="QLabel" name="labelA">
136 <property name="text">
137 <string>A:</string>
138 </property>
139 </widget>
140 </item>
141 <item>
142 <widget class="QPushButton" name="buttonA">
143 <property name="text">
144 <string/>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </item> 196 </item>
150 <item row="0" column="1"> 197 <item row="1" column="1">
151 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> 198 <widget class="QLabel" name="label_3">
152 <item> 199 <property name="minimumSize">
153 <widget class="QLabel" name="labelB"> 200 <size>
154 <property name="text"> 201 <width>55</width>
155 <string>B:</string> 202 <height>0</height>
156 </property> 203 </size>
157 </widget> 204 </property>
158 </item> 205 <property name="text">
159 <item> 206 <string>Player 1</string>
160 <widget class="QPushButton" name="buttonB"> 207 </property>
161 <property name="text"> 208 </widget>
162 <string/>
163 </property>
164 </widget>
165 </item>
166 </layout>
167 </item> 209 </item>
168 <item row="1" column="0"> 210 <item row="2" column="1">
169 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> 211 <widget class="QLabel" name="label_4">
170 <item> 212 <property name="text">
171 <widget class="QLabel" name="labelX"> 213 <string>Player 2</string>
172 <property name="text"> 214 </property>
173 <string>X:</string> 215 </widget>
174 </property>
175 </widget>
176 </item>
177 <item>
178 <widget class="QPushButton" name="buttonX">
179 <property name="text">
180 <string/>
181 </property>
182 </widget>
183 </item>
184 </layout>
185 </item> 216 </item>
186 <item row="1" column="1"> 217 <item row="3" column="1">
187 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> 218 <widget class="QLabel" name="label_5">
188 <item> 219 <property name="text">
189 <widget class="QLabel" name="labelY"> 220 <string>Player 3</string>
190 <property name="text"> 221 </property>
191 <string>Y:</string> 222 </widget>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonY">
197 <property name="text">
198 <string/>
199 </property>
200 </widget>
201 </item>
202 </layout>
203 </item> 223 </item>
204 </layout> 224 <item row="4" column="1">
205 </widget> 225 <widget class="QLabel" name="label_6">
206 </item> 226 <property name="text">
207 <item row="0" column="1"> 227 <string>Player 4</string>
208 <widget class="QGroupBox" name="Dpad"> 228 </property>
209 <property name="title"> 229 </widget>
210 <string>Directional Pad</string>
211 </property>
212 <property name="flat">
213 <bool>false</bool>
214 </property>
215 <property name="checkable">
216 <bool>false</bool>
217 </property>
218 <layout class="QGridLayout" name="gridLayout_2">
219 <item row="1" column="0">
220 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
221 <item>
222 <widget class="QLabel" name="labelDpadUp">
223 <property name="text">
224 <string>Up:</string>
225 </property>
226 </widget>
227 </item>
228 <item>
229 <widget class="QPushButton" name="buttonDpadUp">
230 <property name="text">
231 <string/>
232 </property>
233 </widget>
234 </item>
235 </layout>
236 </item> 230 </item>
237 <item row="1" column="1"> 231 <item row="5" column="1">
238 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> 232 <widget class="QLabel" name="label_7">
239 <item> 233 <property name="text">
240 <widget class="QLabel" name="labelDpadDown"> 234 <string>Player 5</string>
241 <property name="text"> 235 </property>
242 <string>Down:</string> 236 </widget>
243 </property>
244 </widget>
245 </item>
246 <item>
247 <widget class="QPushButton" name="buttonDpadDown">
248 <property name="text">
249 <string/>
250 </property>
251 </widget>
252 </item>
253 </layout>
254 </item> 237 </item>
255 <item row="0" column="0"> 238 <item row="6" column="1">
256 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> 239 <widget class="QLabel" name="label_8">
257 <item> 240 <property name="text">
258 <widget class="QLabel" name="labelDpadLeft"> 241 <string>Player 6</string>
259 <property name="text"> 242 </property>
260 <string>Left:</string> 243 </widget>
261 </property>
262 </widget>
263 </item>
264 <item>
265 <widget class="QPushButton" name="buttonDpadLeft">
266 <property name="text">
267 <string/>
268 </property>
269 </widget>
270 </item>
271 </layout>
272 </item> 244 </item>
273 <item row="0" column="1"> 245 <item row="7" column="1">
274 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> 246 <widget class="QLabel" name="label_9">
275 <item> 247 <property name="text">
276 <widget class="QLabel" name="labelDpadRight"> 248 <string>Player 7</string>
277 <property name="text"> 249 </property>
278 <string>Right:</string> 250 </widget>
279 </property> 251 </item>
280 </widget> 252 <item row="8" column="1">
281 </item> 253 <widget class="QLabel" name="label_10">
282 <item> 254 <property name="text">
283 <widget class="QPushButton" name="buttonDpadRight"> 255 <string>Player 8</string>
284 <property name="text"> 256 </property>
285 <string/> 257 </widget>
286 </property>
287 </widget>
288 </item>
289 </layout>
290 </item> 258 </item>
291 </layout> 259 </layout>
292 </widget> 260 </widget>
293 </item> 261 </item>
294 <item row="3" column="0"> 262 <item>
295 <widget class="QGroupBox" name="shoulderButtons"> 263 <widget class="QGroupBox" name="gridGroupBox">
296 <property name="title"> 264 <property name="title">
297 <string>Shoulder Buttons</string> 265 <string>Handheld</string>
298 </property>
299 <property name="flat">
300 <bool>false</bool>
301 </property> 266 </property>
302 <property name="checkable"> 267 <layout class="QGridLayout" name="gridLayout_2">
303 <bool>false</bool> 268 <item row="1" column="2">
304 </property> 269 <spacer name="horizontalSpacer_5">
305 <layout class="QGridLayout" name="gridLayout_3"> 270 <property name="orientation">
306 <item row="0" column="0"> 271 <enum>Qt::Horizontal</enum>
307 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> 272 </property>
308 <item> 273 <property name="sizeType">
309 <widget class="QLabel" name="labelL"> 274 <enum>QSizePolicy::Fixed</enum>
310 <property name="text"> 275 </property>
311 <string>L:</string> 276 <property name="sizeHint" stdset="0">
312 </property> 277 <size>
313 </widget> 278 <width>72</width>
314 </item> 279 <height>20</height>
315 <item> 280 </size>
316 <widget class="QPushButton" name="buttonL"> 281 </property>
317 <property name="text"> 282 </spacer>
318 <string/>
319 </property>
320 </widget>
321 </item>
322 </layout>
323 </item> 283 </item>
324 <item row="0" column="1"> 284 <item row="1" column="4">
325 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 285 <spacer name="horizontalSpacer_4">
326 <item> 286 <property name="orientation">
327 <widget class="QLabel" name="labelR"> 287 <enum>Qt::Horizontal</enum>
328 <property name="text"> 288 </property>
329 <string>R:</string> 289 <property name="sizeHint" stdset="0">
330 </property> 290 <size>
331 </widget> 291 <width>40</width>
332 </item> 292 <height>20</height>
333 <item> 293 </size>
334 <widget class="QPushButton" name="buttonR"> 294 </property>
335 <property name="text"> 295 </spacer>
336 <string/> 296 </item>
337 </property> 297 <item row="1" column="3">
338 </widget> 298 <widget class="QPushButton" name="handheld_configure">
339 </item> 299 <property name="text">
340 </layout> 300 <string>Configure</string>
301 </property>
302 </widget>
341 </item> 303 </item>
342 <item row="1" column="0"> 304 <item row="1" column="0">
343 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> 305 <spacer name="horizontalSpacer_3">
344 <item> 306 <property name="orientation">
345 <widget class="QLabel" name="labelZL"> 307 <enum>Qt::Horizontal</enum>
346 <property name="text"> 308 </property>
347 <string>ZL:</string> 309 <property name="sizeHint" stdset="0">
348 </property> 310 <size>
349 </widget> 311 <width>40</width>
350 </item> 312 <height>20</height>
351 <item> 313 </size>
352 <widget class="QPushButton" name="buttonZL"> 314 </property>
353 <property name="text"> 315 </spacer>
354 <string/>
355 </property>
356 </widget>
357 </item>
358 </layout>
359 </item> 316 </item>
360 <item row="1" column="1"> 317 <item row="1" column="1">
361 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> 318 <widget class="QCheckBox" name="handheld_connected">
362 <item> 319 <property name="text">
363 <widget class="QLabel" name="labelZR"> 320 <string>Joycons Docked</string>
364 <property name="text"> 321 </property>
365 <string>ZR:</string> 322 </widget>
366 </property>
367 </widget>
368 </item>
369 <item>
370 <widget class="QPushButton" name="buttonZR">
371 <property name="text">
372 <string/>
373 </property>
374 </widget>
375 </item>
376 </layout>
377 </item>
378 <item row="2" column="0">
379 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
380 <item>
381 <widget class="QLabel" name="labelSL">
382 <property name="text">
383 <string>SL:</string>
384 </property>
385 </widget>
386 </item>
387 <item>
388 <widget class="QPushButton" name="buttonSL">
389 <property name="text">
390 <string/>
391 </property>
392 </widget>
393 </item>
394 </layout>
395 </item> 323 </item>
396 <item row="2" column="1"> 324 <item row="0" column="1">
397 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> 325 <widget class="QCheckBox" name="use_docked_mode">
398 <item> 326 <property name="text">
399 <widget class="QLabel" name="labelSR"> 327 <string>Use Docked Mode</string>
400 <property name="text"> 328 </property>
401 <string>SR:</string> 329 </widget>
402 </property>
403 </widget>
404 </item>
405 <item>
406 <widget class="QPushButton" name="buttonSR">
407 <property name="text">
408 <string/>
409 </property>
410 </widget>
411 </item>
412 </layout>
413 </item> 330 </item>
414 </layout> 331 </layout>
415 </widget> 332 </widget>
416 </item> 333 </item>
417 <item row="1" column="1"> 334 <item>
418 <widget class="QGroupBox" name="RStick"> 335 <widget class="QGroupBox" name="gridGroupBox">
419 <property name="title"> 336 <property name="title">
420 <string>Right Stick</string> 337 <string>Other</string>
421 </property>
422 <property name="alignment">
423 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
424 </property>
425 <property name="flat">
426 <bool>false</bool>
427 </property> 338 </property>
428 <property name="checkable"> 339 <layout class="QGridLayout" name="gridLayout_3">
429 <bool>false</bool>
430 </property>
431 <layout class="QGridLayout" name="gridLayout_5">
432 <item row="1" column="1"> 340 <item row="1" column="1">
433 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 341 <widget class="QCheckBox" name="keyboard_enabled">
434 <item> 342 <property name="minimumSize">
435 <widget class="QLabel" name="labelRStickDown"> 343 <size>
436 <property name="text"> 344 <width>0</width>
437 <string>Down:</string> 345 <height>23</height>
438 </property> 346 </size>
439 </widget> 347 </property>
440 </item>
441 <item>
442 <widget class="QPushButton" name="buttonRStickDown">
443 <property name="text">
444 <string/>
445 </property>
446 </widget>
447 </item>
448 </layout>
449 </item>
450 <item row="0" column="1">
451 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
452 <item>
453 <widget class="QLabel" name="labelRStickRight">
454 <property name="text">
455 <string>Right:</string>
456 </property>
457 </widget>
458 </item>
459 <item>
460 <widget class="QPushButton" name="buttonRStickRight">
461 <property name="text">
462 <string/>
463 </property>
464 </widget>
465 </item>
466 </layout>
467 </item>
468 <item row="3" column="0" colspan="2">
469 <widget class="QPushButton" name="buttonRStickAnalog">
470 <property name="text"> 348 <property name="text">
471 <string>Set Analog Stick</string> 349 <string>Keyboard</string>
472 </property> 350 </property>
473 </widget> 351 </widget>
474 </item> 352 </item>
475 <item row="0" column="0">
476 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
477 <item>
478 <widget class="QLabel" name="labelRStickLeft">
479 <property name="text">
480 <string>Left:</string>
481 </property>
482 </widget>
483 </item>
484 <item>
485 <widget class="QPushButton" name="buttonRStickLeft">
486 <property name="text">
487 <string/>
488 </property>
489 </widget>
490 </item>
491 </layout>
492 </item>
493 <item row="1" column="0">
494 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
495 <item>
496 <widget class="QLabel" name="labelRStickUp">
497 <property name="text">
498 <string>Up:</string>
499 </property>
500 </widget>
501 </item>
502 <item>
503 <widget class="QPushButton" name="buttonRStickUp">
504 <property name="text">
505 <string/>
506 </property>
507 </widget>
508 </item>
509 </layout>
510 </item>
511 <item row="2" column="0">
512 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
513 <item>
514 <widget class="QLabel" name="labelRStickPressed">
515 <property name="text">
516 <string>Pressed:</string>
517 </property>
518 </widget>
519 </item>
520 <item>
521 <widget class="QPushButton" name="buttonRStick">
522 <property name="text">
523 <string/>
524 </property>
525 </widget>
526 </item>
527 </layout>
528 </item>
529 <item row="2" column="1"> 353 <item row="2" column="1">
530 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 354 <widget class="QCheckBox" name="debug_enabled">
531 <item> 355 <property name="text">
532 <widget class="QLabel" name="labelRStickMod"> 356 <string>Debug Controller</string>
533 <property name="text"> 357 </property>
534 <string>Modifier:</string> 358 </widget>
535 </property>
536 </widget>
537 </item>
538 <item>
539 <widget class="QPushButton" name="buttonRStickMod">
540 <property name="text">
541 <string/>
542 </property>
543 </widget>
544 </item>
545 </layout>
546 </item> 359 </item>
547 </layout> 360 <item row="3" column="1">
548 </widget> 361 <widget class="QCheckBox" name="touchscreen_enabled">
549 </item>
550 <item row="1" column="0">
551 <widget class="QGroupBox" name="LStick">
552 <property name="title">
553 <string>Left Stick</string>
554 </property>
555 <property name="flat">
556 <bool>false</bool>
557 </property>
558 <property name="checkable">
559 <bool>false</bool>
560 </property>
561 <layout class="QGridLayout" name="gridLayout_4">
562 <item row="1" column="1">
563 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
564 <item>
565 <widget class="QLabel" name="labelLStickDown">
566 <property name="text">
567 <string>Down:</string>
568 </property>
569 </widget>
570 </item>
571 <item>
572 <widget class="QPushButton" name="buttonLStickDown">
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 </layout>
579 </item>
580 <item row="4" column="0" colspan="2">
581 <widget class="QPushButton" name="buttonLStickAnalog">
582 <property name="text"> 362 <property name="text">
583 <string>Set Analog Stick</string> 363 <string>Touchscreen</string>
584 </property> 364 </property>
585 </widget> 365 </widget>
586 </item> 366 </item>
587 <item row="0" column="1"> 367 <item row="0" column="1">
588 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> 368 <widget class="QCheckBox" name="mouse_enabled">
589 <item> 369 <property name="minimumSize">
590 <widget class="QLabel" name="labelLStickRight"> 370 <size>
591 <property name="text"> 371 <width>0</width>
592 <string>Right:</string> 372 <height>23</height>
593 </property> 373 </size>
594 </widget> 374 </property>
595 </item> 375 <property name="text">
596 <item> 376 <string>Mouse</string>
597 <widget class="QPushButton" name="buttonLStickRight"> 377 </property>
598 <property name="text"> 378 </widget>
599 <string/> 379 </item>
600 </property> 380 <item row="0" column="4">
601 </widget> 381 <spacer name="horizontalSpacer_7">
602 </item> 382 <property name="orientation">
603 </layout> 383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
604 </item> 408 </item>
605 <item row="0" column="0"> 409 <item row="0" column="0">
606 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> 410 <spacer name="horizontalSpacer_6">
607 <item> 411 <property name="orientation">
608 <widget class="QLabel" name="labelLStickLeft"> 412 <enum>Qt::Horizontal</enum>
609 <property name="text"> 413 </property>
610 <string>Left:</string> 414 <property name="sizeHint" stdset="0">
611 </property> 415 <size>
612 </widget> 416 <width>40</width>
613 </item> 417 <height>20</height>
614 <item> 418 </size>
615 <widget class="QPushButton" name="buttonLStickLeft"> 419 </property>
616 <property name="text"> 420 </spacer>
617 <string/>
618 </property>
619 </widget>
620 </item>
621 </layout>
622 </item> 421 </item>
623 <item row="1" column="0"> 422 <item row="3" column="3">
624 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 423 <widget class="QPushButton" name="touchscreen_advanced">
625 <item> 424 <property name="text">
626 <widget class="QLabel" name="labelLStickUp"> 425 <string>Advanced</string>
627 <property name="text"> 426 </property>
628 <string>Up:</string> 427 </widget>
629 </property>
630 </widget>
631 </item>
632 <item>
633 <widget class="QPushButton" name="buttonLStickUp">
634 <property name="text">
635 <string/>
636 </property>
637 </widget>
638 </item>
639 </layout>
640 </item>
641 <item row="3" column="0">
642 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
643 <item>
644 <widget class="QLabel" name="labelLStickMod">
645 <property name="text">
646 <string>Modifier:</string>
647 </property>
648 </widget>
649 </item>
650 <item>
651 <widget class="QPushButton" name="buttonLStickMod">
652 <property name="text">
653 <string/>
654 </property>
655 </widget>
656 </item>
657 </layout>
658 </item> 428 </item>
659 <item row="3" column="1"> 429 <item row="2" column="3">
660 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 430 <widget class="QPushButton" name="debug_configure">
661 <item> 431 <property name="text">
662 <widget class="QLabel" name="labelLStickPressed"> 432 <string>Configure</string>
663 <property name="text"> 433 </property>
664 <string>Pressed:</string> 434 </widget>
665 </property> 435 </item>
666 </widget> 436 <item row="0" column="3">
667 </item> 437 <widget class="QPushButton" name="mouse_advanced">
668 <item> 438 <property name="text">
669 <widget class="QPushButton" name="buttonLStick"> 439 <string>Advanced</string>
670 <property name="text"> 440 </property>
671 <string/> 441 </widget>
672 </property>
673 </widget>
674 </item>
675 </layout>
676 </item> 442 </item>
677 </layout> 443 </layout>
678 </widget> 444 </widget>
679 </item> 445 </item>
680 </layout>
681 </item>
682 <item>
683 <layout class="QHBoxLayout" name="horizontalLayout">
684 <item> 446 <item>
685 <spacer name="horizontalSpacer"> 447 <spacer name="verticalSpacer">
686 <property name="orientation"> 448 <property name="orientation">
687 <enum>Qt::Horizontal</enum> 449 <enum>Qt::Vertical</enum>
688 </property> 450 </property>
689 <property name="sizeHint" stdset="0"> 451 <property name="sizeHint" stdset="0">
690 <size> 452 <size>
691 <width>40</width> 453 <width>20</width>
692 <height>20</height> 454 <height>40</height>
693 </size> 455 </size>
694 </property> 456 </property>
695 </spacer> 457 </spacer>
696 </item> 458 </item>
697 <item> 459 <item>
698 <widget class="QPushButton" name="buttonClearAll"> 460 <layout class="QHBoxLayout" name="horizontalLayout">
699 <property name="sizePolicy"> 461 <item>
700 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 462 <widget class="QPushButton" name="restore_defaults_button">
701 <horstretch>0</horstretch> 463 <property name="text">
702 <verstretch>0</verstretch> 464 <string>Restore Defaults</string>
703 </sizepolicy> 465 </property>
704 </property> 466 </widget>
705 <property name="sizeIncrement"> 467 </item>
706 <size> 468 <item>
707 <width>0</width> 469 <spacer name="horizontalSpacer_9">
708 <height>0</height> 470 <property name="orientation">
709 </size> 471 <enum>Qt::Horizontal</enum>
710 </property> 472 </property>
711 <property name="baseSize"> 473 <property name="sizeHint" stdset="0">
712 <size> 474 <size>
713 <width>0</width> 475 <width>40</width>
714 <height>0</height> 476 <height>20</height>
715 </size> 477 </size>
716 </property> 478 </property>
717 <property name="layoutDirection"> 479 </spacer>
718 <enum>Qt::LeftToRight</enum> 480 </item>
719 </property> 481 </layout>
720 <property name="text">
721 <string>Clear All</string>
722 </property>
723 </widget>
724 </item>
725 <item>
726 <widget class="QPushButton" name="buttonRestoreDefaults">
727 <property name="sizePolicy">
728 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
729 <horstretch>0</horstretch>
730 <verstretch>0</verstretch>
731 </sizepolicy>
732 </property>
733 <property name="sizeIncrement">
734 <size>
735 <width>0</width>
736 <height>0</height>
737 </size>
738 </property>
739 <property name="baseSize">
740 <size>
741 <width>0</width>
742 <height>0</height>
743 </size>
744 </property>
745 <property name="layoutDirection">
746 <enum>Qt::LeftToRight</enum>
747 </property>
748 <property name="text">
749 <string>Restore Defaults</string>
750 </property>
751 </widget>
752 </item> 482 </item>
753 </layout> 483 </layout>
754 </item> 484 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
new file mode 100644
index 000000000..ba6e09368
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -0,0 +1,508 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QColorDialog>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_input_player.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_input_player.h"
18
19const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
20 ConfigureInputPlayer::analog_sub_buttons{{
21 "up",
22 "down",
23 "left",
24 "right",
25 "modifier",
26 }};
27
28static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
29 int column_new) {
30 const auto item = grid->itemAtPosition(row_old, column_old);
31 // grid->removeItem(item);
32 grid->addItem(item, row_new, column_new);
33}
34
35static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
36 const int index1 = grid->indexOf(item);
37 const int index2 = grid->indexOf(onTopOf);
38 int row, column, rowSpan, columnSpan;
39 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
40 grid->takeAt(index1);
41 grid->addWidget(item, row, column, rowSpan, columnSpan);
42}
43
44static QString GetKeyName(int key_code) {
45 switch (key_code) {
46 case Qt::Key_Shift:
47 return QObject::tr("Shift");
48 case Qt::Key_Control:
49 return QObject::tr("Ctrl");
50 case Qt::Key_Alt:
51 return QObject::tr("Alt");
52 case Qt::Key_Meta:
53 return "";
54 default:
55 return QKeySequence(key_code).toString();
56 }
57}
58
59static void SetAnalogButton(const Common::ParamPackage& input_param,
60 Common::ParamPackage& analog_param, const std::string& button_name) {
61 if (analog_param.Get("engine", "") != "analog_from_button") {
62 analog_param = {
63 {"engine", "analog_from_button"},
64 {"modifier_scale", "0.5"},
65 };
66 }
67 analog_param.Set(button_name, input_param.Serialize());
68}
69
70static QString ButtonToText(const Common::ParamPackage& param) {
71 if (!param.Has("engine")) {
72 return QObject::tr("[not set]");
73 } else if (param.Get("engine", "") == "keyboard") {
74 return GetKeyName(param.Get("code", 0));
75 } else if (param.Get("engine", "") == "sdl") {
76 if (param.Has("hat")) {
77 return QString(QObject::tr("Hat %1 %2"))
78 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
79 }
80 if (param.Has("axis")) {
81 return QString(QObject::tr("Axis %1%2"))
82 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
83 }
84 if (param.Has("button")) {
85 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
86 }
87 return QString();
88 } else {
89 return QObject::tr("[unknown]");
90 }
91};
92
93static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
94 if (!param.Has("engine")) {
95 return QObject::tr("[not set]");
96 } else if (param.Get("engine", "") == "analog_from_button") {
97 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
98 } else if (param.Get("engine", "") == "sdl") {
99 if (dir == "modifier") {
100 return QString(QObject::tr("[unused]"));
101 }
102
103 if (dir == "left" || dir == "right") {
104 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
105 } else if (dir == "up" || dir == "down") {
106 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
107 }
108 return QString();
109 } else {
110 return QObject::tr("[unknown]");
111 }
112};
113
114ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
115 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
116 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
117 player_index(player_index), debug(debug) {
118
119 ui->setupUi(this);
120 setFocusPolicy(Qt::ClickFocus);
121
122 button_map = {
123 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
124 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
125 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
126 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
127 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
128 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
129 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
130 };
131
132 analog_map_buttons = {{
133 {
134 ui->buttonLStickUp,
135 ui->buttonLStickDown,
136 ui->buttonLStickLeft,
137 ui->buttonLStickRight,
138 ui->buttonLStickMod,
139 },
140 {
141 ui->buttonRStickUp,
142 ui->buttonRStickDown,
143 ui->buttonRStickLeft,
144 ui->buttonRStickRight,
145 ui->buttonRStickMod,
146 },
147 }};
148
149 debug_hidden = {
150 ui->buttonSL, ui->labelSL,
151 ui->buttonSR, ui->labelSR,
152 ui->buttonLStick, ui->labelLStickPressed,
153 ui->buttonRStick, ui->labelRStickPressed,
154 ui->buttonHome, ui->labelHome,
155 ui->buttonScreenshot, ui->labelScreenshot,
156 };
157
158 auto layout = Settings::values.players[player_index].type;
159 if (debug)
160 layout = Settings::ControllerType::DualJoycon;
161
162 switch (layout) {
163 case Settings::ControllerType::ProController:
164 case Settings::ControllerType::DualJoycon:
165 layout_hidden = {
166 ui->buttonSL,
167 ui->labelSL,
168 ui->buttonSR,
169 ui->labelSR,
170 };
171 break;
172 case Settings::ControllerType::LeftJoycon:
173 layout_hidden = {
174 ui->right_body_button,
175 ui->right_buttons_button,
176 ui->right_body_label,
177 ui->right_buttons_label,
178 ui->buttonR,
179 ui->labelR,
180 ui->buttonZR,
181 ui->labelZR,
182 ui->labelHome,
183 ui->buttonHome,
184 ui->buttonPlus,
185 ui->labelPlus,
186 ui->RStick,
187 ui->faceButtons,
188 };
189 break;
190 case Settings::ControllerType::RightJoycon:
191 layout_hidden = {
192 ui->left_body_button, ui->left_buttons_button,
193 ui->left_body_label, ui->left_buttons_label,
194 ui->buttonL, ui->labelL,
195 ui->buttonZL, ui->labelZL,
196 ui->labelScreenshot, ui->buttonScreenshot,
197 ui->buttonMinus, ui->labelMinus,
198 ui->LStick, ui->Dpad,
199 };
200 break;
201 }
202
203 if (debug || layout == Settings::ControllerType::ProController) {
204 ui->controller_color->hide();
205 } else {
206 if (layout == Settings::ControllerType::LeftJoycon ||
207 layout == Settings::ControllerType::RightJoycon) {
208 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
209
210 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
211 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
212 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
213 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
214 }
215 }
216
217 for (auto* widget : layout_hidden)
218 widget->setVisible(false);
219
220 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
221
222 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
223 if (!button_map[button_id])
224 continue;
225 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
226 connect(button_map[button_id], &QPushButton::released, [=]() {
227 handleClick(
228 button_map[button_id],
229 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
230 InputCommon::Polling::DeviceType::Button);
231 });
232 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
233 [=](const QPoint& menu_location) {
234 QMenu context_menu;
235 context_menu.addAction(tr("Clear"), [&] {
236 buttons_param[button_id].Clear();
237 button_map[button_id]->setText(tr("[not set]"));
238 });
239 context_menu.addAction(tr("Restore Default"), [&] {
240 buttons_param[button_id] = Common::ParamPackage{
241 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
242 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
243 });
244 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
245 });
246 }
247
248 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
249 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
250 if (!analog_map_buttons[analog_id][sub_button_id])
251 continue;
252 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
253 Qt::CustomContextMenu);
254 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
255 handleClick(analog_map_buttons[analog_id][sub_button_id],
256 [=](const Common::ParamPackage& params) {
257 SetAnalogButton(params, analogs_param[analog_id],
258 analog_sub_buttons[sub_button_id]);
259 },
260 InputCommon::Polling::DeviceType::Button);
261 });
262 connect(analog_map_buttons[analog_id][sub_button_id],
263 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
264 QMenu context_menu;
265 context_menu.addAction(tr("Clear"), [&] {
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
268 });
269 context_menu.addAction(tr("Restore Default"), [&] {
270 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
271 Config::default_analogs[analog_id][sub_button_id])};
272 SetAnalogButton(params, analogs_param[analog_id],
273 analog_sub_buttons[sub_button_id]);
274 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
275 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
276 });
277 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
278 menu_location));
279 });
280 }
281 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
282 QMessageBox::information(this, tr("Information"),
283 tr("After pressing OK, first move your joystick horizontally, "
284 "and then vertically."));
285 handleClick(
286 analog_map_stick[analog_id],
287 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
288 InputCommon::Polling::DeviceType::Analog);
289 });
290 }
291
292 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
293 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
294
295 timeout_timer->setSingleShot(true);
296 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
297
298 connect(poll_timer.get(), &QTimer::timeout, [this]() {
299 Common::ParamPackage params;
300 for (auto& poller : device_pollers) {
301 params = poller->GetNextInput();
302 if (params.Has("engine")) {
303 setPollingResult(params, false);
304 return;
305 }
306 }
307 });
308
309 controller_color_buttons = {
310 ui->left_body_button,
311 ui->left_buttons_button,
312 ui->right_body_button,
313 ui->right_buttons_button,
314 };
315
316 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
317 connect(controller_color_buttons[i], &QPushButton::clicked, this,
318 std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
319 }
320
321 this->loadConfiguration();
322 this->resize(0, 0);
323
324 // TODO(wwylele): enable this when we actually emulate it
325 ui->buttonHome->setEnabled(false);
326}
327
328ConfigureInputPlayer::~ConfigureInputPlayer() = default;
329
330void ConfigureInputPlayer::applyConfiguration() {
331 auto& buttons =
332 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
333 auto& analogs =
334 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
335
336 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
337 [](const Common::ParamPackage& param) { return param.Serialize(); });
338 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
339 [](const Common::ParamPackage& param) { return param.Serialize(); });
340
341 if (debug)
342 return;
343
344 std::array<u32, 4> colors{};
345 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
346 [](QColor color) { return color.rgb(); });
347
348 Settings::values.players[player_index].body_color_left = colors[0];
349 Settings::values.players[player_index].button_color_left = colors[1];
350 Settings::values.players[player_index].body_color_right = colors[2];
351 Settings::values.players[player_index].button_color_right = colors[3];
352}
353
354void ConfigureInputPlayer::OnControllerButtonClick(int i) {
355 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
356 if (!new_bg_color.isValid())
357 return;
358 controller_colors[i] = new_bg_color;
359 controller_color_buttons[i]->setStyleSheet(
360 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
361}
362
363void ConfigureInputPlayer::loadConfiguration() {
364 if (debug) {
365 std::transform(Settings::values.debug_pad_buttons.begin(),
366 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
367 [](const std::string& str) { return Common::ParamPackage(str); });
368 std::transform(Settings::values.debug_pad_analogs.begin(),
369 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
370 [](const std::string& str) { return Common::ParamPackage(str); });
371 } else {
372 std::transform(Settings::values.players[player_index].buttons.begin(),
373 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
374 [](const std::string& str) { return Common::ParamPackage(str); });
375 std::transform(Settings::values.players[player_index].analogs.begin(),
376 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
377 [](const std::string& str) { return Common::ParamPackage(str); });
378 }
379
380 updateButtonLabels();
381
382 if (debug)
383 return;
384
385 std::array<u32, 4> colors = {
386 Settings::values.players[player_index].body_color_left,
387 Settings::values.players[player_index].button_color_left,
388 Settings::values.players[player_index].body_color_right,
389 Settings::values.players[player_index].button_color_right,
390 };
391
392 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
393 [](u32 rgb) { return QColor::fromRgb(rgb); });
394
395 for (std::size_t i = 0; i < colors.size(); ++i) {
396 controller_color_buttons[i]->setStyleSheet(
397 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
398 }
399}
400
401void ConfigureInputPlayer::restoreDefaults() {
402 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
403 buttons_param[button_id] = Common::ParamPackage{
404 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
405 }
406
407 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
408 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
409 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
410 Config::default_analogs[analog_id][sub_button_id])};
411 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
412 }
413 }
414 updateButtonLabels();
415}
416
417void ConfigureInputPlayer::ClearAll() {
418 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
419 if (button_map[button_id] && button_map[button_id]->isEnabled())
420 buttons_param[button_id].Clear();
421 }
422 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
423 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
424 if (analog_map_buttons[analog_id][sub_button_id] &&
425 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
426 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
427 }
428 }
429
430 updateButtonLabels();
431}
432
433void ConfigureInputPlayer::updateButtonLabels() {
434 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
435 button_map[button]->setText(ButtonToText(buttons_param[button]));
436 }
437
438 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
439 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
440 if (analog_map_buttons[analog_id][sub_button_id]) {
441 analog_map_buttons[analog_id][sub_button_id]->setText(
442 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
443 }
444 }
445 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
446 }
447}
448
449void ConfigureInputPlayer::handleClick(
450 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
451 InputCommon::Polling::DeviceType type) {
452 button->setText(tr("[press key]"));
453 button->setFocus();
454
455 const auto iter = std::find(button_map.begin(), button_map.end(), button);
456 ASSERT(iter != button_map.end());
457 const auto index = std::distance(button_map.begin(), iter);
458 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
459
460 input_setter = new_input_setter;
461
462 device_pollers = InputCommon::Polling::GetPollers(type);
463
464 // Keyboard keys can only be used as button devices
465 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
466
467 for (auto& poller : device_pollers) {
468 poller->Start();
469 }
470
471 grabKeyboard();
472 grabMouse();
473 timeout_timer->start(5000); // Cancel after 5 seconds
474 poll_timer->start(200); // Check for new inputs every 200ms
475}
476
477void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) {
478 releaseKeyboard();
479 releaseMouse();
480 timeout_timer->stop();
481 poll_timer->stop();
482 for (auto& poller : device_pollers) {
483 poller->Stop();
484 }
485
486 if (!abort) {
487 (*input_setter)(params);
488 }
489
490 updateButtonLabels();
491 input_setter = std::nullopt;
492}
493
494void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
495 if (!input_setter || !event)
496 return;
497
498 if (event->key() != Qt::Key_Escape) {
499 if (want_keyboard_keys) {
500 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
501 false);
502 } else {
503 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
504 return;
505 }
506 }
507 setPollingResult({}, true);
508}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
new file mode 100644
index 000000000..b0e5550c5
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -0,0 +1,103 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <optional>
11#include <string>
12#include <unordered_map>
13#include <QDialog>
14#include <QKeyEvent>
15#include "common/param_package.h"
16#include "core/settings.h"
17#include "input_common/main.h"
18#include "ui_configure_input.h"
19
20class QPushButton;
21class QString;
22class QTimer;
23
24namespace Ui {
25class ConfigureInputPlayer;
26}
27
28class ConfigureInputPlayer : public QDialog {
29 Q_OBJECT
30
31public:
32 explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
33 ~ConfigureInputPlayer() override;
34
35 /// Save all button configurations to settings file
36 void applyConfiguration();
37
38private:
39 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
40
41 u8 player_index;
42 bool debug;
43
44 std::unique_ptr<QTimer> timeout_timer;
45 std::unique_ptr<QTimer> poll_timer;
46
47 /// This will be the the setting function when an input is awaiting configuration.
48 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
49
50 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
51 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
52
53 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
54
55 /// Each button input is represented by a QPushButton.
56 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
57
58 std::vector<QWidget*> debug_hidden;
59 std::vector<QWidget*> layout_hidden;
60
61 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
62 /// down, left, right, and modifier, respectively.
63 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
64 analog_map_buttons;
65
66 /// Analog inputs are also represented each with a single button, used to configure with an
67 /// actual analog stick
68 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
69
70 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
71
72 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
73
74 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
75 /// keyboard events are ignored.
76 bool want_keyboard_keys = false;
77
78 std::array<QPushButton*, 4> controller_color_buttons;
79 std::array<QColor, 4> controller_colors;
80
81 void OnControllerButtonClick(int i);
82
83 /// Load configuration settings.
84 void loadConfiguration();
85 /// Restore all buttons to their default values.
86 void restoreDefaults();
87 /// Clear all input configuration
88 void ClearAll();
89
90 /// Update UI to reflect current configuration.
91 void updateButtonLabels();
92
93 /// Called when the button was pressed.
94 void handleClick(QPushButton* button,
95 std::function<void(const Common::ParamPackage&)> new_input_setter,
96 InputCommon::Polling::DeviceType type);
97
98 /// Finish polling and configure input using the input_setter
99 void setPollingResult(const Common::ParamPackage& params, bool abort);
100
101 /// Handle key press events.
102 void keyPressEvent(QKeyEvent* event) override;
103};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
new file mode 100644
index 000000000..42db020be
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -0,0 +1,1164 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>408</width>
10 <height>731</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QGridLayout" name="buttons">
19 <item row="1" column="1">
20 <widget class="QGroupBox" name="RStick">
21 <property name="title">
22 <string>Right Stick</string>
23 </property>
24 <property name="alignment">
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
26 </property>
27 <property name="flat">
28 <bool>false</bool>
29 </property>
30 <property name="checkable">
31 <bool>false</bool>
32 </property>
33 <layout class="QGridLayout" name="gridLayout_5">
34 <item row="1" column="1">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
36 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
38 <item>
39 <widget class="QLabel" name="labelRStickDown">
40 <property name="text">
41 <string>Down:</string>
42 </property>
43 </widget>
44 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item>
61 <widget class="QLabel" name="labelRStickRight">
62 <property name="text">
63 <string>Right:</string>
64 </property>
65 </widget>
66 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item>
90 <widget class="QLabel" name="labelRStickLeft">
91 <property name="text">
92 <string>Left:</string>
93 </property>
94 </widget>
95 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item>
112 <widget class="QLabel" name="labelRStickUp">
113 <property name="text">
114 <string>Up:</string>
115 </property>
116 </widget>
117 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item>
134 <widget class="QLabel" name="labelRStickPressed">
135 <property name="text">
136 <string>Pressed:</string>
137 </property>
138 </widget>
139 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget>
148 </item>
149 </layout>
150 </item>
151 <item row="2" column="1">
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
153 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
155 <item>
156 <widget class="QLabel" name="labelRStickMod">
157 <property name="text">
158 <string>Modifier:</string>
159 </property>
160 </widget>
161 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 </layout>
174 </widget>
175 </item>
176 <item row="0" column="1">
177 <widget class="QGroupBox" name="Dpad">
178 <property name="title">
179 <string>Directional Pad</string>
180 </property>
181 <property name="flat">
182 <bool>false</bool>
183 </property>
184 <property name="checkable">
185 <bool>false</bool>
186 </property>
187 <layout class="QGridLayout" name="gridLayout_2">
188 <item row="1" column="0">
189 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
190 <item>
191 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
192 <item>
193 <widget class="QLabel" name="labelDpadUp">
194 <property name="text">
195 <string>Up:</string>
196 </property>
197 </widget>
198 </item>
199 </layout>
200 </item>
201 <item>
202 <widget class="QPushButton" name="buttonDpadUp">
203 <property name="text">
204 <string/>
205 </property>
206 </widget>
207 </item>
208 </layout>
209 </item>
210 <item row="1" column="1">
211 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
212 <item>
213 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
214 <item>
215 <widget class="QLabel" name="labelDpadDown">
216 <property name="text">
217 <string>Down:</string>
218 </property>
219 </widget>
220 </item>
221 </layout>
222 </item>
223 <item>
224 <widget class="QPushButton" name="buttonDpadDown">
225 <property name="text">
226 <string/>
227 </property>
228 </widget>
229 </item>
230 </layout>
231 </item>
232 <item row="0" column="0">
233 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
234 <item>
235 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
236 <item>
237 <widget class="QLabel" name="labelDpadLeft">
238 <property name="minimumSize">
239 <size>
240 <width>80</width>
241 <height>0</height>
242 </size>
243 </property>
244 <property name="text">
245 <string>Left:</string>
246 </property>
247 </widget>
248 </item>
249 </layout>
250 </item>
251 <item>
252 <widget class="QPushButton" name="buttonDpadLeft">
253 <property name="text">
254 <string/>
255 </property>
256 </widget>
257 </item>
258 </layout>
259 </item>
260 <item row="0" column="1">
261 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
262 <item>
263 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
264 <item>
265 <widget class="QLabel" name="labelDpadRight">
266 <property name="minimumSize">
267 <size>
268 <width>80</width>
269 <height>0</height>
270 </size>
271 </property>
272 <property name="text">
273 <string>Right:</string>
274 </property>
275 </widget>
276 </item>
277 </layout>
278 </item>
279 <item>
280 <widget class="QPushButton" name="buttonDpadRight">
281 <property name="text">
282 <string/>
283 </property>
284 </widget>
285 </item>
286 </layout>
287 </item>
288 </layout>
289 </widget>
290 </item>
291 <item row="0" column="0">
292 <widget class="QGroupBox" name="faceButtons">
293 <property name="title">
294 <string>Face Buttons</string>
295 </property>
296 <property name="flat">
297 <bool>false</bool>
298 </property>
299 <property name="checkable">
300 <bool>false</bool>
301 </property>
302 <layout class="QGridLayout" name="gridLayout">
303 <item row="0" column="0">
304 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
305 <item>
306 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
307 <item>
308 <widget class="QLabel" name="labelA">
309 <property name="minimumSize">
310 <size>
311 <width>80</width>
312 <height>0</height>
313 </size>
314 </property>
315 <property name="text">
316 <string>A:</string>
317 </property>
318 </widget>
319 </item>
320 </layout>
321 </item>
322 <item>
323 <widget class="QPushButton" name="buttonA">
324 <property name="text">
325 <string/>
326 </property>
327 </widget>
328 </item>
329 </layout>
330 </item>
331 <item row="0" column="1">
332 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
333 <item>
334 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
335 <item>
336 <widget class="QLabel" name="labelB">
337 <property name="minimumSize">
338 <size>
339 <width>80</width>
340 <height>0</height>
341 </size>
342 </property>
343 <property name="text">
344 <string>B:</string>
345 </property>
346 </widget>
347 </item>
348 </layout>
349 </item>
350 <item>
351 <widget class="QPushButton" name="buttonB">
352 <property name="text">
353 <string/>
354 </property>
355 </widget>
356 </item>
357 </layout>
358 </item>
359 <item row="1" column="0">
360 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
361 <item>
362 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
363 <item>
364 <widget class="QLabel" name="labelX">
365 <property name="text">
366 <string>X:</string>
367 </property>
368 </widget>
369 </item>
370 </layout>
371 </item>
372 <item>
373 <widget class="QPushButton" name="buttonX">
374 <property name="text">
375 <string/>
376 </property>
377 </widget>
378 </item>
379 </layout>
380 </item>
381 <item row="1" column="1">
382 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
383 <item>
384 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
385 <item>
386 <widget class="QLabel" name="labelY">
387 <property name="text">
388 <string>Y:</string>
389 </property>
390 </widget>
391 </item>
392 </layout>
393 </item>
394 <item>
395 <widget class="QPushButton" name="buttonY">
396 <property name="text">
397 <string/>
398 </property>
399 </widget>
400 </item>
401 </layout>
402 </item>
403 </layout>
404 </widget>
405 </item>
406 <item row="5" column="0" colspan="2">
407 <widget class="QGroupBox" name="controller_color">
408 <property name="title">
409 <string>Controller Color</string>
410 </property>
411 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
412 <item row="0" column="0">
413 <spacer name="horizontalSpacer_2">
414 <property name="orientation">
415 <enum>Qt::Horizontal</enum>
416 </property>
417 <property name="sizeHint" stdset="0">
418 <size>
419 <width>40</width>
420 <height>20</height>
421 </size>
422 </property>
423 </spacer>
424 </item>
425 <item row="0" column="1">
426 <widget class="QLabel" name="left_body_label">
427 <property name="text">
428 <string>Left Body</string>
429 </property>
430 </widget>
431 </item>
432 <item row="0" column="6">
433 <spacer name="horizontalSpacer_3">
434 <property name="orientation">
435 <enum>Qt::Horizontal</enum>
436 </property>
437 <property name="sizeHint" stdset="0">
438 <size>
439 <width>40</width>
440 <height>20</height>
441 </size>
442 </property>
443 </spacer>
444 </item>
445 <item row="1" column="1">
446 <widget class="QLabel" name="left_buttons_label">
447 <property name="minimumSize">
448 <size>
449 <width>90</width>
450 <height>0</height>
451 </size>
452 </property>
453 <property name="text">
454 <string>Left Buttons</string>
455 </property>
456 </widget>
457 </item>
458 <item row="1" column="5">
459 <widget class="QPushButton" name="right_buttons_button">
460 <property name="sizePolicy">
461 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
462 <horstretch>0</horstretch>
463 <verstretch>0</verstretch>
464 </sizepolicy>
465 </property>
466 <property name="minimumSize">
467 <size>
468 <width>32</width>
469 <height>0</height>
470 </size>
471 </property>
472 <property name="maximumSize">
473 <size>
474 <width>40</width>
475 <height>16777215</height>
476 </size>
477 </property>
478 <property name="text">
479 <string/>
480 </property>
481 </widget>
482 </item>
483 <item row="0" column="4">
484 <widget class="QLabel" name="right_body_label">
485 <property name="text">
486 <string>Right Body</string>
487 </property>
488 </widget>
489 </item>
490 <item row="1" column="4">
491 <widget class="QLabel" name="right_buttons_label">
492 <property name="minimumSize">
493 <size>
494 <width>90</width>
495 <height>0</height>
496 </size>
497 </property>
498 <property name="text">
499 <string>Right Buttons</string>
500 </property>
501 </widget>
502 </item>
503 <item row="1" column="2">
504 <widget class="QPushButton" name="left_buttons_button">
505 <property name="sizePolicy">
506 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
507 <horstretch>0</horstretch>
508 <verstretch>0</verstretch>
509 </sizepolicy>
510 </property>
511 <property name="minimumSize">
512 <size>
513 <width>32</width>
514 <height>0</height>
515 </size>
516 </property>
517 <property name="maximumSize">
518 <size>
519 <width>40</width>
520 <height>16777215</height>
521 </size>
522 </property>
523 <property name="text">
524 <string/>
525 </property>
526 </widget>
527 </item>
528 <item row="0" column="2">
529 <widget class="QPushButton" name="left_body_button">
530 <property name="sizePolicy">
531 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
532 <horstretch>0</horstretch>
533 <verstretch>0</verstretch>
534 </sizepolicy>
535 </property>
536 <property name="minimumSize">
537 <size>
538 <width>32</width>
539 <height>0</height>
540 </size>
541 </property>
542 <property name="maximumSize">
543 <size>
544 <width>40</width>
545 <height>16777215</height>
546 </size>
547 </property>
548 <property name="text">
549 <string/>
550 </property>
551 </widget>
552 </item>
553 <item row="0" column="5">
554 <widget class="QPushButton" name="right_body_button">
555 <property name="sizePolicy">
556 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
557 <horstretch>0</horstretch>
558 <verstretch>0</verstretch>
559 </sizepolicy>
560 </property>
561 <property name="minimumSize">
562 <size>
563 <width>32</width>
564 <height>0</height>
565 </size>
566 </property>
567 <property name="maximumSize">
568 <size>
569 <width>40</width>
570 <height>16777215</height>
571 </size>
572 </property>
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 <item row="0" column="3">
579 <spacer name="horizontalSpacer_4">
580 <property name="orientation">
581 <enum>Qt::Horizontal</enum>
582 </property>
583 <property name="sizeType">
584 <enum>QSizePolicy::Fixed</enum>
585 </property>
586 <property name="sizeHint" stdset="0">
587 <size>
588 <width>20</width>
589 <height>20</height>
590 </size>
591 </property>
592 </spacer>
593 </item>
594 </layout>
595 </widget>
596 </item>
597 <item row="1" column="0">
598 <widget class="QGroupBox" name="LStick">
599 <property name="title">
600 <string>Left Stick</string>
601 </property>
602 <property name="flat">
603 <bool>false</bool>
604 </property>
605 <property name="checkable">
606 <bool>false</bool>
607 </property>
608 <layout class="QGridLayout" name="gridLayout_4">
609 <item row="1" column="1">
610 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
611 <item>
612 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
613 <item>
614 <widget class="QLabel" name="labelLStickUp">
615 <property name="text">
616 <string>Up:</string>
617 </property>
618 </widget>
619 </item>
620 </layout>
621 </item>
622 <item>
623 <widget class="QPushButton" name="buttonLStickUp">
624 <property name="text">
625 <string/>
626 </property>
627 </widget>
628 </item>
629 </layout>
630 </item>
631 <item row="0" column="2">
632 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
633 <item>
634 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
635 <item>
636 <widget class="QLabel" name="labelLStickRight">
637 <property name="text">
638 <string>Right:</string>
639 </property>
640 </widget>
641 </item>
642 </layout>
643 </item>
644 <item>
645 <widget class="QPushButton" name="buttonLStickRight">
646 <property name="text">
647 <string/>
648 </property>
649 </widget>
650 </item>
651 </layout>
652 </item>
653 <item row="4" column="1" colspan="2">
654 <widget class="QPushButton" name="buttonLStickAnalog">
655 <property name="text">
656 <string>Set Analog Stick</string>
657 </property>
658 </widget>
659 </item>
660 <item row="0" column="1">
661 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
662 <item>
663 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
664 <item>
665 <widget class="QLabel" name="labelLStickLeft">
666 <property name="text">
667 <string>Left:</string>
668 </property>
669 </widget>
670 </item>
671 </layout>
672 </item>
673 <item>
674 <widget class="QPushButton" name="buttonLStickLeft">
675 <property name="text">
676 <string/>
677 </property>
678 </widget>
679 </item>
680 </layout>
681 </item>
682 <item row="1" column="2">
683 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
684 <item>
685 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
686 <item>
687 <widget class="QLabel" name="labelLStickDown">
688 <property name="text">
689 <string>Down:</string>
690 </property>
691 </widget>
692 </item>
693 </layout>
694 </item>
695 <item>
696 <widget class="QPushButton" name="buttonLStickDown">
697 <property name="text">
698 <string/>
699 </property>
700 </widget>
701 </item>
702 </layout>
703 </item>
704 <item row="3" column="2">
705 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
706 <item>
707 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
708 <item>
709 <widget class="QLabel" name="labelLStickMod">
710 <property name="text">
711 <string>Modifier:</string>
712 </property>
713 </widget>
714 </item>
715 </layout>
716 </item>
717 <item>
718 <widget class="QPushButton" name="buttonLStickMod">
719 <property name="text">
720 <string/>
721 </property>
722 </widget>
723 </item>
724 </layout>
725 </item>
726 <item row="3" column="1">
727 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
728 <item>
729 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
730 <item>
731 <widget class="QLabel" name="labelLStickPressed">
732 <property name="text">
733 <string>Pressed:</string>
734 </property>
735 </widget>
736 </item>
737 </layout>
738 </item>
739 <item>
740 <widget class="QPushButton" name="buttonLStick">
741 <property name="text">
742 <string/>
743 </property>
744 </widget>
745 </item>
746 </layout>
747 </item>
748 </layout>
749 </widget>
750 </item>
751 <item row="3" column="0">
752 <widget class="QGroupBox" name="shoulderButtons">
753 <property name="title">
754 <string>Shoulder Buttons</string>
755 </property>
756 <property name="flat">
757 <bool>false</bool>
758 </property>
759 <property name="checkable">
760 <bool>false</bool>
761 </property>
762 <layout class="QGridLayout" name="gridLayout_3">
763 <item row="3" column="0">
764 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
765 <item>
766 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
767 <item>
768 <widget class="QLabel" name="labelSL">
769 <property name="text">
770 <string>SL:</string>
771 </property>
772 </widget>
773 </item>
774 </layout>
775 </item>
776 <item>
777 <widget class="QPushButton" name="buttonSL">
778 <property name="text">
779 <string/>
780 </property>
781 </widget>
782 </item>
783 </layout>
784 </item>
785 <item row="2" column="1">
786 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
787 <item>
788 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
789 <item>
790 <widget class="QLabel" name="labelZR">
791 <property name="text">
792 <string>ZR:</string>
793 </property>
794 </widget>
795 </item>
796 </layout>
797 </item>
798 <item>
799 <widget class="QPushButton" name="buttonZR">
800 <property name="text">
801 <string/>
802 </property>
803 </widget>
804 </item>
805 </layout>
806 </item>
807 <item row="3" column="1">
808 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
809 <item>
810 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
811 <item>
812 <widget class="QLabel" name="labelSR">
813 <property name="text">
814 <string>SR:</string>
815 </property>
816 </widget>
817 </item>
818 </layout>
819 </item>
820 <item>
821 <widget class="QPushButton" name="buttonSR">
822 <property name="text">
823 <string/>
824 </property>
825 </widget>
826 </item>
827 </layout>
828 </item>
829 <item row="0" column="1">
830 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
831 <item>
832 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
833 <item>
834 <widget class="QLabel" name="labelZL">
835 <property name="text">
836 <string>ZL:</string>
837 </property>
838 </widget>
839 </item>
840 </layout>
841 </item>
842 <item>
843 <widget class="QPushButton" name="buttonZL">
844 <property name="text">
845 <string/>
846 </property>
847 </widget>
848 </item>
849 </layout>
850 </item>
851 <item row="0" column="0">
852 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
853 <item>
854 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
855 <item>
856 <widget class="QLabel" name="labelL">
857 <property name="text">
858 <string>L:</string>
859 </property>
860 </widget>
861 </item>
862 </layout>
863 </item>
864 <item>
865 <widget class="QPushButton" name="buttonL">
866 <property name="text">
867 <string/>
868 </property>
869 </widget>
870 </item>
871 </layout>
872 </item>
873 <item row="2" column="0">
874 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
875 <item>
876 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
877 <item>
878 <widget class="QLabel" name="labelR">
879 <property name="text">
880 <string>R:</string>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item>
887 <widget class="QPushButton" name="buttonR">
888 <property name="text">
889 <string/>
890 </property>
891 </widget>
892 </item>
893 </layout>
894 </item>
895 </layout>
896 </widget>
897 </item>
898 <item row="3" column="1">
899 <widget class="QGroupBox" name="misc">
900 <property name="title">
901 <string>Misc.</string>
902 </property>
903 <property name="flat">
904 <bool>false</bool>
905 </property>
906 <property name="checkable">
907 <bool>false</bool>
908 </property>
909 <layout class="QGridLayout" name="gridLayout_6">
910 <item row="1" column="0">
911 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
912 <item>
913 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
914 <item>
915 <widget class="QLabel" name="labelMinus">
916 <property name="text">
917 <string>Minus:</string>
918 </property>
919 </widget>
920 </item>
921 </layout>
922 </item>
923 <item>
924 <widget class="QPushButton" name="buttonMinus">
925 <property name="text">
926 <string/>
927 </property>
928 </widget>
929 </item>
930 </layout>
931 </item>
932 <item row="3" column="1">
933 <spacer name="verticalSpacer_2">
934 <property name="orientation">
935 <enum>Qt::Vertical</enum>
936 </property>
937 <property name="sizeHint" stdset="0">
938 <size>
939 <width>20</width>
940 <height>40</height>
941 </size>
942 </property>
943 </spacer>
944 </item>
945 <item row="0" column="0">
946 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
947 <item>
948 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
949 <item>
950 <widget class="QLabel" name="labelPlus">
951 <property name="text">
952 <string>Plus:</string>
953 </property>
954 </widget>
955 </item>
956 </layout>
957 </item>
958 <item>
959 <widget class="QPushButton" name="buttonPlus">
960 <property name="text">
961 <string/>
962 </property>
963 </widget>
964 </item>
965 </layout>
966 </item>
967 <item row="0" column="1">
968 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
969 <item>
970 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
971 <item>
972 <widget class="QLabel" name="labelHome">
973 <property name="text">
974 <string>Home:</string>
975 </property>
976 </widget>
977 </item>
978 </layout>
979 </item>
980 <item>
981 <widget class="QPushButton" name="buttonHome">
982 <property name="text">
983 <string/>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </item>
989 <item row="1" column="1">
990 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
991 <item>
992 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
993 <item>
994 <widget class="QLabel" name="labelScreenshot">
995 <property name="text">
996 <string>Screen Capture:</string>
997 </property>
998 <property name="wordWrap">
999 <bool>false</bool>
1000 </property>
1001 </widget>
1002 </item>
1003 </layout>
1004 </item>
1005 <item>
1006 <widget class="QPushButton" name="buttonScreenshot">
1007 <property name="sizePolicy">
1008 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1009 <horstretch>0</horstretch>
1010 <verstretch>0</verstretch>
1011 </sizepolicy>
1012 </property>
1013 <property name="maximumSize">
1014 <size>
1015 <width>80</width>
1016 <height>16777215</height>
1017 </size>
1018 </property>
1019 <property name="text">
1020 <string/>
1021 </property>
1022 </widget>
1023 </item>
1024 </layout>
1025 </item>
1026 </layout>
1027 </widget>
1028 </item>
1029 </layout>
1030 </item>
1031 <item>
1032 <spacer name="verticalSpacer">
1033 <property name="orientation">
1034 <enum>Qt::Vertical</enum>
1035 </property>
1036 <property name="sizeHint" stdset="0">
1037 <size>
1038 <width>20</width>
1039 <height>40</height>
1040 </size>
1041 </property>
1042 </spacer>
1043 </item>
1044 <item>
1045 <layout class="QHBoxLayout" name="horizontalLayout"/>
1046 </item>
1047 <item>
1048 <layout class="QHBoxLayout" name="horizontalLayout_2">
1049 <item>
1050 <widget class="QPushButton" name="buttonClearAll">
1051 <property name="sizePolicy">
1052 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1053 <horstretch>0</horstretch>
1054 <verstretch>0</verstretch>
1055 </sizepolicy>
1056 </property>
1057 <property name="sizeIncrement">
1058 <size>
1059 <width>0</width>
1060 <height>0</height>
1061 </size>
1062 </property>
1063 <property name="baseSize">
1064 <size>
1065 <width>0</width>
1066 <height>0</height>
1067 </size>
1068 </property>
1069 <property name="layoutDirection">
1070 <enum>Qt::LeftToRight</enum>
1071 </property>
1072 <property name="text">
1073 <string>Clear All</string>
1074 </property>
1075 </widget>
1076 </item>
1077 <item>
1078 <widget class="QPushButton" name="buttonRestoreDefaults">
1079 <property name="sizePolicy">
1080 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1081 <horstretch>0</horstretch>
1082 <verstretch>0</verstretch>
1083 </sizepolicy>
1084 </property>
1085 <property name="sizeIncrement">
1086 <size>
1087 <width>0</width>
1088 <height>0</height>
1089 </size>
1090 </property>
1091 <property name="baseSize">
1092 <size>
1093 <width>0</width>
1094 <height>0</height>
1095 </size>
1096 </property>
1097 <property name="layoutDirection">
1098 <enum>Qt::LeftToRight</enum>
1099 </property>
1100 <property name="text">
1101 <string>Restore Defaults</string>
1102 </property>
1103 </widget>
1104 </item>
1105 <item>
1106 <spacer name="horizontalSpacer">
1107 <property name="orientation">
1108 <enum>Qt::Horizontal</enum>
1109 </property>
1110 <property name="sizeHint" stdset="0">
1111 <size>
1112 <width>40</width>
1113 <height>20</height>
1114 </size>
1115 </property>
1116 </spacer>
1117 </item>
1118 <item>
1119 <widget class="QDialogButtonBox" name="buttonBox">
1120 <property name="standardButtons">
1121 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1122 </property>
1123 </widget>
1124 </item>
1125 </layout>
1126 </item>
1127 </layout>
1128 </widget>
1129 <resources/>
1130 <connections>
1131 <connection>
1132 <sender>buttonBox</sender>
1133 <signal>accepted()</signal>
1134 <receiver>ConfigureInputPlayer</receiver>
1135 <slot>accept()</slot>
1136 <hints>
1137 <hint type="sourcelabel">
1138 <x>371</x>
1139 <y>730</y>
1140 </hint>
1141 <hint type="destinationlabel">
1142 <x>229</x>
1143 <y>375</y>
1144 </hint>
1145 </hints>
1146 </connection>
1147 <connection>
1148 <sender>buttonBox</sender>
1149 <signal>rejected()</signal>
1150 <receiver>ConfigureInputPlayer</receiver>
1151 <slot>reject()</slot>
1152 <hints>
1153 <hint type="sourcelabel">
1154 <x>371</x>
1155 <y>730</y>
1156 </hint>
1157 <hint type="destinationlabel">
1158 <x>229</x>
1159 <y>375</y>
1160 </hint>
1161 </hints>
1162 </connection>
1163 </connections>
1164</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
new file mode 100644
index 000000000..dab58fbaa
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -0,0 +1,213 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QKeyEvent>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_mouse_advanced.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_mouse_advanced.h"
18
19static QString GetKeyName(int key_code) {
20 switch (key_code) {
21 case Qt::Key_Shift:
22 return QObject::tr("Shift");
23 case Qt::Key_Control:
24 return QObject::tr("Ctrl");
25 case Qt::Key_Alt:
26 return QObject::tr("Alt");
27 case Qt::Key_Meta:
28 return "";
29 default:
30 return QKeySequence(key_code).toString();
31 }
32}
33
34static QString ButtonToText(const Common::ParamPackage& param) {
35 if (!param.Has("engine")) {
36 return QObject::tr("[not set]");
37 } else if (param.Get("engine", "") == "keyboard") {
38 return GetKeyName(param.Get("code", 0));
39 } else if (param.Get("engine", "") == "sdl") {
40 if (param.Has("hat")) {
41 return QString(QObject::tr("Hat %1 %2"))
42 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
43 }
44 if (param.Has("axis")) {
45 return QString(QObject::tr("Axis %1%2"))
46 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
47 }
48 if (param.Has("button")) {
49 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
50 }
51 return QString();
52 } else {
53 return QObject::tr("[unknown]");
54 }
55}
56
57ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
58 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
59 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
60 ui->setupUi(this);
61 setFocusPolicy(Qt::ClickFocus);
62
63 button_map = {
64 ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
65 };
66
67 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
68 if (!button_map[button_id])
69 continue;
70 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
71 connect(button_map[button_id], &QPushButton::released, [=]() {
72 handleClick(
73 button_map[button_id],
74 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
75 InputCommon::Polling::DeviceType::Button);
76 });
77 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
78 [=](const QPoint& menu_location) {
79 QMenu context_menu;
80 context_menu.addAction(tr("Clear"), [&] {
81 buttons_param[button_id].Clear();
82 button_map[button_id]->setText(tr("[not set]"));
83 });
84 context_menu.addAction(tr("Restore Default"), [&] {
85 buttons_param[button_id] =
86 Common::ParamPackage{InputCommon::GenerateKeyboardParam(
87 Config::default_mouse_buttons[button_id])};
88 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
89 });
90 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
91 });
92 }
93
94 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
95 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
96
97 timeout_timer->setSingleShot(true);
98 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
99
100 connect(poll_timer.get(), &QTimer::timeout, [this]() {
101 Common::ParamPackage params;
102 for (auto& poller : device_pollers) {
103 params = poller->GetNextInput();
104 if (params.Has("engine")) {
105 setPollingResult(params, false);
106 return;
107 }
108 }
109 });
110
111 loadConfiguration();
112 resize(0, 0);
113}
114
115ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
116
117void ConfigureMouseAdvanced::applyConfiguration() {
118 std::transform(buttons_param.begin(), buttons_param.end(),
119 Settings::values.mouse_buttons.begin(),
120 [](const Common::ParamPackage& param) { return param.Serialize(); });
121}
122
123void ConfigureMouseAdvanced::loadConfiguration() {
124 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
125 buttons_param.begin(),
126 [](const std::string& str) { return Common::ParamPackage(str); });
127 updateButtonLabels();
128}
129
130void ConfigureMouseAdvanced::restoreDefaults() {
131 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
132 buttons_param[button_id] = Common::ParamPackage{
133 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
134 }
135
136 updateButtonLabels();
137}
138
139void ConfigureMouseAdvanced::ClearAll() {
140 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
141 if (button_map[i] && button_map[i]->isEnabled())
142 buttons_param[i].Clear();
143 }
144
145 updateButtonLabels();
146}
147
148void ConfigureMouseAdvanced::updateButtonLabels() {
149 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
150 button_map[button]->setText(ButtonToText(buttons_param[button]));
151 }
152}
153
154void ConfigureMouseAdvanced::handleClick(
155 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
156 InputCommon::Polling::DeviceType type) {
157 button->setText(tr("[press key]"));
158 button->setFocus();
159
160 const auto iter = std::find(button_map.begin(), button_map.end(), button);
161 ASSERT(iter != button_map.end());
162 const auto index = std::distance(button_map.begin(), iter);
163 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
164
165 input_setter = new_input_setter;
166
167 device_pollers = InputCommon::Polling::GetPollers(type);
168
169 // Keyboard keys can only be used as button devices
170 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
171
172 for (auto& poller : device_pollers) {
173 poller->Start();
174 }
175
176 grabKeyboard();
177 grabMouse();
178 timeout_timer->start(5000); // Cancel after 5 seconds
179 poll_timer->start(200); // Check for new inputs every 200ms
180}
181
182void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) {
183 releaseKeyboard();
184 releaseMouse();
185 timeout_timer->stop();
186 poll_timer->stop();
187 for (auto& poller : device_pollers) {
188 poller->Stop();
189 }
190
191 if (!abort) {
192 (*input_setter)(params);
193 }
194
195 updateButtonLabels();
196 input_setter = std::nullopt;
197}
198
199void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
200 if (!input_setter || !event)
201 return;
202
203 if (event->key() != Qt::Key_Escape) {
204 if (want_keyboard_keys) {
205 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
206 false);
207 } else {
208 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
209 return;
210 }
211 }
212 setPollingResult({}, true);
213}
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
new file mode 100644
index 000000000..218df2bda
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -0,0 +1,68 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9#include <QDialog>
10#include <QWidget>
11#include "core/settings.h"
12
13class QCheckBox;
14class QPushButton;
15class QTimer;
16
17namespace Ui {
18class ConfigureMouseAdvanced;
19}
20
21class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureMouseAdvanced(QWidget* parent);
26 ~ConfigureMouseAdvanced() override;
27
28 void applyConfiguration();
29
30private:
31 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
32
33 /// This will be the the setting function when an input is awaiting configuration.
34 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
35
36 std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
37 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
38
39 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
40
41 std::unique_ptr<QTimer> timeout_timer;
42 std::unique_ptr<QTimer> poll_timer;
43
44 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
45 /// keyboard events are ignored.
46 bool want_keyboard_keys = false;
47
48 /// Load configuration settings.
49 void loadConfiguration();
50 /// Restore all buttons to their default values.
51 void restoreDefaults();
52 /// Clear all input configuration
53 void ClearAll();
54
55 /// Update UI to reflect current configuration.
56 void updateButtonLabels();
57
58 /// Called when the button was pressed.
59 void handleClick(QPushButton* button,
60 std::function<void(const Common::ParamPackage&)> new_input_setter,
61 InputCommon::Polling::DeviceType type);
62
63 /// Finish polling and configure input using the input_setter
64 void setPollingResult(const Common::ParamPackage& params, bool abort);
65
66 /// Handle key press events.
67 void keyPressEvent(QKeyEvent* event) override;
68};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
new file mode 100644
index 000000000..08245ecf0
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -0,0 +1,261 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMouseAdvanced</class>
4 <widget class="QDialog" name="ConfigureMouseAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>250</width>
10 <height>261</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Mouse</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QGroupBox" name="gridGroupBox">
19 <property name="title">
20 <string>Mouse Buttons</string>
21 </property>
22 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4">
24 <spacer name="horizontalSpacer_2">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3">
43 <item>
44 <widget class="QLabel" name="label_3">
45 <property name="text">
46 <string>Right:</string>
47 </property>
48 </widget>
49 </item>
50 </layout>
51 </item>
52 <item>
53 <widget class="QPushButton" name="right_button">
54 <property name="minimumSize">
55 <size>
56 <width>75</width>
57 <height>0</height>
58 </size>
59 </property>
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text">
99 <string/>
100 </property>
101 </widget>
102 </item>
103 </layout>
104 </item>
105 <item row="3" column="1">
106 <layout class="QVBoxLayout" name="verticalLayout_5">
107 <item>
108 <layout class="QHBoxLayout" name="horizontalLayout_4">
109 <item>
110 <widget class="QLabel" name="label_4">
111 <property name="minimumSize">
112 <size>
113 <width>54</width>
114 <height>0</height>
115 </size>
116 </property>
117 <property name="text">
118 <string>Back:</string>
119 </property>
120 </widget>
121 </item>
122 </layout>
123 </item>
124 <item>
125 <widget class="QPushButton" name="back_button">
126 <property name="text">
127 <string/>
128 </property>
129 </widget>
130 </item>
131 </layout>
132 </item>
133 <item row="0" column="1">
134 <layout class="QVBoxLayout" name="verticalLayout_2">
135 <item>
136 <layout class="QHBoxLayout" name="horizontalLayout">
137 <item>
138 <widget class="QLabel" name="label">
139 <property name="text">
140 <string>Left:</string>
141 </property>
142 </widget>
143 </item>
144 </layout>
145 </item>
146 <item>
147 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize">
149 <size>
150 <width>75</width>
151 <height>0</height>
152 </size>
153 </property>
154 <property name="text">
155 <string/>
156 </property>
157 </widget>
158 </item>
159 </layout>
160 </item>
161 <item row="3" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6">
163 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5">
165 <item>
166 <widget class="QLabel" name="label_5">
167 <property name="text">
168 <string>Forward:</string>
169 </property>
170 </widget>
171 </item>
172 </layout>
173 </item>
174 <item>
175 <widget class="QPushButton" name="forward_button">
176 <property name="text">
177 <string/>
178 </property>
179 </widget>
180 </item>
181 </layout>
182 </item>
183 </layout>
184 </widget>
185 </item>
186 <item>
187 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item>
189 <widget class="QPushButton" name="buttonClearAll">
190 <property name="text">
191 <string>Clear All</string>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults">
197 <property name="text">
198 <string>Restore Defaults</string>
199 </property>
200 </widget>
201 </item>
202 <item>
203 <spacer name="horizontalSpacer_3">
204 <property name="orientation">
205 <enum>Qt::Horizontal</enum>
206 </property>
207 <property name="sizeHint" stdset="0">
208 <size>
209 <width>40</width>
210 <height>20</height>
211 </size>
212 </property>
213 </spacer>
214 </item>
215 </layout>
216 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout>
225 </widget>
226 <resources/>
227 <connections>
228 <connection>
229 <sender>buttonBox</sender>
230 <signal>accepted()</signal>
231 <receiver>ConfigureMouseAdvanced</receiver>
232 <slot>accept()</slot>
233 <hints>
234 <hint type="sourcelabel">
235 <x>124</x>
236 <y>266</y>
237 </hint>
238 <hint type="destinationlabel">
239 <x>124</x>
240 <y>143</y>
241 </hint>
242 </hints>
243 </connection>
244 <connection>
245 <sender>buttonBox</sender>
246 <signal>rejected()</signal>
247 <receiver>ConfigureMouseAdvanced</receiver>
248 <slot>reject()</slot>
249 <hints>
250 <hint type="sourcelabel">
251 <x>124</x>
252 <y>266</y>
253 </hint>
254 <hint type="destinationlabel">
255 <x>124</x>
256 <y>143</y>
257 </hint>
258 </hints>
259 </connection>
260 </connections>
261</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index b4b4a4a56..ab5d46492 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -137,6 +137,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); 137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); 138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
139 139
140 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
141 ui->rng_seed_edit->setEnabled(checked);
142 if (!checked)
143 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
144 });
145
140 scene = new QGraphicsScene; 146 scene = new QGraphicsScene;
141 ui->current_user_icon->setScene(scene); 147 ui->current_user_icon->setScene(scene);
142 148
@@ -155,6 +161,13 @@ void ConfigureSystem::setConfiguration() {
155 161
156 PopulateUserList(); 162 PopulateUserList();
157 UpdateCurrentUser(); 163 UpdateCurrentUser();
164
165 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
166 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
167
168 const auto rng_seed =
169 QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper();
170 ui->rng_seed_edit->setText(rng_seed);
158} 171}
159 172
160void ConfigureSystem::PopulateUserList() { 173void ConfigureSystem::PopulateUserList() {
@@ -195,6 +208,12 @@ void ConfigureSystem::applyConfiguration() {
195 return; 208 return;
196 209
197 Settings::values.language_index = ui->combo_language->currentIndex(); 210 Settings::values.language_index = ui->combo_language->currentIndex();
211
212 if (ui->rng_seed_checkbox->isChecked())
213 Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
214 else
215 Settings::values.rng_seed = std::nullopt;
216
198 Settings::Apply(); 217 Settings::Apply();
199} 218}
200 219
@@ -240,7 +259,7 @@ void ConfigureSystem::RefreshConsoleID() {
240 259
241void ConfigureSystem::SelectUser(const QModelIndex& index) { 260void ConfigureSystem::SelectUser(const QModelIndex& index) {
242 Settings::values.current_user = 261 Settings::values.current_user =
243 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); 262 std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
244 263
245 UpdateCurrentUser(); 264 UpdateCurrentUser();
246 265
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 020b32a37..a91580893 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.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>360</width> 9 <width>366</width>
10 <height>483</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -22,98 +22,6 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="0">
26 <widget class="QLabel" name="label_language">
27 <property name="text">
28 <string>Language</string>
29 </property>
30 </widget>
31 </item>
32 <item row="0" column="0">
33 <widget class="QLabel" name="label_birthday">
34 <property name="text">
35 <string>Birthday</string>
36 </property>
37 </widget>
38 </item>
39 <item row="3" column="0">
40 <widget class="QLabel" name="label_console_id">
41 <property name="text">
42 <string>Console ID:</string>
43 </property>
44 </widget>
45 </item>
46 <item row="0" column="1">
47 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
48 <item>
49 <widget class="QComboBox" name="combo_birthmonth">
50 <item>
51 <property name="text">
52 <string>January</string>
53 </property>
54 </item>
55 <item>
56 <property name="text">
57 <string>February</string>
58 </property>
59 </item>
60 <item>
61 <property name="text">
62 <string>March</string>
63 </property>
64 </item>
65 <item>
66 <property name="text">
67 <string>April</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>May</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string>June</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>July</string>
83 </property>
84 </item>
85 <item>
86 <property name="text">
87 <string>August</string>
88 </property>
89 </item>
90 <item>
91 <property name="text">
92 <string>September</string>
93 </property>
94 </item>
95 <item>
96 <property name="text">
97 <string>October</string>
98 </property>
99 </item>
100 <item>
101 <property name="text">
102 <string>November</string>
103 </property>
104 </item>
105 <item>
106 <property name="text">
107 <string>December</string>
108 </property>
109 </item>
110 </widget>
111 </item>
112 <item>
113 <widget class="QComboBox" name="combo_birthday"/>
114 </item>
115 </layout>
116 </item>
117 <item row="1" column="1"> 25 <item row="1" column="1">
118 <widget class="QComboBox" name="combo_language"> 26 <widget class="QComboBox" name="combo_language">
119 <property name="toolTip"> 27 <property name="toolTip">
@@ -206,6 +114,13 @@
206 </item> 114 </item>
207 </widget> 115 </widget>
208 </item> 116 </item>
117 <item row="3" column="0">
118 <widget class="QLabel" name="label_console_id">
119 <property name="text">
120 <string>Console ID:</string>
121 </property>
122 </widget>
123 </item>
209 <item row="2" column="0"> 124 <item row="2" column="0">
210 <widget class="QLabel" name="label_sound"> 125 <widget class="QLabel" name="label_sound">
211 <property name="text"> 126 <property name="text">
@@ -213,6 +128,100 @@
213 </property> 128 </property>
214 </widget> 129 </widget>
215 </item> 130 </item>
131 <item row="0" column="0">
132 <widget class="QLabel" name="label_birthday">
133 <property name="text">
134 <string>Birthday</string>
135 </property>
136 </widget>
137 </item>
138 <item row="0" column="1">
139 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
140 <item>
141 <widget class="QComboBox" name="combo_birthmonth">
142 <item>
143 <property name="text">
144 <string>January</string>
145 </property>
146 </item>
147 <item>
148 <property name="text">
149 <string>February</string>
150 </property>
151 </item>
152 <item>
153 <property name="text">
154 <string>March</string>
155 </property>
156 </item>
157 <item>
158 <property name="text">
159 <string>April</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>May</string>
165 </property>
166 </item>
167 <item>
168 <property name="text">
169 <string>June</string>
170 </property>
171 </item>
172 <item>
173 <property name="text">
174 <string>July</string>
175 </property>
176 </item>
177 <item>
178 <property name="text">
179 <string>August</string>
180 </property>
181 </item>
182 <item>
183 <property name="text">
184 <string>September</string>
185 </property>
186 </item>
187 <item>
188 <property name="text">
189 <string>October</string>
190 </property>
191 </item>
192 <item>
193 <property name="text">
194 <string>November</string>
195 </property>
196 </item>
197 <item>
198 <property name="text">
199 <string>December</string>
200 </property>
201 </item>
202 </widget>
203 </item>
204 <item>
205 <widget class="QComboBox" name="combo_birthday"/>
206 </item>
207 </layout>
208 </item>
209 <item row="3" column="1">
210 <widget class="QPushButton" name="button_regenerate_console_id">
211 <property name="sizePolicy">
212 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
213 <horstretch>0</horstretch>
214 <verstretch>0</verstretch>
215 </sizepolicy>
216 </property>
217 <property name="layoutDirection">
218 <enum>Qt::RightToLeft</enum>
219 </property>
220 <property name="text">
221 <string>Regenerate</string>
222 </property>
223 </widget>
224 </item>
216 <item row="2" column="1"> 225 <item row="2" column="1">
217 <widget class="QComboBox" name="combo_sound"> 226 <widget class="QComboBox" name="combo_sound">
218 <item> 227 <item>
@@ -232,19 +241,38 @@
232 </item> 241 </item>
233 </widget> 242 </widget>
234 </item> 243 </item>
235 <item row="3" column="1"> 244 <item row="1" column="0">
236 <widget class="QPushButton" name="button_regenerate_console_id"> 245 <widget class="QLabel" name="label_language">
246 <property name="text">
247 <string>Language</string>
248 </property>
249 </widget>
250 </item>
251 <item row="4" column="0">
252 <widget class="QCheckBox" name="rng_seed_checkbox">
253 <property name="text">
254 <string>RNG Seed</string>
255 </property>
256 </widget>
257 </item>
258 <item row="4" column="1">
259 <widget class="QLineEdit" name="rng_seed_edit">
237 <property name="sizePolicy"> 260 <property name="sizePolicy">
238 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 261 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
239 <horstretch>0</horstretch> 262 <horstretch>0</horstretch>
240 <verstretch>0</verstretch> 263 <verstretch>0</verstretch>
241 </sizepolicy> 264 </sizepolicy>
242 </property> 265 </property>
243 <property name="layoutDirection"> 266 <property name="font">
244 <enum>Qt::RightToLeft</enum> 267 <font>
268 <family>Lucida Console</family>
269 </font>
245 </property> 270 </property>
246 <property name="text"> 271 <property name="inputMask">
247 <string>Regenerate</string> 272 <string notr="true">HHHHHHHH</string>
273 </property>
274 <property name="maxLength">
275 <number>8</number>
248 </property> 276 </property>
249 </widget> 277 </widget>
250 </item> 278 </item>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
new file mode 100644
index 000000000..9c1561e9d
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
@@ -0,0 +1,42 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "ui_configure_touchscreen_advanced.h"
7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_touchscreen_advanced.h"
9
10ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
11 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) {
12 ui->setupUi(this);
13
14 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
15 &ConfigureTouchscreenAdvanced::restoreDefaults);
16
17 loadConfiguration();
18 resize(0, 0);
19}
20
21ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
22
23void ConfigureTouchscreenAdvanced::applyConfiguration() {
24 Settings::values.touchscreen.finger = ui->finger_box->value();
25 Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
26 Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
27 Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
28}
29
30void ConfigureTouchscreenAdvanced::loadConfiguration() {
31 ui->finger_box->setValue(Settings::values.touchscreen.finger);
32 ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
33 ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
34 ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
35}
36
37void ConfigureTouchscreenAdvanced::restoreDefaults() {
38 ui->finger_box->setValue(0);
39 ui->diameter_x_box->setValue(15);
40 ui->diameter_y_box->setValue(15);
41 ui->angle_box->setValue(0);
42}
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
new file mode 100644
index 000000000..41cd255fb
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -0,0 +1,32 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9#include <QWidget>
10#include "yuzu/configuration/config.h"
11
12namespace Ui {
13class ConfigureTouchscreenAdvanced;
14}
15
16class ConfigureTouchscreenAdvanced : public QDialog {
17 Q_OBJECT
18
19public:
20 explicit ConfigureTouchscreenAdvanced(QWidget* parent);
21 ~ConfigureTouchscreenAdvanced() override;
22
23 void applyConfiguration();
24
25private:
26 /// Load configuration settings.
27 void loadConfiguration();
28 /// Restore all buttons to their default values.
29 void restoreDefaults();
30
31 std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
32};
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui
new file mode 100644
index 000000000..1171c2dd1
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui
@@ -0,0 +1,199 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureTouchscreenAdvanced</class>
4 <widget class="QDialog" name="ConfigureTouchscreenAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Touchscreen</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>20</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="gridGroupBox">
51 <property name="title">
52 <string>Touch Parameters</string>
53 </property>
54 <layout class="QGridLayout" name="gridLayout">
55 <item row="0" column="0">
56 <spacer name="horizontalSpacer">
57 <property name="orientation">
58 <enum>Qt::Horizontal</enum>
59 </property>
60 <property name="sizeHint" stdset="0">
61 <size>
62 <width>40</width>
63 <height>20</height>
64 </size>
65 </property>
66 </spacer>
67 </item>
68 <item row="2" column="1">
69 <widget class="QLabel" name="label_4">
70 <property name="text">
71 <string>Touch Diameter Y</string>
72 </property>
73 </widget>
74 </item>
75 <item row="0" column="1">
76 <widget class="QLabel" name="label">
77 <property name="text">
78 <string>Finger</string>
79 </property>
80 </widget>
81 </item>
82 <item row="0" column="3">
83 <spacer name="horizontalSpacer_2">
84 <property name="orientation">
85 <enum>Qt::Horizontal</enum>
86 </property>
87 <property name="sizeHint" stdset="0">
88 <size>
89 <width>40</width>
90 <height>20</height>
91 </size>
92 </property>
93 </spacer>
94 </item>
95 <item row="1" column="1">
96 <widget class="QLabel" name="label_3">
97 <property name="text">
98 <string>Touch Diameter X</string>
99 </property>
100 </widget>
101 </item>
102 <item row="0" column="2">
103 <widget class="QSpinBox" name="finger_box">
104 <property name="minimumSize">
105 <size>
106 <width>80</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="3" column="1">
113 <widget class="QLabel" name="label_5">
114 <property name="text">
115 <string>Rotational Angle</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="2">
120 <widget class="QSpinBox" name="diameter_x_box"/>
121 </item>
122 <item row="2" column="2">
123 <widget class="QSpinBox" name="diameter_y_box"/>
124 </item>
125 <item row="3" column="2">
126 <widget class="QSpinBox" name="angle_box"/>
127 </item>
128 </layout>
129 </widget>
130 </item>
131 <item>
132 <spacer name="verticalSpacer">
133 <property name="orientation">
134 <enum>Qt::Vertical</enum>
135 </property>
136 <property name="sizeHint" stdset="0">
137 <size>
138 <width>20</width>
139 <height>40</height>
140 </size>
141 </property>
142 </spacer>
143 </item>
144 <item>
145 <layout class="QHBoxLayout" name="horizontalLayout">
146 <item>
147 <widget class="QPushButton" name="restore_defaults_button">
148 <property name="text">
149 <string>Restore Defaults</string>
150 </property>
151 </widget>
152 </item>
153 <item>
154 <widget class="QDialogButtonBox" name="buttonBox">
155 <property name="standardButtons">
156 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
157 </property>
158 </widget>
159 </item>
160 </layout>
161 </item>
162 </layout>
163 </widget>
164 <resources/>
165 <connections>
166 <connection>
167 <sender>buttonBox</sender>
168 <signal>accepted()</signal>
169 <receiver>ConfigureTouchscreenAdvanced</receiver>
170 <slot>accept()</slot>
171 <hints>
172 <hint type="sourcelabel">
173 <x>140</x>
174 <y>318</y>
175 </hint>
176 <hint type="destinationlabel">
177 <x>140</x>
178 <y>169</y>
179 </hint>
180 </hints>
181 </connection>
182 <connection>
183 <sender>buttonBox</sender>
184 <signal>rejected()</signal>
185 <receiver>ConfigureTouchscreenAdvanced</receiver>
186 <slot>reject()</slot>
187 <hints>
188 <hint type="sourcelabel">
189 <x>140</x>
190 <y>318</y>
191 </hint>
192 <hint type="destinationlabel">
193 <x>140</x>
194 <y>169</y>
195 </hint>
196 </hints>
197 </connection>
198 </connections>
199</ui>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 0adbab27d..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -386,9 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
390 Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format), 390 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
391 surface_width, surface_height, 1U); 391 surface_height, 1U);
392 392
393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
394 surface_width, surface_height); 394 surface_width, surface_height);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index a5a4aa432..11a8c390b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -215,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
215 tree_view->setUniformRowHeights(true); 215 tree_view->setUniformRowHeights(true);
216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
217 217
218 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); 221
222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); 222 if (UISettings::values.show_add_ons) {
223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); 223 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
224 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
225 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
226 } else {
227 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
228 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
229 }
224 230
225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 231 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 232 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -394,6 +400,25 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
394 } 400 }
395 401
396 tree_view->setEnabled(false); 402 tree_view->setEnabled(false);
403
404 // Update the columns in case UISettings has changed
405 item_model->removeColumns(0, item_model->columnCount());
406 item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
407 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
408 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
409
410 if (UISettings::values.show_add_ons) {
411 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
412 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
413 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
414 } else {
415 item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
416 item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
417 item_model->removeColumns(COLUMN_COUNT - 1, 1);
418 }
419
420 LoadInterfaceLayout();
421
397 // Delete any rows that might already exist if we're repopulating 422 // Delete any rows that might already exist if we're repopulating
398 item_model->removeRows(0, item_model->rowCount()); 423 item_model->removeRows(0, item_model->rowCount());
399 424
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 3d865a12d..362902e46 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -123,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
123 if (it != compatibility_list.end()) 123 if (it != compatibility_list.end())
124 compatibility = it->second.first; 124 compatibility = it->second.first;
125 125
126 emit EntryReady({ 126 QList<QStandardItem*> list{
127 new GameListItemPath( 127 new GameListItemPath(
128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name), 128 FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
130 program_id), 130 program_id),
131 new GameListItemCompat(compatibility), 131 new GameListItemCompat(compatibility),
132 new GameListItem(FormatPatchNameVersions(patch, *loader)),
133 new GameListItem( 132 new GameListItem(
134 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 133 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
135 new GameListItemSize(file->GetSize()), 134 new GameListItemSize(file->GetSize()),
136 }); 135 };
136
137 if (UISettings::values.show_add_ons) {
138 list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
139 }
140
141 emit EntryReady(list);
137 } 142 }
138 143
139 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application, 144 const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
@@ -216,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
216 if (it != compatibility_list.end()) 221 if (it != compatibility_list.end())
217 compatibility = it->second.first; 222 compatibility = it->second.first;
218 223
219 emit EntryReady({ 224 QList<QStandardItem*> list{
220 new GameListItemPath( 225 new GameListItemPath(
221 FormatGameName(physical_name), icon, QString::fromStdString(name), 226 FormatGameName(physical_name), icon, QString::fromStdString(name),
222 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 227 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
223 program_id), 228 program_id),
224 new GameListItemCompat(compatibility), 229 new GameListItemCompat(compatibility),
225 new GameListItem( 230 new GameListItem(
226 FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
227 new GameListItem(
228 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 231 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
229 new GameListItemSize(FileUtil::GetSize(physical_name)), 232 new GameListItemSize(FileUtil::GetSize(physical_name)),
230 }); 233 };
234
235 if (UISettings::values.show_add_ons) {
236 list.insert(2, new GameListItem(FormatPatchNameVersions(
237 patch, *loader, loader->IsRomFSUpdatable())));
238 }
239
240 emit EntryReady(std::move(list));
231 } else if (is_dir && recursion > 0) { 241 } else if (is_dir && recursion > 0) {
232 watch_list.append(QString::fromStdString(physical_name)); 242 watch_list.append(QString::fromStdString(physical_name));
233 AddFstEntriesToGameList(physical_name, recursion - 1); 243 AddFstEntriesToGameList(physical_name, recursion - 1);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c5a56cbfd..9c6d150a5 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,11 +8,13 @@
8#include <thread> 8#include <thread>
9 9
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "applets/software_keyboard.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h" 13#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
15#include "core/hle/service/am/applets/applets.h"
14 16
15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 17// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
16// defines. 18// defines.
17static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( 19static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
18 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { 20 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -59,6 +61,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
59#include "core/file_sys/romfs.h" 61#include "core/file_sys/romfs.h"
60#include "core/file_sys/savedata_factory.h" 62#include "core/file_sys/savedata_factory.h"
61#include "core/file_sys/submission_package.h" 63#include "core/file_sys/submission_package.h"
64#include "core/frontend/applets/software_keyboard.h"
62#include "core/hle/kernel/process.h" 65#include "core/hle/kernel/process.h"
63#include "core/hle/service/filesystem/filesystem.h" 66#include "core/hle/service/filesystem/filesystem.h"
64#include "core/hle/service/filesystem/fsp_ldr.h" 67#include "core/hle/service/filesystem/fsp_ldr.h"
@@ -142,6 +145,9 @@ static void InitializeLogging() {
142 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 145 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
143 FileUtil::CreateFullPath(log_dir); 146 FileUtil::CreateFullPath(log_dir);
144 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 147 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
148#ifdef _WIN32
149 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
150#endif
145} 151}
146 152
147GMainWindow::GMainWindow() 153GMainWindow::GMainWindow()
@@ -201,6 +207,27 @@ GMainWindow::~GMainWindow() {
201 delete render_window; 207 delete render_window;
202} 208}
203 209
210void GMainWindow::SoftwareKeyboardGetText(
211 const Core::Frontend::SoftwareKeyboardParameters& parameters) {
212 QtSoftwareKeyboardDialog dialog(this, parameters);
213 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
214 Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
215 dialog.setWindowModality(Qt::WindowModal);
216 dialog.exec();
217
218 if (!dialog.GetStatus()) {
219 emit SoftwareKeyboardFinishedText(std::nullopt);
220 return;
221 }
222
223 emit SoftwareKeyboardFinishedText(dialog.GetText());
224}
225
226void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
227 QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
228 emit SoftwareKeyboardFinishedCheckDialog();
229}
230
204void GMainWindow::InitializeWidgets() { 231void GMainWindow::InitializeWidgets() {
205#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 232#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
206 ui.action_Report_Compatibility->setVisible(true); 233 ui.action_Report_Compatibility->setVisible(true);
@@ -305,6 +332,8 @@ void GMainWindow::InitializeHotkeys() {
305 Qt::ApplicationShortcut); 332 Qt::ApplicationShortcut);
306 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), 333 hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
307 Qt::ApplicationShortcut); 334 Qt::ApplicationShortcut);
335 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
336 Qt::ApplicationShortcut);
308 hotkey_registry.LoadHotkeys(); 337 hotkey_registry.LoadHotkeys();
309 338
310 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 339 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -358,6 +387,12 @@ void GMainWindow::InitializeHotkeys() {
358 UpdateStatusBar(); 387 UpdateStatusBar();
359 } 388 }
360 }); 389 });
390 connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
391 this, [&] {
392 if (ui.action_Load_Amiibo->isEnabled()) {
393 OnLoadAmiibo();
394 }
395 });
361} 396}
362 397
363void GMainWindow::SetDefaultUIGeometry() { 398void GMainWindow::SetDefaultUIGeometry() {
@@ -454,6 +489,7 @@ void GMainWindow::ConnectMenuEvents() {
454 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 489 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
455 490
456 // Help 491 // Help
492 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
457 connect(ui.action_Rederive, &QAction::triggered, this, 493 connect(ui.action_Rederive, &QAction::triggered, this,
458 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning)); 494 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
459 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 495 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -482,32 +518,18 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
482QStringList GMainWindow::GetUnsupportedGLExtensions() { 518QStringList GMainWindow::GetUnsupportedGLExtensions() {
483 QStringList unsupported_ext; 519 QStringList unsupported_ext;
484 520
485 if (!GLAD_GL_ARB_program_interface_query)
486 unsupported_ext.append("ARB_program_interface_query");
487 if (!GLAD_GL_ARB_separate_shader_objects)
488 unsupported_ext.append("ARB_separate_shader_objects");
489 if (!GLAD_GL_ARB_vertex_attrib_binding)
490 unsupported_ext.append("ARB_vertex_attrib_binding");
491 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 521 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
492 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev"); 522 unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
493 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 523 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
494 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge"); 524 unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
495 if (!GLAD_GL_ARB_base_instance)
496 unsupported_ext.append("ARB_base_instance");
497 if (!GLAD_GL_ARB_texture_storage)
498 unsupported_ext.append("ARB_texture_storage");
499 if (!GLAD_GL_ARB_multi_bind) 525 if (!GLAD_GL_ARB_multi_bind)
500 unsupported_ext.append("ARB_multi_bind"); 526 unsupported_ext.append("ARB_multi_bind");
501 if (!GLAD_GL_ARB_copy_image)
502 unsupported_ext.append("ARB_copy_image");
503 527
504 // Extensions required to support some texture formats. 528 // Extensions required to support some texture formats.
505 if (!GLAD_GL_EXT_texture_compression_s3tc) 529 if (!GLAD_GL_EXT_texture_compression_s3tc)
506 unsupported_ext.append("EXT_texture_compression_s3tc"); 530 unsupported_ext.append("EXT_texture_compression_s3tc");
507 if (!GLAD_GL_ARB_texture_compression_rgtc) 531 if (!GLAD_GL_ARB_texture_compression_rgtc)
508 unsupported_ext.append("ARB_texture_compression_rgtc"); 532 unsupported_ext.append("ARB_texture_compression_rgtc");
509 if (!GLAD_GL_ARB_texture_compression_bptc)
510 unsupported_ext.append("ARB_texture_compression_bptc");
511 if (!GLAD_GL_ARB_depth_buffer_float) 533 if (!GLAD_GL_ARB_depth_buffer_float)
512 unsupported_ext.append("ARB_depth_buffer_float"); 534 unsupported_ext.append("ARB_depth_buffer_float");
513 535
@@ -526,8 +548,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
526 render_window->MakeCurrent(); 548 render_window->MakeCurrent();
527 549
528 if (!gladLoadGL()) { 550 if (!gladLoadGL()) {
529 QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"), 551 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
530 tr("Your GPU may not support OpenGL 3.3, or you do not " 552 tr("Your GPU may not support OpenGL 4.3, or you do not "
531 "have the latest graphics driver.")); 553 "have the latest graphics driver."));
532 return false; 554 return false;
533 } 555 }
@@ -547,6 +569,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
547 569
548 system.SetGPUDebugContext(debug_context); 570 system.SetGPUDebugContext(debug_context);
549 571
572 system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
573
550 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 574 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
551 575
552 const auto drd_callout = 576 const auto drd_callout =
@@ -929,7 +953,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
929 const auto full = res == "Full"; 953 const auto full = res == "Full";
930 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 954 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
931 955
932 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); 956 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
957 static_cast<s32>(entry_size), this);
933 progress.setWindowModality(Qt::WindowModal); 958 progress.setWindowModality(Qt::WindowModal);
934 progress.setMinimumDuration(100); 959 progress.setMinimumDuration(100);
935 960
@@ -1215,8 +1240,13 @@ void GMainWindow::OnMenuRecentFile() {
1215 1240
1216void GMainWindow::OnStartGame() { 1241void GMainWindow::OnStartGame() {
1217 emu_thread->SetRunning(true); 1242 emu_thread->SetRunning(true);
1243
1244 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
1245 "Core::Frontend::SoftwareKeyboardParameters");
1218 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); 1246 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
1219 qRegisterMetaType<std::string>("std::string"); 1247 qRegisterMetaType<std::string>("std::string");
1248 qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
1249
1220 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); 1250 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
1221 1251
1222 ui.action_Start->setEnabled(false); 1252 ui.action_Start->setEnabled(false);
@@ -1328,7 +1358,13 @@ void GMainWindow::OnConfigure() {
1328 UpdateUITheme(); 1358 UpdateUITheme();
1329 if (UISettings::values.enable_discord_presence != old_discord_presence) 1359 if (UISettings::values.enable_discord_presence != old_discord_presence)
1330 SetDiscordEnabled(UISettings::values.enable_discord_presence); 1360 SetDiscordEnabled(UISettings::values.enable_discord_presence);
1331 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1361
1362 const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
1363 if (reload) {
1364 game_list->PopulateAsync(UISettings::values.gamedir,
1365 UISettings::values.gamedir_deepscan);
1366 }
1367
1332 config->Save(); 1368 config->Save();
1333 } 1369 }
1334} 1370}
@@ -1374,6 +1410,11 @@ void GMainWindow::OnLoadAmiibo() {
1374 } 1410 }
1375} 1411}
1376 1412
1413void GMainWindow::OnOpenYuzuFolder() {
1414 QDesktopServices::openUrl(QUrl::fromLocalFile(
1415 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
1416}
1417
1377void GMainWindow::OnAbout() { 1418void GMainWindow::OnAbout() {
1378 AboutDialog aboutDialog(this); 1419 AboutDialog aboutDialog(this);
1379 aboutDialog.exec(); 1420 aboutDialog.exec();
@@ -1532,7 +1573,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1532 "derivation. It will be attempted but may not complete.<br><br>") + 1573 "derivation. It will be attempted but may not complete.<br><br>") +
1533 errors + 1574 errors +
1534 tr("<br><br>You can get all of these and dump all of your games easily by " 1575 tr("<br><br>You can get all of these and dump all of your games easily by "
1535 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " 1576 "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
1536 "quickstart guide</a>. Alternatively, you can use another method of dumping " 1577 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1537 "to obtain all of your keys.")); 1578 "to obtain all of your keys."));
1538 } 1579 }
@@ -1612,7 +1653,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
1612 return; 1653 return;
1613 } 1654 }
1614 1655
1615 if (ui.action_Fullscreen->isChecked()) { 1656 if (!ui.action_Fullscreen->isChecked()) {
1616 UISettings::values.geometry = saveGeometry(); 1657 UISettings::values.geometry = saveGeometry();
1617 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 1658 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
1618 } 1659 }
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index af637d89e..674e73412 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -29,6 +29,10 @@ class ProfilerWidget;
29class WaitTreeWidget; 29class WaitTreeWidget;
30enum class GameListOpenTarget; 30enum class GameListOpenTarget;
31 31
32namespace Core::Frontend {
33struct SoftwareKeyboardParameters;
34} // namespace Core::Frontend
35
32namespace FileSys { 36namespace FileSys {
33class RegisteredCacheUnion; 37class RegisteredCacheUnion;
34class VfsFilesystem; 38class VfsFilesystem;
@@ -95,6 +99,13 @@ signals:
95 // Signal that tells widgets to update icons to use the current theme 99 // Signal that tells widgets to update icons to use the current theme
96 void UpdateThemedIcons(); 100 void UpdateThemedIcons();
97 101
102 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
103 void SoftwareKeyboardFinishedCheckDialog();
104
105public slots:
106 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
107 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
108
98private: 109private:
99 void InitializeWidgets(); 110 void InitializeWidgets();
100 void InitializeDebugWidgets(); 111 void InitializeDebugWidgets();
@@ -167,6 +178,7 @@ private slots:
167 void OnMenuRecentFile(); 178 void OnMenuRecentFile();
168 void OnConfigure(); 179 void OnConfigure();
169 void OnLoadAmiibo(); 180 void OnLoadAmiibo();
181 void OnOpenYuzuFolder();
170 void OnAbout(); 182 void OnAbout();
171 void OnToggleFilterBar(); 183 void OnToggleFilterBar();
172 void OnDisplayTitleBars(bool); 184 void OnDisplayTitleBars(bool);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 48d099591..75e96387f 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -70,6 +70,8 @@
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/> 71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/> 72 <addaction name="separator"/>
73 <addaction name="action_Open_yuzu_Folder"/>
74 <addaction name="separator"/>
73 <addaction name="action_Exit"/> 75 <addaction name="action_Exit"/>
74 </widget> 76 </widget>
75 <widget class="QMenu" name="menu_Emulation"> 77 <widget class="QMenu" name="menu_Emulation">
@@ -277,6 +279,11 @@
277 <bool>false</bool> 279 <bool>false</bool>
278 </property> 280 </property>
279 </action> 281 </action>
282 <action name="action_Open_yuzu_Folder">
283 <property name="text">
284 <string>Open yuzu Folder</string>
285 </property>
286 </action>
280 </widget> 287 </widget>
281 <resources/> 288 <resources/>
282 <connections/> 289 <connections/>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 2e617d52a..e80aebc0a 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <vector> 9#include <vector>
9#include <QByteArray> 10#include <QByteArray>
10#include <QString> 11#include <QString>
@@ -59,9 +60,11 @@ struct Values {
59 60
60 // Game List 61 // Game List
61 bool show_unknown; 62 bool show_unknown;
63 bool show_add_ons;
62 uint32_t icon_size; 64 uint32_t icon_size;
63 uint8_t row_1_text_id; 65 uint8_t row_1_text_id;
64 uint8_t row_2_text_id; 66 uint8_t row_2_text_id;
67 std::atomic_bool is_game_list_reload_pending{false};
65}; 68};
66 69
67extern Values values; 70extern Values values;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index b456266a6..097c1fbe3 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -65,30 +65,271 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
65 }, 65 },
66}}; 66}};
67 67
68static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
69 SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
70 SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
71};
72
73static const std::array<int, 0x8A> keyboard_keys = {
74 0,
75 0,
76 0,
77 0,
78 SDL_SCANCODE_A,
79 SDL_SCANCODE_B,
80 SDL_SCANCODE_C,
81 SDL_SCANCODE_D,
82 SDL_SCANCODE_E,
83 SDL_SCANCODE_F,
84 SDL_SCANCODE_G,
85 SDL_SCANCODE_H,
86 SDL_SCANCODE_I,
87 SDL_SCANCODE_J,
88 SDL_SCANCODE_K,
89 SDL_SCANCODE_L,
90 SDL_SCANCODE_M,
91 SDL_SCANCODE_N,
92 SDL_SCANCODE_O,
93 SDL_SCANCODE_P,
94 SDL_SCANCODE_Q,
95 SDL_SCANCODE_R,
96 SDL_SCANCODE_S,
97 SDL_SCANCODE_T,
98 SDL_SCANCODE_U,
99 SDL_SCANCODE_V,
100 SDL_SCANCODE_W,
101 SDL_SCANCODE_X,
102 SDL_SCANCODE_Y,
103 SDL_SCANCODE_Z,
104 SDL_SCANCODE_1,
105 SDL_SCANCODE_2,
106 SDL_SCANCODE_3,
107 SDL_SCANCODE_4,
108 SDL_SCANCODE_5,
109 SDL_SCANCODE_6,
110 SDL_SCANCODE_7,
111 SDL_SCANCODE_8,
112 SDL_SCANCODE_9,
113 SDL_SCANCODE_0,
114 SDL_SCANCODE_RETURN,
115 SDL_SCANCODE_ESCAPE,
116 SDL_SCANCODE_BACKSPACE,
117 SDL_SCANCODE_TAB,
118 SDL_SCANCODE_SPACE,
119 SDL_SCANCODE_MINUS,
120 SDL_SCANCODE_EQUALS,
121 SDL_SCANCODE_LEFTBRACKET,
122 SDL_SCANCODE_RIGHTBRACKET,
123 SDL_SCANCODE_BACKSLASH,
124 0,
125 SDL_SCANCODE_SEMICOLON,
126 SDL_SCANCODE_APOSTROPHE,
127 SDL_SCANCODE_GRAVE,
128 SDL_SCANCODE_COMMA,
129 SDL_SCANCODE_PERIOD,
130 SDL_SCANCODE_SLASH,
131 SDL_SCANCODE_CAPSLOCK,
132
133 SDL_SCANCODE_F1,
134 SDL_SCANCODE_F2,
135 SDL_SCANCODE_F3,
136 SDL_SCANCODE_F4,
137 SDL_SCANCODE_F5,
138 SDL_SCANCODE_F6,
139 SDL_SCANCODE_F7,
140 SDL_SCANCODE_F8,
141 SDL_SCANCODE_F9,
142 SDL_SCANCODE_F10,
143 SDL_SCANCODE_F11,
144 SDL_SCANCODE_F12,
145
146 0,
147 SDL_SCANCODE_SCROLLLOCK,
148 SDL_SCANCODE_PAUSE,
149 SDL_SCANCODE_INSERT,
150 SDL_SCANCODE_HOME,
151 SDL_SCANCODE_PAGEUP,
152 SDL_SCANCODE_DELETE,
153 SDL_SCANCODE_END,
154 SDL_SCANCODE_PAGEDOWN,
155 SDL_SCANCODE_RIGHT,
156 SDL_SCANCODE_LEFT,
157 SDL_SCANCODE_DOWN,
158 SDL_SCANCODE_UP,
159
160 SDL_SCANCODE_NUMLOCKCLEAR,
161 SDL_SCANCODE_KP_DIVIDE,
162 SDL_SCANCODE_KP_MULTIPLY,
163 SDL_SCANCODE_KP_MINUS,
164 SDL_SCANCODE_KP_PLUS,
165 SDL_SCANCODE_KP_ENTER,
166 SDL_SCANCODE_KP_1,
167 SDL_SCANCODE_KP_2,
168 SDL_SCANCODE_KP_3,
169 SDL_SCANCODE_KP_4,
170 SDL_SCANCODE_KP_5,
171 SDL_SCANCODE_KP_6,
172 SDL_SCANCODE_KP_7,
173 SDL_SCANCODE_KP_8,
174 SDL_SCANCODE_KP_9,
175 SDL_SCANCODE_KP_0,
176 SDL_SCANCODE_KP_PERIOD,
177
178 0,
179 0,
180 SDL_SCANCODE_POWER,
181 SDL_SCANCODE_KP_EQUALS,
182
183 SDL_SCANCODE_F13,
184 SDL_SCANCODE_F14,
185 SDL_SCANCODE_F15,
186 SDL_SCANCODE_F16,
187 SDL_SCANCODE_F17,
188 SDL_SCANCODE_F18,
189 SDL_SCANCODE_F19,
190 SDL_SCANCODE_F20,
191 SDL_SCANCODE_F21,
192 SDL_SCANCODE_F22,
193 SDL_SCANCODE_F23,
194 SDL_SCANCODE_F24,
195
196 0,
197 SDL_SCANCODE_HELP,
198 SDL_SCANCODE_MENU,
199 0,
200 0,
201 0,
202 0,
203 0,
204 0,
205 0,
206 0,
207 0,
208 0,
209 0,
210 0,
211 SDL_SCANCODE_KP_COMMA,
212 SDL_SCANCODE_KP_LEFTPAREN,
213 SDL_SCANCODE_KP_RIGHTPAREN,
214 0,
215 0,
216 0,
217 0,
218};
219
220static const std::array<int, 8> keyboard_mods{
221 SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
222 SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
223};
224
68void Config::ReadValues() { 225void Config::ReadValues() {
69 // Controls 226 // Controls
227 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
228 const auto group = fmt::format("ControlsP{}", p);
229 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
230 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
231 Settings::values.players[p].buttons[i] =
232 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
233 if (Settings::values.players[p].buttons[i].empty())
234 Settings::values.players[p].buttons[i] = default_param;
235 }
236
237 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
238 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
239 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
240 default_analogs[i][3], default_analogs[i][4], 0.5f);
241 Settings::values.players[p].analogs[i] =
242 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
243 if (Settings::values.players[p].analogs[i].empty())
244 Settings::values.players[p].analogs[i] = default_param;
245 }
246 }
247
248 Settings::values.mouse_enabled =
249 sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
250 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
251 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
252 Settings::values.mouse_buttons[i] = sdl2_config->Get(
253 "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
254 default_param);
255 if (Settings::values.mouse_buttons[i].empty())
256 Settings::values.mouse_buttons[i] = default_param;
257 }
258
259 Settings::values.motion_device = sdl2_config->Get(
260 "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
261
262 Settings::values.keyboard_enabled =
263 sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
264
265 Settings::values.debug_pad_enabled =
266 sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
70 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 267 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
71 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 268 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
72 Settings::values.buttons[i] = 269 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
73 sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param); 270 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
74 if (Settings::values.buttons[i].empty()) 271 default_param);
75 Settings::values.buttons[i] = default_param; 272 if (Settings::values.debug_pad_buttons[i].empty())
273 Settings::values.debug_pad_buttons[i] = default_param;
76 } 274 }
77 275
78 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 276 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
79 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 277 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
80 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 278 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
81 default_analogs[i][3], default_analogs[i][4], 0.5f); 279 default_analogs[i][3], default_analogs[i][4], 0.5f);
82 Settings::values.analogs[i] = 280 Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
83 sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param); 281 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
84 if (Settings::values.analogs[i].empty()) 282 default_param);
85 Settings::values.analogs[i] = default_param; 283 if (Settings::values.debug_pad_analogs[i].empty())
284 Settings::values.debug_pad_analogs[i] = default_param;
86 } 285 }
87 286
88 Settings::values.motion_device = sdl2_config->Get( 287 Settings::values.touchscreen.enabled =
89 "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); 288 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
90 Settings::values.touch_device = 289 Settings::values.touchscreen.device =
91 sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); 290 sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
291 Settings::values.touchscreen.finger =
292 sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
293 Settings::values.touchscreen.rotation_angle =
294 sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
295 Settings::values.touchscreen.diameter_x =
296 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
297 Settings::values.touchscreen.diameter_y =
298 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
299
300 std::transform(keyboard_keys.begin(), keyboard_keys.end(),
301 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
302 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
303 Settings::values.keyboard_keys.begin() +
304 Settings::NativeKeyboard::LeftControlKey,
305 InputCommon::GenerateKeyboardParam);
306 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
307 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
308
309 // Data Storage
310 Settings::values.use_virtual_sd =
311 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
312 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
313 sdl2_config->Get("Data Storage", "nand_directory",
314 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
315 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
316 sdl2_config->Get("Data Storage", "sdmc_directory",
317 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
318
319 // System
320 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
321 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
322 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
323
324 Settings::values.current_user = std::clamp<int>(
325 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
326
327 const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
328 if (enabled) {
329 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0);
330 } else {
331 Settings::values.rng_seed = std::nullopt;
332 }
92 333
93 // Core 334 // Core
94 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 335 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@@ -114,23 +355,7 @@ void Config::ReadValues() {
114 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 355 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
115 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); 356 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
116 357
117 // Data Storage 358 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
118 Settings::values.use_virtual_sd =
119 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
120 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
121 sdl2_config->Get("Data Storage", "nand_directory",
122 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
123 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
124 sdl2_config->Get("Data Storage", "sdmc_directory",
125 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
126
127 // System
128 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
129 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
130 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
131
132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
134 359
135 // Miscellaneous 360 // Miscellaneous
136 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 361 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
@@ -141,6 +366,8 @@ void Config::ReadValues() {
141 Settings::values.gdbstub_port = 366 Settings::values.gdbstub_port =
142 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 367 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
143 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 368 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
369 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
370 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
144 371
145 // Web Service 372 // Web Service
146 Settings::values.enable_telemetry = 373 Settings::values.enable_telemetry =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e0b223cd6..d73669f36 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -178,6 +178,11 @@ use_docked_mode =
178# 1 (default): Yes, 0 : No 178# 1 (default): Yes, 0 : No
179enable_nfc = 179enable_nfc =
180 180
181# Sets the seed for the RNG generator built into the switch
182# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
183rng_seed_enabled =
184rng_seed =
185
181# Sets the account username, max length is 32 characters 186# Sets the account username, max length is 32 characters
182# yuzu (default) 187# yuzu (default)
183username = yuzu 188username = yuzu
@@ -201,6 +206,10 @@ log_filter = *:Trace
201# Port for listening to GDB connections. 206# Port for listening to GDB connections.
202use_gdbstub=false 207use_gdbstub=false
203gdbstub_port=24689 208gdbstub_port=24689
209# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
210dump_exefs=false
211# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
212dump_nso=false
204 213
205[WebService] 214[WebService]
206# Whether or not to enable telemetry 215# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index a9ad92a80..2d6f8cced 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -111,32 +111,18 @@ void EmuWindow_SDL2::Fullscreen() {
111bool EmuWindow_SDL2::SupportsRequiredGLExtensions() { 111bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
112 std::vector<std::string> unsupported_ext; 112 std::vector<std::string> unsupported_ext;
113 113
114 if (!GLAD_GL_ARB_program_interface_query)
115 unsupported_ext.push_back("ARB_program_interface_query");
116 if (!GLAD_GL_ARB_separate_shader_objects)
117 unsupported_ext.push_back("ARB_separate_shader_objects");
118 if (!GLAD_GL_ARB_vertex_attrib_binding)
119 unsupported_ext.push_back("ARB_vertex_attrib_binding");
120 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) 114 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
121 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev"); 115 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
122 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) 116 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
123 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge"); 117 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
124 if (!GLAD_GL_ARB_base_instance)
125 unsupported_ext.push_back("ARB_base_instance");
126 if (!GLAD_GL_ARB_texture_storage)
127 unsupported_ext.push_back("ARB_texture_storage");
128 if (!GLAD_GL_ARB_multi_bind) 118 if (!GLAD_GL_ARB_multi_bind)
129 unsupported_ext.push_back("ARB_multi_bind"); 119 unsupported_ext.push_back("ARB_multi_bind");
130 if (!GLAD_GL_ARB_copy_image)
131 unsupported_ext.push_back("ARB_copy_image");
132 120
133 // Extensions required to support some texture formats. 121 // Extensions required to support some texture formats.
134 if (!GLAD_GL_EXT_texture_compression_s3tc) 122 if (!GLAD_GL_EXT_texture_compression_s3tc)
135 unsupported_ext.push_back("EXT_texture_compression_s3tc"); 123 unsupported_ext.push_back("EXT_texture_compression_s3tc");
136 if (!GLAD_GL_ARB_texture_compression_rgtc) 124 if (!GLAD_GL_ARB_texture_compression_rgtc)
137 unsupported_ext.push_back("ARB_texture_compression_rgtc"); 125 unsupported_ext.push_back("ARB_texture_compression_rgtc");
138 if (!GLAD_GL_ARB_texture_compression_bptc)
139 unsupported_ext.push_back("ARB_texture_compression_bptc");
140 if (!GLAD_GL_ARB_depth_buffer_float) 126 if (!GLAD_GL_ARB_depth_buffer_float)
141 unsupported_ext.push_back("ARB_depth_buffer_float"); 127 unsupported_ext.push_back("ARB_depth_buffer_float");
142 128
@@ -157,7 +143,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
157 exit(1); 143 exit(1);
158 } 144 }
159 145
160 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 146 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
161 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 147 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
162 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 148 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
163 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 149 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c8b93b85b..806127b12 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -76,6 +76,9 @@ static void InitializeLogging() {
76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); 76 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
77 FileUtil::CreateFullPath(log_dir); 77 FileUtil::CreateFullPath(log_dir);
78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); 78 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
79#ifdef _WIN32
80 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
81#endif
79} 82}
80 83
81/// Application entry point 84/// Application entry point