summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appveyor.yml2
m---------externals/boost0
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/citra/CMakeLists.txt2
-rw-r--r--src/citra/config.cpp62
-rw-r--r--src/citra/default_ini.h84
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp21
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h6
-rw-r--r--src/citra_qt/CMakeLists.txt48
-rw-r--r--src/citra_qt/bootmanager.cpp27
-rw-r--r--src/citra_qt/bootmanager.h6
-rw-r--r--src/citra_qt/configuration/config.cpp (renamed from src/citra_qt/config.cpp)82
-rw-r--r--src/citra_qt/configuration/config.h (renamed from src/citra_qt/config.h)5
-rw-r--r--src/citra_qt/configuration/configure.ui (renamed from src/citra_qt/configure.ui)12
-rw-r--r--src/citra_qt/configuration/configure_audio.cpp (renamed from src/citra_qt/configure_audio.cpp)2
-rw-r--r--src/citra_qt/configuration/configure_audio.h (renamed from src/citra_qt/configure_audio.h)0
-rw-r--r--src/citra_qt/configuration/configure_audio.ui (renamed from src/citra_qt/configure_audio.ui)0
-rw-r--r--src/citra_qt/configuration/configure_debug.cpp (renamed from src/citra_qt/configure_debug.cpp)2
-rw-r--r--src/citra_qt/configuration/configure_debug.h (renamed from src/citra_qt/configure_debug.h)0
-rw-r--r--src/citra_qt/configuration/configure_debug.ui (renamed from src/citra_qt/configure_debug.ui)0
-rw-r--r--src/citra_qt/configuration/configure_dialog.cpp (renamed from src/citra_qt/configure_dialog.cpp)4
-rw-r--r--src/citra_qt/configuration/configure_dialog.h (renamed from src/citra_qt/configure_dialog.h)0
-rw-r--r--src/citra_qt/configuration/configure_general.cpp (renamed from src/citra_qt/configure_general.cpp)2
-rw-r--r--src/citra_qt/configuration/configure_general.h (renamed from src/citra_qt/configure_general.h)0
-rw-r--r--src/citra_qt/configuration/configure_general.ui (renamed from src/citra_qt/configure_general.ui)0
-rw-r--r--src/citra_qt/configuration/configure_graphics.cpp (renamed from src/citra_qt/configure_graphics.cpp)4
-rw-r--r--src/citra_qt/configuration/configure_graphics.h (renamed from src/citra_qt/configure_graphics.h)0
-rw-r--r--src/citra_qt/configuration/configure_graphics.ui (renamed from src/citra_qt/configure_graphics.ui)2
-rw-r--r--src/citra_qt/configuration/configure_input.cpp205
-rw-r--r--src/citra_qt/configuration/configure_input.h69
-rw-r--r--src/citra_qt/configuration/configure_input.ui (renamed from src/citra_qt/configure_input.ui)0
-rw-r--r--src/citra_qt/configuration/configure_system.cpp (renamed from src/citra_qt/configure_system.cpp)2
-rw-r--r--src/citra_qt/configuration/configure_system.h (renamed from src/citra_qt/configure_system.h)0
-rw-r--r--src/citra_qt/configuration/configure_system.ui (renamed from src/citra_qt/configure_system.ui)0
-rw-r--r--src/citra_qt/configure_input.cpp145
-rw-r--r--src/citra_qt/configure_input.h57
-rw-r--r--src/citra_qt/main.cpp5
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/file_util.cpp18
-rw-r--r--src/common/framebuffer_layout.cpp19
-rw-r--r--src/common/framebuffer_layout.h8
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/param_package.cpp120
-rw-r--r--src/common/param_package.h40
-rw-r--r--src/common/x64/cpu_detect.cpp12
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp2
-rw-r--r--src/core/file_sys/archive_sdmc.cpp2
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.cpp2
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.cpp2
-rw-r--r--src/core/frontend/emu_window.cpp52
-rw-r--r--src/core/frontend/emu_window.h54
-rw-r--r--src/core/frontend/input.h110
-rw-r--r--src/core/frontend/key_map.cpp152
-rw-r--r--src/core/frontend/key_map.h93
-rw-r--r--src/core/hle/function_wrappers.h4
-rw-r--r--src/core/hle/ipc.h7
-rw-r--r--src/core/hle/ipc_helpers.h170
-rw-r--r--src/core/hle/service/apt/apt.cpp444
-rw-r--r--src/core/hle/service/cfg/cfg.cpp31
-rw-r--r--src/core/hle/service/hid/hid.cpp56
-rw-r--r--src/core/hle/service/hid/hid.h37
-rw-r--r--src/core/hle/service/ptm/ptm.cpp14
-rw-r--r--src/core/hle/service/ptm/ptm.h2
-rw-r--r--src/core/hle/service/soc_u.cpp71
-rw-r--r--src/core/hle/service/y2r_u.cpp8
-rw-r--r--src/core/hle/shared_page.cpp7
-rw-r--r--src/core/hle/shared_page.h10
-rw-r--r--src/core/hle/svc.cpp24
-rw-r--r--src/core/loader/loader.cpp2
-rw-r--r--src/core/loader/ncch.cpp9
-rw-r--r--src/core/settings.cpp3
-rw-r--r--src/core/settings.h90
-rw-r--r--src/input_common/CMakeLists.txt27
-rwxr-xr-xsrc/input_common/analog_from_button.cpp58
-rwxr-xr-xsrc/input_common/analog_from_button.h31
-rw-r--r--src/input_common/keyboard.cpp93
-rw-r--r--src/input_common/keyboard.h47
-rw-r--r--src/input_common/main.cpp63
-rw-r--r--src/input_common/main.h29
-rw-r--r--src/input_common/sdl/sdl.cpp202
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/param_package.cpp25
-rw-r--r--src/video_core/regs_framebuffer.h4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp1
87 files changed, 2043 insertions, 1096 deletions
diff --git a/appveyor.yml b/appveyor.yml
index c07559479..2a4a15d40 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -20,6 +20,8 @@ install:
20 - git submodule update --init --recursive 20 - git submodule update --init --recursive
21 21
22before_build: 22before_build:
23 # Xamarin log spam workaround
24 - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
23 - mkdir build 25 - mkdir build
24 - cd build 26 - cd build
25 - cmake -G "Visual Studio 14 2015 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 .. 27 - cmake -G "Visual Studio 14 2015 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 ..
diff --git a/externals/boost b/externals/boost
Subproject f005c955f8147a29667aa0b65257abc3dd520b0 Subproject 351972396392c97a659b9a02f34ce9269293d21
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1e1245160..a45439481 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(video_core) 6add_subdirectory(video_core)
7add_subdirectory(audio_core) 7add_subdirectory(audio_core)
8add_subdirectory(input_common)
8add_subdirectory(tests) 9add_subdirectory(tests)
9if (ENABLE_SDL2) 10if (ENABLE_SDL2)
10 add_subdirectory(citra) 11 add_subdirectory(citra)
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index ecb5d2dfe..47231ba71 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -18,7 +18,7 @@ create_directory_groups(${SRCS} ${HEADERS})
18include_directories(${SDL2_INCLUDE_DIR}) 18include_directories(${SDL2_INCLUDE_DIR})
19 19
20add_executable(citra ${SRCS} ${HEADERS}) 20add_executable(citra ${SRCS} ${HEADERS})
21target_link_libraries(citra core video_core audio_core common) 21target_link_libraries(citra core video_core audio_core common input_common)
22target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) 22target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
23if (MSVC) 23if (MSVC)
24 target_link_libraries(citra getopt) 24 target_link_libraries(citra getopt)
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index fac1c9a0e..a4162e9ad 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -8,8 +8,10 @@
8#include "citra/default_ini.h" 8#include "citra/default_ini.h"
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/param_package.h"
11#include "config.h" 12#include "config.h"
12#include "core/settings.h" 13#include "core/settings.h"
14#include "input_common/main.h"
13 15
14Config::Config() { 16Config::Config() {
15 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -37,25 +39,40 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
37 return true; 39 return true;
38} 40}
39 41
40static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { 42static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
41 // directly mapped keys 43 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
42 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W, 44 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
43 SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T, 45 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
44 SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
45 SDL_SCANCODE_L,
46
47 // indirectly mapped keys
48 SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
49}; 46};
50 47
48static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
49 {
50 SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
51 },
52 {
53 SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, SDL_SCANCODE_D,
54 },
55}};
56
51void Config::ReadValues() { 57void Config::ReadValues() {
52 // Controls 58 // Controls
53 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 59 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
54 Settings::values.input_mappings[Settings::NativeInput::All[i]] = 60 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
55 sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); 61 Settings::values.buttons[i] =
62 sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
63 if (Settings::values.buttons[i].empty())
64 Settings::values.buttons[i] = default_param;
65 }
66
67 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
68 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
69 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
70 default_analogs[i][3], default_analogs[i][4], 0.5f);
71 Settings::values.analogs[i] =
72 sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
73 if (Settings::values.analogs[i].empty())
74 Settings::values.analogs[i] = default_param;
56 } 75 }
57 Settings::values.pad_circle_modifier_scale =
58 (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
59 76
60 // Core 77 // Core
61 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 78 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@@ -77,6 +94,23 @@ void Config::ReadValues() {
77 Settings::values.layout_option = 94 Settings::values.layout_option =
78 static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); 95 static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0));
79 Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); 96 Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false);
97 Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false);
98 Settings::values.custom_top_left =
99 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0));
100 Settings::values.custom_top_top =
101 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_top", 0));
102 Settings::values.custom_top_right =
103 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_right", 400));
104 Settings::values.custom_top_bottom =
105 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_bottom", 240));
106 Settings::values.custom_bottom_left =
107 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_left", 40));
108 Settings::values.custom_bottom_top =
109 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_top", 240));
110 Settings::values.custom_bottom_right =
111 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360));
112 Settings::values.custom_bottom_bottom =
113 static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480));
80 114
81 // Audio 115 // Audio
82 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 116 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 435ba6f00..084372df4 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -8,34 +8,47 @@ namespace DefaultINI {
8 8
9const char* sdl2_config_file = R"( 9const char* sdl2_config_file = R"(
10[Controls] 10[Controls]
11pad_start = 11# The input devices and parameters for each 3DS native input
12pad_select = 12# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
13pad_home = 13# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
14pad_dup = 14
15pad_ddown = 15# for button input, the following devices are avaible:
16pad_dleft = 16# - "keyboard" (default) for keyboard input. Required parameters:
17pad_dright = 17# - "code": the code of the key to bind
18pad_a = 18# - "sdl" for joystick input using SDL. Required parameters:
19pad_b = 19# - "joystick": the index of the joystick to bind
20pad_x = 20# - "button"(optional): the index of the button to bind
21pad_y = 21# - "hat"(optional): the index of the hat to bind as direction buttons
22pad_l = 22# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
23pad_r = 23button_a=
24pad_zl = 24button_b=
25pad_zr = 25button_x=
26pad_cup = 26button_y=
27pad_cdown = 27button_up=
28pad_cleft = 28button_down=
29pad_cright = 29button_left=
30pad_circle_up = 30button_right=
31pad_circle_down = 31button_l=
32pad_circle_left = 32button_r=
33pad_circle_right = 33button_start=
34pad_circle_modifier = 34button_select=
35 35button_zl=
36# The applied modifier scale to circle pad. 36button_zr=
37# Must be in range of 0.0-1.0. Defaults to 0.5 37button_home=
38pad_circle_modifier_scale = 38
39# for analog input, the following devices are avaible:
40# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
41# - "up", "down", "left", "right": sub-devices for each direction.
42# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
43# - "modifier": sub-devices as a modifier.
44# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
45# Must be in range of 0.0-1.0. Defaults to 0.5
46# - "sdl" for joystick input using SDL. Required parameters:
47# - "joystick": the index of the joystick to bind
48# - "axis_x": the index of the axis to bind as x-axis (default to 0)
49# - "axis_y": the index of the axis to bind as y-axis (default to 1)
50circle_pad=
51c_stick=
39 52
40[Core] 53[Core]
41# Whether to use the Just-In-Time (JIT) compiler for CPU emulation 54# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
@@ -71,6 +84,21 @@ bg_green =
71# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen 84# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
72layout_option = 85layout_option =
73 86
87# Toggle custom layout (using the settings below) on or off.
88# 0 (default): Off , 1: On
89custom_layout =
90
91# Screen placement when using Custom layout option
92# 0x, 0y is the top left corner of the render window.
93custom_top_left =
94custom_top_top =
95custom_top_right =
96custom_top_bottom =
97custom_bottom_left =
98custom_bottom_top =
99custom_bottom_right =
100custom_bottom_bottom =
101
74#Whether to toggle frame limiter on or off. 102#Whether to toggle frame limiter on or off.
75# 0: Off , 1 (default): On 103# 0: Off , 1 (default): On
76toggle_framelimit = 104toggle_framelimit =
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 00d00905a..6bc0b0d00 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -12,9 +12,9 @@
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/scm_rev.h" 13#include "common/scm_rev.h"
14#include "common/string_util.h" 14#include "common/string_util.h"
15#include "core/frontend/key_map.h"
16#include "core/hle/service/hid/hid.h"
17#include "core/settings.h" 15#include "core/settings.h"
16#include "input_common/keyboard.h"
17#include "input_common/main.h"
18#include "video_core/video_core.h" 18#include "video_core/video_core.h"
19 19
20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
40 40
41void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 41void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
42 if (state == SDL_PRESSED) { 42 if (state == SDL_PRESSED) {
43 KeyMap::PressKey(*this, {key, keyboard_id}); 43 InputCommon::GetKeyboard()->PressKey(key);
44 } else if (state == SDL_RELEASED) { 44 } else if (state == SDL_RELEASED) {
45 KeyMap::ReleaseKey(*this, {key, keyboard_id}); 45 InputCommon::GetKeyboard()->ReleaseKey(key);
46 } 46 }
47} 47}
48 48
@@ -57,9 +57,8 @@ void EmuWindow_SDL2::OnResize() {
57} 57}
58 58
59EmuWindow_SDL2::EmuWindow_SDL2() { 59EmuWindow_SDL2::EmuWindow_SDL2() {
60 keyboard_id = KeyMap::NewDeviceId(); 60 InputCommon::Init();
61 61
62 ReloadSetKeymaps();
63 motion_emu = std::make_unique<Motion::MotionEmu>(*this); 62 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
64 63
65 SDL_SetMainReady(); 64 SDL_SetMainReady();
@@ -117,6 +116,7 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
117 SDL_GL_DeleteContext(gl_context); 116 SDL_GL_DeleteContext(gl_context);
118 SDL_Quit(); 117 SDL_Quit();
119 motion_emu = nullptr; 118 motion_emu = nullptr;
119 InputCommon::Shutdown();
120} 120}
121 121
122void EmuWindow_SDL2::SwapBuffers() { 122void EmuWindow_SDL2::SwapBuffers() {
@@ -169,15 +169,6 @@ void EmuWindow_SDL2::DoneCurrent() {
169 SDL_GL_MakeCurrent(render_window, nullptr); 169 SDL_GL_MakeCurrent(render_window, nullptr);
170} 170}
171 171
172void EmuWindow_SDL2::ReloadSetKeymaps() {
173 KeyMap::ClearKeyMapping(keyboard_id);
174 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
175 KeyMap::SetKeyMapping(
176 {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
177 KeyMap::mapping_targets[i]);
178 }
179}
180
181void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( 172void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
182 const std::pair<unsigned, unsigned>& minimal_size) { 173 const std::pair<unsigned, unsigned>& minimal_size) {
183 174
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
index b1cbf16d7..1ce2991f7 100644
--- a/src/citra/emu_window/emu_window_sdl2.h
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -31,9 +31,6 @@ public:
31 /// Whether the window is still open, and a close request hasn't yet been sent 31 /// Whether the window is still open, and a close request hasn't yet been sent
32 bool IsOpen() const; 32 bool IsOpen() const;
33 33
34 /// Load keymap from configuration
35 void ReloadSetKeymaps() override;
36
37private: 34private:
38 /// Called by PollEvents when a key is pressed or released. 35 /// Called by PollEvents when a key is pressed or released.
39 void OnKeyEvent(int key, u8 state); 36 void OnKeyEvent(int key, u8 state);
@@ -61,9 +58,6 @@ private:
61 /// The OpenGL context associated with the window 58 /// The OpenGL context associated with the window
62 SDL_GLContext gl_context; 59 SDL_GLContext gl_context;
63 60
64 /// Device id of keyboard for use with KeyMap
65 int keyboard_id;
66
67 /// Motion sensors emulation 61 /// Motion sensors emulation
68 std::unique_ptr<Motion::MotionEmu> motion_emu; 62 std::unique_ptr<Motion::MotionEmu> motion_emu;
69}; 63};
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 15a6ccf9a..3e6106f0a 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -3,7 +3,14 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
3set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) 3set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
4 4
5set(SRCS 5set(SRCS
6 config.cpp 6 configuration/config.cpp
7 configuration/configure_audio.cpp
8 configuration/configure_debug.cpp
9 configuration/configure_dialog.cpp
10 configuration/configure_general.cpp
11 configuration/configure_graphics.cpp
12 configuration/configure_input.cpp
13 configuration/configure_system.cpp
7 debugger/callstack.cpp 14 debugger/callstack.cpp
8 debugger/disassembler.cpp 15 debugger/disassembler.cpp
9 debugger/graphics/graphics.cpp 16 debugger/graphics/graphics.cpp
@@ -19,13 +26,6 @@ set(SRCS
19 util/spinbox.cpp 26 util/spinbox.cpp
20 util/util.cpp 27 util/util.cpp
21 bootmanager.cpp 28 bootmanager.cpp
22 configure_audio.cpp
23 configure_debug.cpp
24 configure_dialog.cpp
25 configure_general.cpp
26 configure_graphics.cpp
27 configure_system.cpp
28 configure_input.cpp
29 game_list.cpp 29 game_list.cpp
30 hotkeys.cpp 30 hotkeys.cpp
31 main.cpp 31 main.cpp
@@ -35,7 +35,14 @@ set(SRCS
35 ) 35 )
36 36
37set(HEADERS 37set(HEADERS
38 config.h 38 configuration/config.h
39 configuration/configure_audio.h
40 configuration/configure_debug.h
41 configuration/configure_dialog.h
42 configuration/configure_general.h
43 configuration/configure_graphics.h
44 configuration/configure_input.h
45 configuration/configure_system.h
39 debugger/callstack.h 46 debugger/callstack.h
40 debugger/disassembler.h 47 debugger/disassembler.h
41 debugger/graphics/graphics.h 48 debugger/graphics/graphics.h
@@ -52,13 +59,6 @@ set(HEADERS
52 util/spinbox.h 59 util/spinbox.h
53 util/util.h 60 util/util.h
54 bootmanager.h 61 bootmanager.h
55 configure_audio.h
56 configure_debug.h
57 configure_dialog.h
58 configure_general.h
59 configure_graphics.h
60 configure_system.h
61 configure_input.h
62 game_list.h 62 game_list.h
63 game_list_p.h 63 game_list_p.h
64 hotkeys.h 64 hotkeys.h
@@ -67,16 +67,16 @@ set(HEADERS
67 ) 67 )
68 68
69set(UIS 69set(UIS
70 configuration/configure.ui
71 configuration/configure_audio.ui
72 configuration/configure_debug.ui
73 configuration/configure_general.ui
74 configuration/configure_graphics.ui
75 configuration/configure_input.ui
76 configuration/configure_system.ui
70 debugger/callstack.ui 77 debugger/callstack.ui
71 debugger/disassembler.ui 78 debugger/disassembler.ui
72 debugger/registers.ui 79 debugger/registers.ui
73 configure.ui
74 configure_audio.ui
75 configure_debug.ui
76 configure_general.ui
77 configure_graphics.ui
78 configure_system.ui
79 configure_input.ui
80 hotkeys.ui 80 hotkeys.ui
81 main.ui 81 main.ui
82 ) 82 )
@@ -97,7 +97,7 @@ if (APPLE)
97else() 97else()
98 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) 98 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
99endif() 99endif()
100target_link_libraries(citra-qt core video_core audio_core common) 100target_link_libraries(citra-qt core video_core audio_core common input_common)
101target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) 101target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
102target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) 102target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)
103 103
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 69d18cf0c..bae576d6a 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -13,7 +13,9 @@
13#include "common/scm_rev.h" 13#include "common/scm_rev.h"
14#include "common/string_util.h" 14#include "common/string_util.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/frontend/key_map.h" 16#include "core/settings.h"
17#include "input_common/keyboard.h"
18#include "input_common/main.h"
17#include "video_core/debug_utils/debug_utils.h" 19#include "video_core/debug_utils/debug_utils.h"
18#include "video_core/video_core.h" 20#include "video_core/video_core.h"
19 21
@@ -99,14 +101,17 @@ private:
99}; 101};
100 102
101GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 103GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
102 : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) { 104 : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
103 105
104 std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name, 106 std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name,
105 Common::g_scm_branch, Common::g_scm_desc); 107 Common::g_scm_branch, Common::g_scm_desc);
106 setWindowTitle(QString::fromStdString(window_title)); 108 setWindowTitle(QString::fromStdString(window_title));
107 109
108 keyboard_id = KeyMap::NewDeviceId(); 110 InputCommon::Init();
109 ReloadSetKeymaps(); 111}
112
113GRenderWindow::~GRenderWindow() {
114 InputCommon::Shutdown();
110} 115}
111 116
112void GRenderWindow::moveContext() { 117void GRenderWindow::moveContext() {
@@ -197,11 +202,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
197} 202}
198 203
199void GRenderWindow::keyPressEvent(QKeyEvent* event) { 204void GRenderWindow::keyPressEvent(QKeyEvent* event) {
200 KeyMap::PressKey(*this, {event->key(), keyboard_id}); 205 InputCommon::GetKeyboard()->PressKey(event->key());
201} 206}
202 207
203void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { 208void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
204 KeyMap::ReleaseKey(*this, {event->key(), keyboard_id}); 209 InputCommon::GetKeyboard()->ReleaseKey(event->key());
205} 210}
206 211
207void GRenderWindow::mousePressEvent(QMouseEvent* event) { 212void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -230,13 +235,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
230 motion_emu->EndTilt(); 235 motion_emu->EndTilt();
231} 236}
232 237
233void GRenderWindow::ReloadSetKeymaps() { 238void GRenderWindow::focusOutEvent(QFocusEvent* event) {
234 KeyMap::ClearKeyMapping(keyboard_id); 239 QWidget::focusOutEvent(event);
235 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 240 InputCommon::GetKeyboard()->ReleaseAllKeys();
236 KeyMap::SetKeyMapping(
237 {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
238 KeyMap::mapping_targets[i]);
239 }
240} 241}
241 242
242void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { 243void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 7dac1c480..9d39f1af8 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -104,6 +104,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
104 104
105public: 105public:
106 GRenderWindow(QWidget* parent, EmuThread* emu_thread); 106 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
107 ~GRenderWindow();
107 108
108 // EmuWindow implementation 109 // EmuWindow implementation
109 void SwapBuffers() override; 110 void SwapBuffers() override;
@@ -127,7 +128,7 @@ public:
127 void mouseMoveEvent(QMouseEvent* event) override; 128 void mouseMoveEvent(QMouseEvent* event) override;
128 void mouseReleaseEvent(QMouseEvent* event) override; 129 void mouseReleaseEvent(QMouseEvent* event) override;
129 130
130 void ReloadSetKeymaps() override; 131 void focusOutEvent(QFocusEvent* event) override;
131 132
132 void OnClientAreaResized(unsigned width, unsigned height); 133 void OnClientAreaResized(unsigned width, unsigned height);
133 134
@@ -152,9 +153,6 @@ private:
152 153
153 QByteArray geometry; 154 QByteArray geometry;
154 155
155 /// Device id of keyboard for use with KeyMap
156 int keyboard_id;
157
158 EmuThread* emu_thread; 156 EmuThread* emu_thread;
159 157
160 /// Motion sensors emulation 158 /// Motion sensors emulation
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/configuration/config.cpp
index 5fe57dfa2..0b9b73f9e 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -3,9 +3,10 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QSettings> 5#include <QSettings>
6#include "citra_qt/config.h" 6#include "citra_qt/configuration/config.h"
7#include "citra_qt/ui_settings.h" 7#include "citra_qt/ui_settings.h"
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "input_common/main.h"
9 10
10Config::Config() { 11Config::Config() {
11 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -16,25 +17,46 @@ Config::Config() {
16 Reload(); 17 Reload();
17} 18}
18 19
19const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { 20const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
20 // directly mapped keys 21 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
21 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, 22 Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
22 Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
23 Qt::Key_K, Qt::Key_J, Qt::Key_L,
24
25 // indirectly mapped keys
26 Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
27}; 23};
28 24
25const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
26 {
27 Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
28 },
29 {
30 Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D,
31 },
32}};
33
29void Config::ReadValues() { 34void Config::ReadValues() {
30 qt_config->beginGroup("Controls"); 35 qt_config->beginGroup("Controls");
31 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 36 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
32 Settings::values.input_mappings[Settings::NativeInput::All[i]] = 37 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
33 qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]) 38 Settings::values.buttons[i] =
34 .toInt(); 39 qt_config
40 ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
41 .toString()
42 .toStdString();
43 if (Settings::values.buttons[i].empty())
44 Settings::values.buttons[i] = default_param;
45 }
46
47 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
48 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
49 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
50 default_analogs[i][3], default_analogs[i][4], 0.5f);
51 Settings::values.analogs[i] =
52 qt_config
53 ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
54 .toString()
55 .toStdString();
56 if (Settings::values.analogs[i].empty())
57 Settings::values.analogs[i] = default_param;
35 } 58 }
36 Settings::values.pad_circle_modifier_scale = 59
37 qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
38 qt_config->endGroup(); 60 qt_config->endGroup();
39 61
40 qt_config->beginGroup("Core"); 62 qt_config->beginGroup("Core");
@@ -57,6 +79,15 @@ void Config::ReadValues() {
57 Settings::values.layout_option = 79 Settings::values.layout_option =
58 static_cast<Settings::LayoutOption>(qt_config->value("layout_option").toInt()); 80 static_cast<Settings::LayoutOption>(qt_config->value("layout_option").toInt());
59 Settings::values.swap_screen = qt_config->value("swap_screen", false).toBool(); 81 Settings::values.swap_screen = qt_config->value("swap_screen", false).toBool();
82 Settings::values.custom_layout = qt_config->value("custom_layout", false).toBool();
83 Settings::values.custom_top_left = qt_config->value("custom_top_left", 0).toInt();
84 Settings::values.custom_top_top = qt_config->value("custom_top_top", 0).toInt();
85 Settings::values.custom_top_right = qt_config->value("custom_top_right", 400).toInt();
86 Settings::values.custom_top_bottom = qt_config->value("custom_top_bottom", 240).toInt();
87 Settings::values.custom_bottom_left = qt_config->value("custom_bottom_left", 40).toInt();
88 Settings::values.custom_bottom_top = qt_config->value("custom_bottom_top", 240).toInt();
89 Settings::values.custom_bottom_right = qt_config->value("custom_bottom_right", 360).toInt();
90 Settings::values.custom_bottom_bottom = qt_config->value("custom_bottom_bottom", 480).toInt();
60 qt_config->endGroup(); 91 qt_config->endGroup();
61 92
62 qt_config->beginGroup("Audio"); 93 qt_config->beginGroup("Audio");
@@ -155,12 +186,14 @@ void Config::ReadValues() {
155 186
156void Config::SaveValues() { 187void Config::SaveValues() {
157 qt_config->beginGroup("Controls"); 188 qt_config->beginGroup("Controls");
158 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 189 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
159 qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), 190 qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
160 Settings::values.input_mappings[Settings::NativeInput::All[i]]); 191 QString::fromStdString(Settings::values.buttons[i]));
192 }
193 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
194 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
195 QString::fromStdString(Settings::values.analogs[i]));
161 } 196 }
162 qt_config->setValue("pad_circle_modifier_scale",
163 (double)Settings::values.pad_circle_modifier_scale);
164 qt_config->endGroup(); 197 qt_config->endGroup();
165 198
166 qt_config->beginGroup("Core"); 199 qt_config->beginGroup("Core");
@@ -183,6 +216,15 @@ void Config::SaveValues() {
183 qt_config->beginGroup("Layout"); 216 qt_config->beginGroup("Layout");
184 qt_config->setValue("layout_option", static_cast<int>(Settings::values.layout_option)); 217 qt_config->setValue("layout_option", static_cast<int>(Settings::values.layout_option));
185 qt_config->setValue("swap_screen", Settings::values.swap_screen); 218 qt_config->setValue("swap_screen", Settings::values.swap_screen);
219 qt_config->setValue("custom_layout", Settings::values.custom_layout);
220 qt_config->setValue("custom_top_left", Settings::values.custom_top_left);
221 qt_config->setValue("custom_top_top", Settings::values.custom_top_top);
222 qt_config->setValue("custom_top_right", Settings::values.custom_top_right);
223 qt_config->setValue("custom_top_bottom", Settings::values.custom_top_bottom);
224 qt_config->setValue("custom_bottom_left", Settings::values.custom_bottom_left);
225 qt_config->setValue("custom_bottom_top", Settings::values.custom_bottom_top);
226 qt_config->setValue("custom_bottom_right", Settings::values.custom_bottom_right);
227 qt_config->setValue("custom_bottom_bottom", Settings::values.custom_bottom_bottom);
186 qt_config->endGroup(); 228 qt_config->endGroup();
187 229
188 qt_config->beginGroup("Audio"); 230 qt_config->beginGroup("Audio");
diff --git a/src/citra_qt/config.h b/src/citra_qt/configuration/config.h
index 79c901804..cbf745ea2 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/configuration/config.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <string> 8#include <string>
8#include <QVariant> 9#include <QVariant>
9#include "core/settings.h" 10#include "core/settings.h"
@@ -23,5 +24,7 @@ public:
23 24
24 void Reload(); 25 void Reload();
25 void Save(); 26 void Save();
26 static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults; 27
28 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
29 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27}; 30};
diff --git a/src/citra_qt/configure.ui b/src/citra_qt/configuration/configure.ui
index 28b4a3b90..85e206e42 100644
--- a/src/citra_qt/configure.ui
+++ b/src/citra_qt/configuration/configure.ui
@@ -64,37 +64,37 @@
64 <customwidget> 64 <customwidget>
65 <class>ConfigureGeneral</class> 65 <class>ConfigureGeneral</class>
66 <extends>QWidget</extends> 66 <extends>QWidget</extends>
67 <header>configure_general.h</header> 67 <header>configuration/configure_general.h</header>
68 <container>1</container> 68 <container>1</container>
69 </customwidget> 69 </customwidget>
70 <customwidget> 70 <customwidget>
71 <class>ConfigureSystem</class> 71 <class>ConfigureSystem</class>
72 <extends>QWidget</extends> 72 <extends>QWidget</extends>
73 <header>configure_system.h</header> 73 <header>configuration/configure_system.h</header>
74 <container>1</container> 74 <container>1</container>
75 </customwidget> 75 </customwidget>
76 <customwidget> 76 <customwidget>
77 <class>ConfigureAudio</class> 77 <class>ConfigureAudio</class>
78 <extends>QWidget</extends> 78 <extends>QWidget</extends>
79 <header>configure_audio.h</header> 79 <header>configuration/configure_audio.h</header>
80 <container>1</container> 80 <container>1</container>
81 </customwidget> 81 </customwidget>
82 <customwidget> 82 <customwidget>
83 <class>ConfigureDebug</class> 83 <class>ConfigureDebug</class>
84 <extends>QWidget</extends> 84 <extends>QWidget</extends>
85 <header>configure_debug.h</header> 85 <header>configuration/configure_debug.h</header>
86 <container>1</container> 86 <container>1</container>
87 </customwidget> 87 </customwidget>
88 <customwidget> 88 <customwidget>
89 <class>ConfigureInput</class> 89 <class>ConfigureInput</class>
90 <extends>QWidget</extends> 90 <extends>QWidget</extends>
91 <header>configure_input.h</header> 91 <header>configuration/configure_input.h</header>
92 <container>1</container> 92 <container>1</container>
93 </customwidget> 93 </customwidget>
94 <customwidget> 94 <customwidget>
95 <class>ConfigureGraphics</class> 95 <class>ConfigureGraphics</class>
96 <extends>QWidget</extends> 96 <extends>QWidget</extends>
97 <header>configure_graphics.h</header> 97 <header>configuration/configure_graphics.h</header>
98 <container>1</container> 98 <container>1</container>
99 </customwidget> 99 </customwidget>
100 </customwidgets> 100 </customwidgets>
diff --git a/src/citra_qt/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp
index 3ddcf9232..3fd1d127a 100644
--- a/src/citra_qt/configure_audio.cpp
+++ b/src/citra_qt/configuration/configure_audio.cpp
@@ -6,7 +6,7 @@
6#include "audio_core/audio_core.h" 6#include "audio_core/audio_core.h"
7#include "audio_core/sink.h" 7#include "audio_core/sink.h"
8#include "audio_core/sink_details.h" 8#include "audio_core/sink_details.h"
9#include "citra_qt/configure_audio.h" 9#include "citra_qt/configuration/configure_audio.h"
10#include "core/settings.h" 10#include "core/settings.h"
11#include "ui_configure_audio.h" 11#include "ui_configure_audio.h"
12 12
diff --git a/src/citra_qt/configure_audio.h b/src/citra_qt/configuration/configure_audio.h
index 8190e694f..8190e694f 100644
--- a/src/citra_qt/configure_audio.h
+++ b/src/citra_qt/configuration/configure_audio.h
diff --git a/src/citra_qt/configure_audio.ui b/src/citra_qt/configuration/configure_audio.ui
index dd870eb61..dd870eb61 100644
--- a/src/citra_qt/configure_audio.ui
+++ b/src/citra_qt/configuration/configure_audio.ui
diff --git a/src/citra_qt/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp
index dcc398eee..263f73f38 100644
--- a/src/citra_qt/configure_debug.cpp
+++ b/src/citra_qt/configuration/configure_debug.cpp
@@ -2,7 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configure_debug.h" 5#include "citra_qt/configuration/configure_debug.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "ui_configure_debug.h" 7#include "ui_configure_debug.h"
8 8
diff --git a/src/citra_qt/configure_debug.h b/src/citra_qt/configuration/configure_debug.h
index d167eb996..d167eb996 100644
--- a/src/citra_qt/configure_debug.h
+++ b/src/citra_qt/configuration/configure_debug.h
diff --git a/src/citra_qt/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui
index bbbb0e3f4..bbbb0e3f4 100644
--- a/src/citra_qt/configure_debug.ui
+++ b/src/citra_qt/configuration/configure_debug.ui
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp
index 525a7cc4e..dfc8c03a7 100644
--- a/src/citra_qt/configure_dialog.cpp
+++ b/src/citra_qt/configuration/configure_dialog.cpp
@@ -2,8 +2,8 @@
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 "citra_qt/config.h" 5#include "citra_qt/configuration/config.h"
6#include "citra_qt/configure_dialog.h" 6#include "citra_qt/configuration/configure_dialog.h"
7#include "core/settings.h" 7#include "core/settings.h"
8#include "ui_configure.h" 8#include "ui_configure.h"
9 9
diff --git a/src/citra_qt/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h
index 21fa1f501..21fa1f501 100644
--- a/src/citra_qt/configure_dialog.h
+++ b/src/citra_qt/configuration/configure_dialog.h
diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp
index ac90a6df4..a21176c34 100644
--- a/src/citra_qt/configure_general.cpp
+++ b/src/citra_qt/configuration/configure_general.cpp
@@ -2,7 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configure_general.h" 5#include "citra_qt/configuration/configure_general.h"
6#include "citra_qt/ui_settings.h" 6#include "citra_qt/ui_settings.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/settings.h" 8#include "core/settings.h"
diff --git a/src/citra_qt/configure_general.h b/src/citra_qt/configuration/configure_general.h
index 447552d8c..447552d8c 100644
--- a/src/citra_qt/configure_general.h
+++ b/src/citra_qt/configuration/configure_general.h
diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configuration/configure_general.ui
index c739605a4..c739605a4 100644
--- a/src/citra_qt/configure_general.ui
+++ b/src/citra_qt/configuration/configure_general.ui
diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp
index 54f799b47..b5a5ab1e1 100644
--- a/src/citra_qt/configure_graphics.cpp
+++ b/src/citra_qt/configuration/configure_graphics.cpp
@@ -2,7 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configure_graphics.h" 5#include "citra_qt/configuration/configure_graphics.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/settings.h" 7#include "core/settings.h"
8#include "ui_configure_graphics.h" 8#include "ui_configure_graphics.h"
@@ -14,6 +14,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
14 this->setConfiguration(); 14 this->setConfiguration();
15 15
16 ui->toggle_vsync->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 16 ui->toggle_vsync->setEnabled(!Core::System::GetInstance().IsPoweredOn());
17
18 ui->layoutBox->setEnabled(!Settings::values.custom_layout);
17} 19}
18 20
19ConfigureGraphics::~ConfigureGraphics() {} 21ConfigureGraphics::~ConfigureGraphics() {}
diff --git a/src/citra_qt/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h
index 5497a55f7..5497a55f7 100644
--- a/src/citra_qt/configure_graphics.h
+++ b/src/citra_qt/configuration/configure_graphics.h
diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index a091f4c60..228f2a869 100644
--- a/src/citra_qt/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -126,7 +126,7 @@
126 </layout> 126 </layout>
127 </item> 127 </item>
128 <item> 128 <item>
129 <widget class="QGroupBox" name="groupBox2"> 129 <widget class="QGroupBox" name="layoutBox">
130 <property name="title"> 130 <property name="title">
131 <string>Layout</string> 131 <string>Layout</string>
132 </property> 132 </property>
diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp
new file mode 100644
index 000000000..daac9b63a
--- /dev/null
+++ b/src/citra_qt/configuration/configure_input.cpp
@@ -0,0 +1,205 @@
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 <QTimer>
9#include "citra_qt/configuration/config.h"
10#include "citra_qt/configuration/configure_input.h"
11#include "common/param_package.h"
12#include "input_common/main.h"
13
14const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
15 ConfigureInput::analog_sub_buttons{{
16 "up", "down", "left", "right", "modifier",
17 }};
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 void SetButtonKey(int key, Common::ParamPackage& button_param) {
35 button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)};
36}
37
38static void SetAnalogKey(int key, Common::ParamPackage& analog_param,
39 const std::string& button_name) {
40 if (analog_param.Get("engine", "") != "analog_from_button") {
41 analog_param = {
42 {"engine", "analog_from_button"}, {"modifier_scale", "0.5"},
43 };
44 }
45 analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key));
46}
47
48ConfigureInput::ConfigureInput(QWidget* parent)
49 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
50 timer(std::make_unique<QTimer>()) {
51
52 ui->setupUi(this);
53 setFocusPolicy(Qt::ClickFocus);
54
55 button_map = {
56 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp,
57 ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR,
58 ui->buttonStart, ui->buttonSelect, ui->buttonZL, ui->buttonZR, ui->buttonHome,
59 };
60
61 analog_map = {{
62 {
63 ui->buttonCircleUp, ui->buttonCircleDown, ui->buttonCircleLeft, ui->buttonCircleRight,
64 ui->buttonCircleMod,
65 },
66 {
67 ui->buttonCStickUp, ui->buttonCStickDown, ui->buttonCStickLeft, ui->buttonCStickRight,
68 nullptr,
69 },
70 }};
71
72 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
73 if (button_map[button_id])
74 connect(button_map[button_id], &QPushButton::released, [=]() {
75 handleClick(button_map[button_id],
76 [=](int key) { SetButtonKey(key, buttons_param[button_id]); });
77 });
78 }
79
80 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
81 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
82 if (analog_map[analog_id][sub_button_id] != nullptr) {
83 connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() {
84 handleClick(analog_map[analog_id][sub_button_id], [=](int key) {
85 SetAnalogKey(key, analogs_param[analog_id],
86 analog_sub_buttons[sub_button_id]);
87 });
88 });
89 }
90 }
91 }
92
93 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
94
95 timer->setSingleShot(true);
96 connect(timer.get(), &QTimer::timeout, [this]() {
97 releaseKeyboard();
98 releaseMouse();
99 key_setter = boost::none;
100 updateButtonLabels();
101 });
102
103 this->loadConfiguration();
104
105 // TODO(wwylele): enable these when the input emulation for them is implemented
106 ui->buttonZL->setEnabled(false);
107 ui->buttonZR->setEnabled(false);
108 ui->buttonHome->setEnabled(false);
109 ui->buttonCStickUp->setEnabled(false);
110 ui->buttonCStickDown->setEnabled(false);
111 ui->buttonCStickLeft->setEnabled(false);
112 ui->buttonCStickRight->setEnabled(false);
113}
114
115void ConfigureInput::applyConfiguration() {
116 std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
117 [](const Common::ParamPackage& param) { return param.Serialize(); });
118 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
119 [](const Common::ParamPackage& param) { return param.Serialize(); });
120
121 Settings::Apply();
122}
123
124void ConfigureInput::loadConfiguration() {
125 std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
126 buttons_param.begin(),
127 [](const std::string& str) { return Common::ParamPackage(str); });
128 std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
129 analogs_param.begin(),
130 [](const std::string& str) { return Common::ParamPackage(str); });
131 updateButtonLabels();
132}
133
134void ConfigureInput::restoreDefaults() {
135 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
136 SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]);
137 }
138
139 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
140 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
141 SetAnalogKey(Config::default_analogs[analog_id][sub_button_id],
142 analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
143 }
144 }
145 updateButtonLabels();
146 applyConfiguration();
147}
148
149void ConfigureInput::updateButtonLabels() {
150 QString non_keyboard(tr("[non-keyboard]"));
151
152 auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) {
153 if (param.Get("engine", "") != "keyboard") {
154 return non_keyboard;
155 } else {
156 return getKeyName(param.Get("code", 0));
157 }
158 };
159
160 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
161 button_map[button]->setText(KeyToText(buttons_param[button]));
162 }
163
164 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
165 if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
166 for (QPushButton* button : analog_map[analog_id]) {
167 if (button)
168 button->setText(non_keyboard);
169 }
170 } else {
171 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
172 Common::ParamPackage param(
173 analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], ""));
174 if (analog_map[analog_id][sub_button_id])
175 analog_map[analog_id][sub_button_id]->setText(KeyToText(param));
176 }
177 }
178 }
179}
180
181void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) {
182 button->setText(tr("[press key]"));
183 button->setFocus();
184
185 key_setter = new_key_setter;
186
187 grabKeyboard();
188 grabMouse();
189 timer->start(5000); // Cancel after 5 seconds
190}
191
192void ConfigureInput::keyPressEvent(QKeyEvent* event) {
193 releaseKeyboard();
194 releaseMouse();
195
196 if (!key_setter || !event)
197 return;
198
199 if (event->key() != Qt::Key_Escape)
200 (*key_setter)(event->key());
201
202 updateButtonLabels();
203 key_setter = boost::none;
204 timer->stop();
205}
diff --git a/src/citra_qt/configuration/configure_input.h b/src/citra_qt/configuration/configure_input.h
new file mode 100644
index 000000000..c950fbcb4
--- /dev/null
+++ b/src/citra_qt/configuration/configure_input.h
@@ -0,0 +1,69 @@
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 <string>
11#include <QKeyEvent>
12#include <QWidget>
13#include <boost/optional.hpp>
14#include "common/param_package.h"
15#include "core/settings.h"
16#include "ui_configure_input.h"
17
18class QPushButton;
19class QString;
20class QTimer;
21
22namespace Ui {
23class ConfigureInput;
24}
25
26class ConfigureInput : public QWidget {
27 Q_OBJECT
28
29public:
30 explicit ConfigureInput(QWidget* parent = nullptr);
31
32 /// Save all button configurations to settings file
33 void applyConfiguration();
34
35private:
36 std::unique_ptr<Ui::ConfigureInput> ui;
37
38 std::unique_ptr<QTimer> timer;
39
40 /// This will be the the setting function when an input is awaiting configuration.
41 boost::optional<std::function<void(int)>> key_setter;
42
43 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
44 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
45
46 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
47
48 /// Each button input is represented by a QPushButton.
49 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
50
51 /// Each analog input is represented by five QPushButtons which represents up, down, left, right
52 /// and modifier
53 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
54 analog_map;
55
56 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
57
58 /// Load configuration settings.
59 void loadConfiguration();
60 /// Restore all buttons to their default values.
61 void restoreDefaults();
62 /// Update UI to reflect current configuration.
63 void updateButtonLabels();
64
65 /// Called when the button was pressed.
66 void handleClick(QPushButton* button, std::function<void(int)> new_key_setter);
67 /// Handle key press events.
68 void keyPressEvent(QKeyEvent* event) override;
69};
diff --git a/src/citra_qt/configure_input.ui b/src/citra_qt/configuration/configure_input.ui
index 2760787e5..2760787e5 100644
--- a/src/citra_qt/configure_input.ui
+++ b/src/citra_qt/configuration/configure_input.ui
diff --git a/src/citra_qt/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp
index 040185e82..a3a9015a4 100644
--- a/src/citra_qt/configure_system.cpp
+++ b/src/citra_qt/configuration/configure_system.cpp
@@ -2,7 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "citra_qt/configure_system.h" 5#include "citra_qt/configuration/configure_system.h"
6#include "citra_qt/ui_settings.h" 6#include "citra_qt/ui_settings.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/cfg/cfg.h" 8#include "core/hle/service/cfg/cfg.h"
diff --git a/src/citra_qt/configure_system.h b/src/citra_qt/configuration/configure_system.h
index db0ead13c..db0ead13c 100644
--- a/src/citra_qt/configure_system.h
+++ b/src/citra_qt/configuration/configure_system.h
diff --git a/src/citra_qt/configure_system.ui b/src/citra_qt/configuration/configure_system.ui
index cc54fa37f..cc54fa37f 100644
--- a/src/citra_qt/configure_system.ui
+++ b/src/citra_qt/configuration/configure_system.ui
diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp
deleted file mode 100644
index c29652f32..000000000
--- a/src/citra_qt/configure_input.cpp
+++ /dev/null
@@ -1,145 +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#include <memory>
6#include <utility>
7#include <QTimer>
8#include "citra_qt/config.h"
9#include "citra_qt/configure_input.h"
10
11static QString getKeyName(Qt::Key key_code) {
12 switch (key_code) {
13 case Qt::Key_Shift:
14 return QObject::tr("Shift");
15 case Qt::Key_Control:
16 return QObject::tr("Ctrl");
17 case Qt::Key_Alt:
18 return QObject::tr("Alt");
19 case Qt::Key_Meta:
20 return "";
21 default:
22 return QKeySequence(key_code).toString();
23 }
24}
25
26ConfigureInput::ConfigureInput(QWidget* parent)
27 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
28 timer(std::make_unique<QTimer>()) {
29
30 ui->setupUi(this);
31 setFocusPolicy(Qt::ClickFocus);
32
33 button_map = {
34 {Settings::NativeInput::Values::A, ui->buttonA},
35 {Settings::NativeInput::Values::B, ui->buttonB},
36 {Settings::NativeInput::Values::X, ui->buttonX},
37 {Settings::NativeInput::Values::Y, ui->buttonY},
38 {Settings::NativeInput::Values::L, ui->buttonL},
39 {Settings::NativeInput::Values::R, ui->buttonR},
40 {Settings::NativeInput::Values::ZL, ui->buttonZL},
41 {Settings::NativeInput::Values::ZR, ui->buttonZR},
42 {Settings::NativeInput::Values::START, ui->buttonStart},
43 {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
44 {Settings::NativeInput::Values::HOME, ui->buttonHome},
45 {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
46 {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
47 {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
48 {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
49 {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
50 {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
51 {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
52 {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
53 {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
54 {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
55 {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
56 {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
57 {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
58 };
59
60 for (const auto& entry : button_map) {
61 const Settings::NativeInput::Values input_id = entry.first;
62 connect(entry.second, &QPushButton::released,
63 [this, input_id]() { handleClick(input_id); });
64 }
65
66 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
67
68 timer->setSingleShot(true);
69 connect(timer.get(), &QTimer::timeout, [this]() {
70 releaseKeyboard();
71 releaseMouse();
72 current_input_id = boost::none;
73 updateButtonLabels();
74 });
75
76 this->loadConfiguration();
77}
78
79void ConfigureInput::applyConfiguration() {
80 for (const auto& input_id : Settings::NativeInput::All) {
81 const size_t index = static_cast<size_t>(input_id);
82 Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]);
83 }
84 Settings::Apply();
85}
86
87void ConfigureInput::loadConfiguration() {
88 for (const auto& input_id : Settings::NativeInput::All) {
89 const size_t index = static_cast<size_t>(input_id);
90 key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]);
91 }
92 updateButtonLabels();
93}
94
95void ConfigureInput::restoreDefaults() {
96 for (const auto& input_id : Settings::NativeInput::All) {
97 const size_t index = static_cast<size_t>(input_id);
98 key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt());
99 }
100 updateButtonLabels();
101 applyConfiguration();
102}
103
104void ConfigureInput::updateButtonLabels() {
105 for (const auto& input_id : Settings::NativeInput::All) {
106 button_map[input_id]->setText(getKeyName(key_map[input_id]));
107 }
108}
109
110void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) {
111 QPushButton* button = button_map[input_id];
112 button->setText(tr("[press key]"));
113 button->setFocus();
114
115 current_input_id = input_id;
116
117 grabKeyboard();
118 grabMouse();
119 timer->start(5000); // Cancel after 5 seconds
120}
121
122void ConfigureInput::keyPressEvent(QKeyEvent* event) {
123 releaseKeyboard();
124 releaseMouse();
125
126 if (!current_input_id || !event)
127 return;
128
129 if (event->key() != Qt::Key_Escape)
130 setInput(*current_input_id, static_cast<Qt::Key>(event->key()));
131
132 updateButtonLabels();
133 current_input_id = boost::none;
134 timer->stop();
135}
136
137void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) {
138 // Remove duplicates
139 for (auto& pair : key_map) {
140 if (pair.second == key_pressed)
141 pair.second = Qt::Key_unknown;
142 }
143
144 key_map[input_id] = key_pressed;
145}
diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h
deleted file mode 100644
index bc343db83..000000000
--- a/src/citra_qt/configure_input.h
+++ /dev/null
@@ -1,57 +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 <memory>
8#include <QKeyEvent>
9#include <QWidget>
10#include <boost/optional.hpp>
11#include "core/settings.h"
12#include "ui_configure_input.h"
13
14class QPushButton;
15class QString;
16class QTimer;
17
18namespace Ui {
19class ConfigureInput;
20}
21
22class ConfigureInput : public QWidget {
23 Q_OBJECT
24
25public:
26 explicit ConfigureInput(QWidget* parent = nullptr);
27
28 /// Save all button configurations to settings file
29 void applyConfiguration();
30
31private:
32 std::unique_ptr<Ui::ConfigureInput> ui;
33
34 /// This input is currently awaiting configuration.
35 /// (i.e.: its corresponding QPushButton has been pressed.)
36 boost::optional<Settings::NativeInput::Values> current_input_id;
37 std::unique_ptr<QTimer> timer;
38
39 /// Each input is represented by a QPushButton.
40 std::map<Settings::NativeInput::Values, QPushButton*> button_map;
41 /// Each input is configured to respond to the press of a Qt::Key.
42 std::map<Settings::NativeInput::Values, Qt::Key> key_map;
43
44 /// Load configuration settings.
45 void loadConfiguration();
46 /// Restore all buttons to their default values.
47 void restoreDefaults();
48 /// Update UI to reflect current configuration.
49 void updateButtonLabels();
50
51 /// Called when the button corresponding to input_id was pressed.
52 void handleClick(Settings::NativeInput::Values input_id);
53 /// Handle key press events.
54 void keyPressEvent(QKeyEvent* event) override;
55 /// Configure input input_id to respond to key key_pressed.
56 void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed);
57};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index fd51659b9..73b4dd34f 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -14,8 +14,8 @@
14#include <QtGui> 14#include <QtGui>
15#include <QtWidgets> 15#include <QtWidgets>
16#include "citra_qt/bootmanager.h" 16#include "citra_qt/bootmanager.h"
17#include "citra_qt/config.h" 17#include "citra_qt/configuration/config.h"
18#include "citra_qt/configure_dialog.h" 18#include "citra_qt/configuration/configure_dialog.h"
19#include "citra_qt/debugger/callstack.h" 19#include "citra_qt/debugger/callstack.h"
20#include "citra_qt/debugger/disassembler.h" 20#include "citra_qt/debugger/disassembler.h"
21#include "citra_qt/debugger/graphics/graphics.h" 21#include "citra_qt/debugger/graphics/graphics.h"
@@ -612,7 +612,6 @@ void GMainWindow::OnConfigure() {
612 auto result = configureDialog.exec(); 612 auto result = configureDialog.exec();
613 if (result == QDialog::Accepted) { 613 if (result == QDialog::Accepted) {
614 configureDialog.applyConfiguration(); 614 configureDialog.applyConfiguration();
615 render_window->ReloadSetKeymaps();
616 config->Save(); 615 config->Save();
617 } 616 }
618} 617}
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8a6170257..13277a5c2 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -35,6 +35,7 @@ set(SRCS
35 memory_util.cpp 35 memory_util.cpp
36 microprofile.cpp 36 microprofile.cpp
37 misc.cpp 37 misc.cpp
38 param_package.cpp
38 scm_rev.cpp 39 scm_rev.cpp
39 string_util.cpp 40 string_util.cpp
40 symbols.cpp 41 symbols.cpp
@@ -66,6 +67,7 @@ set(HEADERS
66 memory_util.h 67 memory_util.h
67 microprofile.h 68 microprofile.h
68 microprofileui.h 69 microprofileui.h
70 param_package.h
69 platform.h 71 platform.h
70 quaternion.h 72 quaternion.h
71 scm_rev.h 73 scm_rev.h
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index df234c225..5ab036b34 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -118,8 +118,7 @@ bool IsDirectory(const std::string& filename) {
118#endif 118#endif
119 119
120 if (result < 0) { 120 if (result < 0) {
121 LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", filename.c_str(), 121 LOG_DEBUG(Common_Filesystem, "stat failed on %s: %s", filename.c_str(), GetLastErrorMsg());
122 GetLastErrorMsg());
123 return false; 122 return false;
124 } 123 }
125 124
@@ -129,12 +128,12 @@ bool IsDirectory(const std::string& filename) {
129// Deletes a given filename, return true on success 128// Deletes a given filename, return true on success
130// Doesn't supports deleting a directory 129// Doesn't supports deleting a directory
131bool Delete(const std::string& filename) { 130bool Delete(const std::string& filename) {
132 LOG_INFO(Common_Filesystem, "file %s", filename.c_str()); 131 LOG_TRACE(Common_Filesystem, "file %s", filename.c_str());
133 132
134 // Return true because we care about the file no 133 // Return true because we care about the file no
135 // being there, not the actual delete. 134 // being there, not the actual delete.
136 if (!Exists(filename)) { 135 if (!Exists(filename)) {
137 LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str()); 136 LOG_DEBUG(Common_Filesystem, "%s does not exist", filename.c_str());
138 return true; 137 return true;
139 } 138 }
140 139
@@ -169,8 +168,7 @@ bool CreateDir(const std::string& path) {
169 return true; 168 return true;
170 DWORD error = GetLastError(); 169 DWORD error = GetLastError();
171 if (error == ERROR_ALREADY_EXISTS) { 170 if (error == ERROR_ALREADY_EXISTS) {
172 LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", 171 LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
173 path.c_str());
174 return true; 172 return true;
175 } 173 }
176 LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error); 174 LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
@@ -182,7 +180,7 @@ bool CreateDir(const std::string& path) {
182 int err = errno; 180 int err = errno;
183 181
184 if (err == EEXIST) { 182 if (err == EEXIST) {
185 LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str()); 183 LOG_DEBUG(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
186 return true; 184 return true;
187 } 185 }
188 186
@@ -197,7 +195,7 @@ bool CreateFullPath(const std::string& fullPath) {
197 LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str()); 195 LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
198 196
199 if (FileUtil::Exists(fullPath)) { 197 if (FileUtil::Exists(fullPath)) {
200 LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str()); 198 LOG_DEBUG(Common_Filesystem, "path exists %s", fullPath.c_str());
201 return true; 199 return true;
202 } 200 }
203 201
@@ -229,7 +227,7 @@ bool CreateFullPath(const std::string& fullPath) {
229 227
230// Deletes a directory filename, returns true on success 228// Deletes a directory filename, returns true on success
231bool DeleteDir(const std::string& filename) { 229bool DeleteDir(const std::string& filename) {
232 LOG_INFO(Common_Filesystem, "directory %s", filename.c_str()); 230 LOG_TRACE(Common_Filesystem, "directory %s", filename.c_str());
233 231
234 // check if a directory 232 // check if a directory
235 if (!FileUtil::IsDirectory(filename)) { 233 if (!FileUtil::IsDirectory(filename)) {
@@ -693,6 +691,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
693 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 691 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
694 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { 692 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
695 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 693 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
694 } else {
695 LOG_INFO(Common_Filesystem, "Using the local user directory");
696 } 696 }
697 697
698 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 698 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
diff --git a/src/common/framebuffer_layout.cpp b/src/common/framebuffer_layout.cpp
index 46c008d9c..a2a0e7dad 100644
--- a/src/common/framebuffer_layout.cpp
+++ b/src/common/framebuffer_layout.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/framebuffer_layout.h" 8#include "common/framebuffer_layout.h"
9#include "core/settings.h"
9#include "video_core/video_core.h" 10#include "video_core/video_core.h"
10 11
11namespace Layout { 12namespace Layout {
@@ -135,4 +136,22 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
135 res.bottom_screen = swapped ? large_screen : small_screen; 136 res.bottom_screen = swapped ? large_screen : small_screen;
136 return res; 137 return res;
137} 138}
139
140FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
141 ASSERT(width > 0);
142 ASSERT(height > 0);
143
144 FramebufferLayout res{width, height, true, true, {}, {}};
145
146 MathUtil::Rectangle<unsigned> top_screen{
147 Settings::values.custom_top_left, Settings::values.custom_top_top,
148 Settings::values.custom_top_right, Settings::values.custom_top_bottom};
149 MathUtil::Rectangle<unsigned> bot_screen{
150 Settings::values.custom_bottom_left, Settings::values.custom_bottom_top,
151 Settings::values.custom_bottom_right, Settings::values.custom_bottom_bottom};
152
153 res.top_screen = top_screen;
154 res.bottom_screen = bot_screen;
155 return res;
156}
138} 157}
diff --git a/src/common/framebuffer_layout.h b/src/common/framebuffer_layout.h
index a125646a3..f1df5c55a 100644
--- a/src/common/framebuffer_layout.h
+++ b/src/common/framebuffer_layout.h
@@ -44,4 +44,12 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa
44 * @return Newly created FramebufferLayout object with default screen regions initialized 44 * @return Newly created FramebufferLayout object with default screen regions initialized
45 */ 45 */
46FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); 46FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
47
48/**
49 * Factory method for constructing a custom FramebufferLayout
50 * @param width Window framebuffer width in pixels
51 * @param height Window framebuffer height in pixels
52 * @return Newly created FramebufferLayout object with default screen regions initialized
53 */
54FramebufferLayout CustomFrameLayout(unsigned width, unsigned height);
47} 55}
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 737e1d57f..42f6a9918 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -71,6 +71,7 @@ namespace Log {
71 CLS(Audio) \ 71 CLS(Audio) \
72 SUB(Audio, DSP) \ 72 SUB(Audio, DSP) \
73 SUB(Audio, Sink) \ 73 SUB(Audio, Sink) \
74 CLS(Input) \
74 CLS(Loader) 75 CLS(Loader)
75 76
76// GetClassName is a macro defined by Windows.h, grrr... 77// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 4b0f8ff03..1b905f66c 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -89,6 +89,7 @@ enum class Class : ClassType {
89 Audio_DSP, ///< The HLE implementation of the DSP 89 Audio_DSP, ///< The HLE implementation of the DSP
90 Audio_Sink, ///< Emulator audio output backend 90 Audio_Sink, ///< Emulator audio output backend
91 Loader, ///< ROM loader 91 Loader, ///< ROM loader
92 Input, ///< Input emulation
92 Count ///< Total number of logging classes 93 Count ///< Total number of logging classes
93}; 94};
94 95
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
new file mode 100644
index 000000000..3a6ef8c27
--- /dev/null
+++ b/src/common/param_package.cpp
@@ -0,0 +1,120 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <vector>
7#include "common/logging/log.h"
8#include "common/param_package.h"
9#include "common/string_util.h"
10
11namespace Common {
12
13constexpr char KEY_VALUE_SEPARATOR = ':';
14constexpr char PARAM_SEPARATOR = ',';
15constexpr char ESCAPE_CHARACTER = '$';
16const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'};
17const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'};
18const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'};
19
20ParamPackage::ParamPackage(const std::string& serialized) {
21 std::vector<std::string> pairs;
22 Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
23
24 for (const std::string& pair : pairs) {
25 std::vector<std::string> key_value;
26 Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
27 if (key_value.size() != 2) {
28 LOG_ERROR(Common, "invalid key pair %s", pair.c_str());
29 continue;
30 }
31
32 for (std::string& part : key_value) {
33 part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
34 part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
35 part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
36 }
37
38 Set(key_value[0], key_value[1]);
39 }
40}
41
42ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : data(list) {}
43
44std::string ParamPackage::Serialize() const {
45 if (data.empty())
46 return "";
47
48 std::string result;
49
50 for (const auto& pair : data) {
51 std::array<std::string, 2> key_value{{pair.first, pair.second}};
52 for (std::string& part : key_value) {
53 part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
54 part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
55 part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
56 }
57 result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
58 }
59
60 result.pop_back(); // discard the trailing PARAM_SEPARATOR
61 return result;
62}
63
64std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
65 auto pair = data.find(key);
66 if (pair == data.end()) {
67 LOG_DEBUG(Common, "key %s not found", key.c_str());
68 return default_value;
69 }
70
71 return pair->second;
72}
73
74int ParamPackage::Get(const std::string& key, int default_value) const {
75 auto pair = data.find(key);
76 if (pair == data.end()) {
77 LOG_DEBUG(Common, "key %s not found", key.c_str());
78 return default_value;
79 }
80
81 try {
82 return std::stoi(pair->second);
83 } catch (const std::logic_error&) {
84 LOG_ERROR(Common, "failed to convert %s to int", pair->second.c_str());
85 return default_value;
86 }
87}
88
89float ParamPackage::Get(const std::string& key, float default_value) const {
90 auto pair = data.find(key);
91 if (pair == data.end()) {
92 LOG_DEBUG(Common, "key %s not found", key.c_str());
93 return default_value;
94 }
95
96 try {
97 return std::stof(pair->second);
98 } catch (const std::logic_error&) {
99 LOG_ERROR(Common, "failed to convert %s to float", pair->second.c_str());
100 return default_value;
101 }
102}
103
104void ParamPackage::Set(const std::string& key, const std::string& value) {
105 data[key] = value;
106}
107
108void ParamPackage::Set(const std::string& key, int value) {
109 data[key] = std::to_string(value);
110}
111
112void ParamPackage::Set(const std::string& key, float value) {
113 data[key] = std::to_string(value);
114}
115
116bool ParamPackage::Has(const std::string& key) const {
117 return data.find(key) != data.end();
118}
119
120} // namespace Common
diff --git a/src/common/param_package.h b/src/common/param_package.h
new file mode 100644
index 000000000..c4c11b221
--- /dev/null
+++ b/src/common/param_package.h
@@ -0,0 +1,40 @@
1// Copyright 2017 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 <string>
9#include <unordered_map>
10
11namespace Common {
12
13/// A string-based key-value container supporting serializing to and deserializing from a string
14class ParamPackage {
15public:
16 using DataType = std::unordered_map<std::string, std::string>;
17
18 ParamPackage() = default;
19 explicit ParamPackage(const std::string& serialized);
20 ParamPackage(std::initializer_list<DataType::value_type> list);
21 ParamPackage(const ParamPackage& other) = default;
22 ParamPackage(ParamPackage&& other) = default;
23
24 ParamPackage& operator=(const ParamPackage& other) = default;
25 ParamPackage& operator=(ParamPackage&& other) = default;
26
27 std::string Serialize() const;
28 std::string Get(const std::string& key, const std::string& default_value) const;
29 int Get(const std::string& key, int default_value) const;
30 float Get(const std::string& key, float default_value) const;
31 void Set(const std::string& key, const std::string& value);
32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const;
35
36private:
37 DataType data;
38};
39
40} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index 370ae2c80..2cb3ab9cc 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -8,9 +8,9 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "cpu_detect.h" 9#include "cpu_detect.h"
10 10
11namespace Common { 11#ifdef _MSC_VER
12 12#include <intrin.h>
13#ifndef _MSC_VER 13#else
14 14
15#if defined(__DragonFly__) || defined(__FreeBSD__) 15#if defined(__DragonFly__) || defined(__FreeBSD__)
16// clang-format off 16// clang-format off
@@ -37,13 +37,15 @@ static inline void __cpuid(int info[4], int function_id) {
37} 37}
38 38
39#define _XCR_XFEATURE_ENABLED_MASK 0 39#define _XCR_XFEATURE_ENABLED_MASK 0
40static u64 _xgetbv(u32 index) { 40static inline u64 _xgetbv(u32 index) {
41 u32 eax, edx; 41 u32 eax, edx;
42 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); 42 __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
43 return ((u64)edx << 32) | eax; 43 return ((u64)edx << 32) | eax;
44} 44}
45 45
46#endif // ifndef _MSC_VER 46#endif // _MSC_VER
47
48namespace Common {
47 49
48// Detects the various CPU features 50// Detects the various CPU features
49static CPUCaps Detect() { 51static CPUCaps Detect() {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ffd67f074..61a0b1cc3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -34,7 +34,6 @@ set(SRCS
34 frontend/camera/factory.cpp 34 frontend/camera/factory.cpp
35 frontend/camera/interface.cpp 35 frontend/camera/interface.cpp
36 frontend/emu_window.cpp 36 frontend/emu_window.cpp
37 frontend/key_map.cpp
38 frontend/motion_emu.cpp 37 frontend/motion_emu.cpp
39 gdbstub/gdbstub.cpp 38 gdbstub/gdbstub.cpp
40 hle/config_mem.cpp 39 hle/config_mem.cpp
@@ -218,7 +217,7 @@ set(HEADERS
218 frontend/camera/factory.h 217 frontend/camera/factory.h
219 frontend/camera/interface.h 218 frontend/camera/interface.h
220 frontend/emu_window.h 219 frontend/emu_window.h
221 frontend/key_map.h 220 frontend/input.h
222 frontend/motion_emu.h 221 frontend/motion_emu.h
223 gdbstub/gdbstub.h 222 gdbstub/gdbstub.h
224 hle/config_mem.h 223 hle/config_mem.h
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index dd2fb167f..f454e7840 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -173,7 +173,7 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
173ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, 173ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location,
174 bool shared) 174 bool shared)
175 : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) { 175 : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
176 LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str()); 176 LOG_DEBUG(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
177} 177}
178 178
179bool ArchiveFactory_ExtSaveData::Initialize() { 179bool ArchiveFactory_ExtSaveData::Initialize() {
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 72ff05c65..679909d06 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -306,7 +306,7 @@ u64 SDMCArchive::GetFreeBytes() const {
306 306
307ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory) 307ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
308 : sdmc_directory(sdmc_directory) { 308 : sdmc_directory(sdmc_directory) {
309 LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); 309 LOG_DEBUG(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
310} 310}
311 311
312bool ArchiveFactory_SDMC::Initialize() { 312bool ArchiveFactory_SDMC::Initialize() {
diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp
index 2aafc9b1d..244aef48a 100644
--- a/src/core/file_sys/archive_sdmcwriteonly.cpp
+++ b/src/core/file_sys/archive_sdmcwriteonly.cpp
@@ -32,7 +32,7 @@ ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory
32 32
33ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point) 33ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
34 : sdmc_directory(mount_point) { 34 : sdmc_directory(mount_point) {
35 LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str()); 35 LOG_DEBUG(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
36} 36}
37 37
38bool ArchiveFactory_SDMCWriteOnly::Initialize() { 38bool ArchiveFactory_SDMCWriteOnly::Initialize() {
diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp
index e01357891..f31a68038 100644
--- a/src/core/file_sys/archive_source_sd_savedata.cpp
+++ b/src/core/file_sys/archive_source_sd_savedata.cpp
@@ -39,7 +39,7 @@ std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 progr
39 39
40ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory) 40ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory)
41 : mount_point(GetSaveDataContainerPath(sdmc_directory)) { 41 : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
42 LOG_INFO(Service_FS, "Directory %s set as SaveData.", mount_point.c_str()); 42 LOG_DEBUG(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
43} 43}
44 44
45ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) { 45ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index a155b657d..5fdb3a7e8 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -7,33 +7,9 @@
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/frontend/key_map.h" 10#include "core/settings.h"
11#include "video_core/video_core.h" 11#include "video_core/video_core.h"
12 12
13void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
14 pad_state.hex |= pad.hex;
15}
16
17void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
18 pad_state.hex &= ~pad.hex;
19}
20
21void EmuWindow::CirclePadUpdated(float x, float y) {
22 constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
23
24 // Make sure the coordinates are in the unit circle,
25 // otherwise normalize it.
26 float r = x * x + y * y;
27 if (r > 1) {
28 r = std::sqrt(r);
29 x /= r;
30 y /= r;
31 }
32
33 circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
34 circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
35}
36
37/** 13/**
38 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
39 * @param layout FramebufferLayout object describing the framebuffer size and screen positions 15 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -113,17 +89,21 @@ void EmuWindow::GyroscopeChanged(float x, float y, float z) {
113 89
114void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 90void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
115 Layout::FramebufferLayout layout; 91 Layout::FramebufferLayout layout;
116 switch (Settings::values.layout_option) { 92 if (Settings::values.custom_layout == true) {
117 case Settings::LayoutOption::SingleScreen: 93 layout = Layout::CustomFrameLayout(width, height);
118 layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen); 94 } else {
119 break; 95 switch (Settings::values.layout_option) {
120 case Settings::LayoutOption::LargeScreen: 96 case Settings::LayoutOption::SingleScreen:
121 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); 97 layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen);
122 break; 98 break;
123 case Settings::LayoutOption::Default: 99 case Settings::LayoutOption::LargeScreen:
124 default: 100 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
125 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); 101 break;
126 break; 102 case Settings::LayoutOption::Default:
103 default:
104 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
105 break;
106 }
127 } 107 }
128 NotifyFramebufferLayoutChanged(layout); 108 NotifyFramebufferLayoutChanged(layout);
129} 109}
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 1ba64c92b..36f2667fa 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -10,7 +10,6 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/framebuffer_layout.h" 11#include "common/framebuffer_layout.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "core/hle/service/hid/hid.h"
14 13
15/** 14/**
16 * Abstraction class used to provide an interface between emulation code and the frontend 15 * Abstraction class used to provide an interface between emulation code and the frontend
@@ -52,30 +51,6 @@ public:
52 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 51 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
53 virtual void DoneCurrent() = 0; 52 virtual void DoneCurrent() = 0;
54 53
55 virtual void ReloadSetKeymaps() = 0;
56
57 /**
58 * Signals a button press action to the HID module.
59 * @param pad_state indicates which button to press
60 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
61 */
62 void ButtonPressed(Service::HID::PadState pad_state);
63
64 /**
65 * Signals a button release action to the HID module.
66 * @param pad_state indicates which button to press
67 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
68 */
69 void ButtonReleased(Service::HID::PadState pad_state);
70
71 /**
72 * Signals a circle pad change action to the HID module.
73 * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
74 * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
75 * @note the coordinates will be normalized if the radius is larger than 1
76 */
77 void CirclePadUpdated(float x, float y);
78
79 /** 54 /**
80 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 55 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
81 * @param framebuffer_x Framebuffer x-coordinate that was pressed 56 * @param framebuffer_x Framebuffer x-coordinate that was pressed
@@ -115,27 +90,6 @@ public:
115 void GyroscopeChanged(float x, float y, float z); 90 void GyroscopeChanged(float x, float y, float z);
116 91
117 /** 92 /**
118 * Gets the current pad state (which buttons are pressed).
119 * @note This should be called by the core emu thread to get a state set by the window thread.
120 * @note This doesn't include analog input like circle pad direction
121 * @todo Fix this function to be thread-safe.
122 * @return PadState object indicating the current pad state
123 */
124 Service::HID::PadState GetPadState() const {
125 return pad_state;
126 }
127
128 /**
129 * Gets the current circle pad state.
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @todo Fix this function to be thread-safe.
132 * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
133 */
134 std::tuple<s16, s16> GetCirclePadState() const {
135 return std::make_tuple(circle_pad_x, circle_pad_y);
136 }
137
138 /**
139 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
140 * @note This should be called by the core emu thread to get a state set by the window thread. 94 * @note This should be called by the core emu thread to get a state set by the window thread.
141 * @todo Fix this function to be thread-safe. 95 * @todo Fix this function to be thread-safe.
@@ -230,11 +184,8 @@ protected:
230 // TODO: Find a better place to set this. 184 // TODO: Find a better place to set this.
231 config.min_client_area_size = std::make_pair(400u, 480u); 185 config.min_client_area_size = std::make_pair(400u, 480u);
232 active_config = config; 186 active_config = config;
233 pad_state.hex = 0;
234 touch_x = 0; 187 touch_x = 0;
235 touch_y = 0; 188 touch_y = 0;
236 circle_pad_x = 0;
237 circle_pad_y = 0;
238 touch_pressed = false; 189 touch_pressed = false;
239 accel_x = 0; 190 accel_x = 0;
240 accel_y = -512; 191 accel_y = -512;
@@ -304,9 +255,6 @@ private:
304 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
305 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
306 257
307 s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
308 s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
309
310 std::mutex accel_mutex; 258 std::mutex accel_mutex;
311 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units 259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
312 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units 260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
@@ -321,6 +269,4 @@ private:
321 * Clip the provided coordinates to be inside the touchscreen area. 269 * Clip the provided coordinates to be inside the touchscreen area.
322 */ 270 */
323 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); 271 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
324
325 Service::HID::PadState pad_state;
326}; 272};
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
new file mode 100644
index 000000000..0a5713dc0
--- /dev/null
+++ b/src/core/frontend/input.h
@@ -0,0 +1,110 @@
1// Copyright 2017 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 <string>
9#include <tuple>
10#include <unordered_map>
11#include <utility>
12#include "common/logging/log.h"
13#include "common/param_package.h"
14
15namespace Input {
16
17/// An abstract class template for an input device (a button, an analog input, etc.).
18template <typename StatusType>
19class InputDevice {
20public:
21 virtual ~InputDevice() = default;
22 virtual StatusType GetStatus() const {
23 return {};
24 }
25};
26
27/// An abstract class template for a factory that can create input devices.
28template <typename InputDeviceType>
29class Factory {
30public:
31 virtual ~Factory() = default;
32 virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
33};
34
35namespace Impl {
36
37template <typename InputDeviceType>
38using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
39
40template <typename InputDeviceType>
41struct FactoryList {
42 static FactoryListType<InputDeviceType> list;
43};
44
45template <typename InputDeviceType>
46FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
47
48} // namespace Impl
49
50/**
51 * Registers an input device factory.
52 * @tparam InputDeviceType the type of input devices the factory can create
53 * @param name the name of the factory. Will be used to match the "engine" parameter when creating
54 * a device
55 * @param factory the factory object to register
56 */
57template <typename InputDeviceType>
58void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
59 auto pair = std::make_pair(name, std::move(factory));
60 if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
61 LOG_ERROR(Input, "Factory %s already registered", name.c_str());
62 }
63}
64
65/**
66 * Unregisters an input device factory.
67 * @tparam InputDeviceType the type of input devices the factory can create
68 * @param name the name of the factory to unregister
69 */
70template <typename InputDeviceType>
71void UnregisterFactory(const std::string& name) {
72 if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
73 LOG_ERROR(Input, "Factory %s not registered", name.c_str());
74 }
75}
76
77/**
78 * Create an input device from given paramters.
79 * @tparam InputDeviceType the type of input devices to create
80 * @param params a serialized ParamPackage string contains all parameters for creating the device
81 */
82template <typename InputDeviceType>
83std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
84 const Common::ParamPackage package(params);
85 const std::string engine = package.Get("engine", "null");
86 const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
87 const auto pair = factory_list.find(engine);
88 if (pair == factory_list.end()) {
89 if (engine != "null") {
90 LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str());
91 }
92 return std::make_unique<InputDeviceType>();
93 }
94 return pair->second->Create(package);
95}
96
97/**
98 * A button device is an input device that returns bool as status.
99 * true for pressed; false for released.
100 */
101using ButtonDevice = InputDevice<bool>;
102
103/**
104 * An analog device is an input device that returns a tuple of x and y coordinates as status. The
105 * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
106 * direction
107 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>;
109
110} // namespace Input
diff --git a/src/core/frontend/key_map.cpp b/src/core/frontend/key_map.cpp
deleted file mode 100644
index 15f0e079c..000000000
--- a/src/core/frontend/key_map.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <map>
6#include "core/frontend/emu_window.h"
7#include "core/frontend/key_map.h"
8
9namespace KeyMap {
10
11// TODO (wwylele): currently we treat c-stick as four direction buttons
12// and map it directly to EmuWindow::ButtonPressed.
13// It should go the analog input way like circle pad does.
14const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
15 Service::HID::PAD_A,
16 Service::HID::PAD_B,
17 Service::HID::PAD_X,
18 Service::HID::PAD_Y,
19 Service::HID::PAD_L,
20 Service::HID::PAD_R,
21 Service::HID::PAD_ZL,
22 Service::HID::PAD_ZR,
23 Service::HID::PAD_START,
24 Service::HID::PAD_SELECT,
25 Service::HID::PAD_NONE,
26 Service::HID::PAD_UP,
27 Service::HID::PAD_DOWN,
28 Service::HID::PAD_LEFT,
29 Service::HID::PAD_RIGHT,
30 Service::HID::PAD_C_UP,
31 Service::HID::PAD_C_DOWN,
32 Service::HID::PAD_C_LEFT,
33 Service::HID::PAD_C_RIGHT,
34
35 IndirectTarget::CirclePadUp,
36 IndirectTarget::CirclePadDown,
37 IndirectTarget::CirclePadLeft,
38 IndirectTarget::CirclePadRight,
39 IndirectTarget::CirclePadModifier,
40}};
41
42static std::map<HostDeviceKey, KeyTarget> key_map;
43static int next_device_id = 0;
44
45static bool circle_pad_up = false;
46static bool circle_pad_down = false;
47static bool circle_pad_left = false;
48static bool circle_pad_right = false;
49static bool circle_pad_modifier = false;
50
51static void UpdateCirclePad(EmuWindow& emu_window) {
52 constexpr float SQRT_HALF = 0.707106781f;
53 int x = 0, y = 0;
54
55 if (circle_pad_right)
56 ++x;
57 if (circle_pad_left)
58 --x;
59 if (circle_pad_up)
60 ++y;
61 if (circle_pad_down)
62 --y;
63
64 float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f;
65 emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF),
66 y * modifier * (x == 0 ? 1.0f : SQRT_HALF));
67}
68
69int NewDeviceId() {
70 return next_device_id++;
71}
72
73void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
74 key_map[key] = target;
75}
76
77void ClearKeyMapping(int device_id) {
78 auto iter = key_map.begin();
79 while (iter != key_map.end()) {
80 if (iter->first.device_id == device_id)
81 key_map.erase(iter++);
82 else
83 ++iter;
84 }
85}
86
87void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
88 auto target = key_map.find(key);
89 if (target == key_map.end())
90 return;
91
92 if (target->second.direct) {
93 emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
94 } else {
95 switch (target->second.target.indirect_target) {
96 case IndirectTarget::CirclePadUp:
97 circle_pad_up = true;
98 UpdateCirclePad(emu_window);
99 break;
100 case IndirectTarget::CirclePadDown:
101 circle_pad_down = true;
102 UpdateCirclePad(emu_window);
103 break;
104 case IndirectTarget::CirclePadLeft:
105 circle_pad_left = true;
106 UpdateCirclePad(emu_window);
107 break;
108 case IndirectTarget::CirclePadRight:
109 circle_pad_right = true;
110 UpdateCirclePad(emu_window);
111 break;
112 case IndirectTarget::CirclePadModifier:
113 circle_pad_modifier = true;
114 UpdateCirclePad(emu_window);
115 break;
116 }
117 }
118}
119
120void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key) {
121 auto target = key_map.find(key);
122 if (target == key_map.end())
123 return;
124
125 if (target->second.direct) {
126 emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
127 } else {
128 switch (target->second.target.indirect_target) {
129 case IndirectTarget::CirclePadUp:
130 circle_pad_up = false;
131 UpdateCirclePad(emu_window);
132 break;
133 case IndirectTarget::CirclePadDown:
134 circle_pad_down = false;
135 UpdateCirclePad(emu_window);
136 break;
137 case IndirectTarget::CirclePadLeft:
138 circle_pad_left = false;
139 UpdateCirclePad(emu_window);
140 break;
141 case IndirectTarget::CirclePadRight:
142 circle_pad_right = false;
143 UpdateCirclePad(emu_window);
144 break;
145 case IndirectTarget::CirclePadModifier:
146 circle_pad_modifier = false;
147 UpdateCirclePad(emu_window);
148 break;
149 }
150 }
151}
152}
diff --git a/src/core/frontend/key_map.h b/src/core/frontend/key_map.h
deleted file mode 100644
index 040794578..000000000
--- a/src/core/frontend/key_map.h
+++ /dev/null
@@ -1,93 +0,0 @@
1// Copyright 2014 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 <tuple>
9#include "core/hle/service/hid/hid.h"
10
11class EmuWindow;
12
13namespace KeyMap {
14
15/**
16 * Represents key mapping targets that are not real 3DS buttons.
17 * They will be handled by KeyMap and translated to 3DS input.
18 */
19enum class IndirectTarget {
20 CirclePadUp,
21 CirclePadDown,
22 CirclePadLeft,
23 CirclePadRight,
24 CirclePadModifier,
25};
26
27/**
28 * Represents a key mapping target. It can be a PadState that represents real 3DS buttons,
29 * or an IndirectTarget.
30 */
31struct KeyTarget {
32 bool direct;
33 union {
34 u32 direct_target_hex;
35 IndirectTarget indirect_target;
36 } target;
37
38 KeyTarget() : direct(true) {
39 target.direct_target_hex = 0;
40 }
41
42 KeyTarget(Service::HID::PadState pad) : direct(true) {
43 target.direct_target_hex = pad.hex;
44 }
45
46 KeyTarget(IndirectTarget i) : direct(false) {
47 target.indirect_target = i;
48 }
49};
50
51/**
52 * Represents a key for a specific host device.
53 */
54struct HostDeviceKey {
55 int key_code;
56 int device_id; ///< Uniquely identifies a host device
57
58 bool operator<(const HostDeviceKey& other) const {
59 return std::tie(key_code, device_id) < std::tie(other.key_code, other.device_id);
60 }
61
62 bool operator==(const HostDeviceKey& other) const {
63 return std::tie(key_code, device_id) == std::tie(other.key_code, other.device_id);
64 }
65};
66
67extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
68
69/**
70 * Generates a new device id, which uniquely identifies a host device within KeyMap.
71 */
72int NewDeviceId();
73
74/**
75 * Maps a device-specific key to a target (a PadState or an IndirectTarget).
76 */
77void SetKeyMapping(HostDeviceKey key, KeyTarget target);
78
79/**
80 * Clears all key mappings belonging to one device.
81 */
82void ClearKeyMapping(int device_id);
83
84/**
85 * Maps a key press action and call the corresponding function in EmuWindow
86 */
87void PressKey(EmuWindow& emu_window, HostDeviceKey key);
88
89/**
90 * Maps a key release action and call the corresponding function in EmuWindow
91 */
92void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
93}
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 7875971ce..f6eb900f0 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -256,9 +256,9 @@ void Wrap() {
256 func(((s64)PARAM(1) << 32) | PARAM(0)); 256 func(((s64)PARAM(1) << 32) | PARAM(0));
257} 257}
258 258
259template <void func(const char*)> 259template <void func(const char*, int len)>
260void Wrap() { 260void Wrap() {
261 func((char*)Memory::GetPointer(PARAM(0))); 261 func((char*)Memory::GetPointer(PARAM(0)), PARAM(1));
262} 262}
263 263
264template <void func(u8)> 264template <void func(u8)>
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index cd9a5863d..3a5d481a5 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -10,7 +10,8 @@
10 10
11namespace Kernel { 11namespace Kernel {
12 12
13static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header 13/// Offset into command buffer of header
14static const int kCommandHeaderOffset = 0x80;
14 15
15/** 16/**
16 * Returns a pointer to the command buffer in the current thread's TLS 17 * Returns a pointer to the command buffer in the current thread's TLS
@@ -26,8 +27,8 @@ inline u32* GetCommandBuffer(const int offset = 0) {
26 offset); 27 offset);
27} 28}
28 29
29static const int kStaticBuffersOffset = 30/// Offset into static buffers, relative to command buffer header
30 0x100; ///< Offset into static buffers, relative to command buffer header 31static const int kStaticBuffersOffset = 0x100;
31 32
32/** 33/**
33 * Returns a pointer to the static buffers area in the current thread's TLS 34 * Returns a pointer to the static buffers area in the current thread's TLS
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 323158bb5..06c4c5a85 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -27,6 +27,24 @@ public:
27 DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", 27 DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
28 header.raw); 28 header.raw);
29 } 29 }
30
31 void Skip(unsigned size_in_words, bool set_to_null) {
32 if (set_to_null)
33 memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
34 index += size_in_words;
35 }
36
37 /**
38 * @brief Retrieves the address of a static buffer, used when a buffer is needed for output
39 * @param buffer_id The index of the static buffer
40 * @param data_size If non-null, will store the size of the buffer
41 */
42 VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const {
43 u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2;
44 if (data_size)
45 *data_size = StaticBufferDescInfo{static_buffer[0]}.size;
46 return static_buffer[1];
47 }
30}; 48};
31 49
32class RequestBuilder : public RequestHelperBase { 50class RequestBuilder : public RequestHelperBase {
@@ -50,14 +68,8 @@ public:
50 template <typename T> 68 template <typename T>
51 void Push(T value); 69 void Push(T value);
52 70
53 void Push(u32 value) {
54 cmdbuf[index++] = value;
55 }
56 template <typename First, typename... Other> 71 template <typename First, typename... Other>
57 void Push(const First& first_value, const Other&... other_values) { 72 void Push(const First& first_value, const Other&... other_values);
58 Push(first_value);
59 Push(other_values...);
60 }
61 73
62 /** 74 /**
63 * @brief Copies the content of the given trivially copyable class to the buffer as a normal 75 * @brief Copies the content of the given trivially copyable class to the buffer as a normal
@@ -65,59 +77,96 @@ public:
65 * @note: The input class must be correctly packed/padded to fit hardware layout. 77 * @note: The input class must be correctly packed/padded to fit hardware layout.
66 */ 78 */
67 template <typename T> 79 template <typename T>
68 void PushRaw(const T& value) { 80 void PushRaw(const T& value);
69 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
70 std::memcpy(cmdbuf + index, &value, sizeof(T));
71 index += (sizeof(T) + 3) / 4; // round up to word length
72 }
73 81
74 // TODO : ensure that translate params are added after all regular params 82 // TODO : ensure that translate params are added after all regular params
75 template <typename... H> 83 template <typename... H>
76 void PushCopyHandles(H... handles) { 84 void PushCopyHandles(H... handles);
77 Push(CopyHandleDesc(sizeof...(H)));
78 Push(static_cast<Kernel::Handle>(handles)...);
79 }
80 85
81 template <typename... H> 86 template <typename... H>
82 void PushMoveHandles(H... handles) { 87 void PushMoveHandles(H... handles);
83 Push(MoveHandleDesc(sizeof...(H)));
84 Push(static_cast<Kernel::Handle>(handles)...);
85 }
86 88
87 void PushCurrentPIDHandle() { 89 void PushCurrentPIDHandle();
88 Push(CallingPidDesc());
89 Push(u32(0));
90 }
91 90
92 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { 91 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
93 Push(StaticBufferDesc(size, buffer_id));
94 Push(buffer_vaddr);
95 }
96 92
97 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms) { 93 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms);
98 Push(MappedBufferDesc(size, perms));
99 Push(buffer_vaddr);
100 }
101}; 94};
102 95
103/// Push /// 96/// Push ///
104 97
105template <> 98template <>
106inline void RequestBuilder::Push<u32>(u32 value) { 99inline void RequestBuilder::Push(u32 value) {
107 Push(value); 100 cmdbuf[index++] = value;
101}
102
103template <typename T>
104void RequestBuilder::PushRaw(const T& value) {
105 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
106 std::memcpy(cmdbuf + index, &value, sizeof(T));
107 index += (sizeof(T) + 3) / 4; // round up to word length
108} 108}
109 109
110template <> 110template <>
111inline void RequestBuilder::Push<u64>(u64 value) { 111inline void RequestBuilder::Push(u8 value) {
112 PushRaw(value);
113}
114
115template <>
116inline void RequestBuilder::Push(u16 value) {
117 PushRaw(value);
118}
119
120template <>
121inline void RequestBuilder::Push(u64 value) {
112 Push(static_cast<u32>(value)); 122 Push(static_cast<u32>(value));
113 Push(static_cast<u32>(value >> 32)); 123 Push(static_cast<u32>(value >> 32));
114} 124}
115 125
116template <> 126template <>
117inline void RequestBuilder::Push<ResultCode>(ResultCode value) { 127inline void RequestBuilder::Push(bool value) {
128 Push(static_cast<u8>(value));
129}
130
131template <>
132inline void RequestBuilder::Push(ResultCode value) {
118 Push(value.raw); 133 Push(value.raw);
119} 134}
120 135
136template <typename First, typename... Other>
137void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
138 Push(first_value);
139 Push(other_values...);
140}
141
142template <typename... H>
143inline void RequestBuilder::PushCopyHandles(H... handles) {
144 Push(CopyHandleDesc(sizeof...(H)));
145 Push(static_cast<Kernel::Handle>(handles)...);
146}
147
148template <typename... H>
149inline void RequestBuilder::PushMoveHandles(H... handles) {
150 Push(MoveHandleDesc(sizeof...(H)));
151 Push(static_cast<Kernel::Handle>(handles)...);
152}
153
154inline void RequestBuilder::PushCurrentPIDHandle() {
155 Push(CallingPidDesc());
156 Push(u32(0));
157}
158
159inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) {
160 Push(StaticBufferDesc(size, buffer_id));
161 Push(buffer_vaddr);
162}
163
164inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
165 MappedBufferPermissions perms) {
166 Push(MappedBufferDesc(size, perms));
167 Push(buffer_vaddr);
168}
169
121class RequestParser : public RequestHelperBase { 170class RequestParser : public RequestHelperBase {
122public: 171public:
123 RequestParser(u32* command_buffer, Header command_header) 172 RequestParser(u32* command_buffer, Header command_header)
@@ -185,24 +234,60 @@ public:
185 */ 234 */
186 template <typename T> 235 template <typename T>
187 void PopRaw(T& value); 236 void PopRaw(T& value);
237
238 /**
239 * @brief Reads the next normal parameters as a struct, by copying it into a new value
240 * @note: The output class must be correctly packed/padded to fit hardware layout.
241 */
242 template <typename T>
243 T PopRaw();
188}; 244};
189 245
190/// Pop /// 246/// Pop ///
191 247
192template <> 248template <>
193inline u32 RequestParser::Pop<u32>() { 249inline u32 RequestParser::Pop() {
194 return cmdbuf[index++]; 250 return cmdbuf[index++];
195} 251}
196 252
253template <typename T>
254void RequestParser::PopRaw(T& value) {
255 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
256 std::memcpy(&value, cmdbuf + index, sizeof(T));
257 index += (sizeof(T) + 3) / 4; // round up to word length
258}
259
260template <typename T>
261T RequestParser::PopRaw() {
262 T value;
263 PopRaw(value);
264 return value;
265}
266
267template <>
268inline u8 RequestParser::Pop() {
269 return PopRaw<u8>();
270}
271
197template <> 272template <>
198inline u64 RequestParser::Pop<u64>() { 273inline u16 RequestParser::Pop() {
274 return PopRaw<u16>();
275}
276
277template <>
278inline u64 RequestParser::Pop() {
199 const u64 lsw = Pop<u32>(); 279 const u64 lsw = Pop<u32>();
200 const u64 msw = Pop<u32>(); 280 const u64 msw = Pop<u32>();
201 return msw << 32 | lsw; 281 return msw << 32 | lsw;
202} 282}
203 283
204template <> 284template <>
205inline ResultCode RequestParser::Pop<ResultCode>() { 285inline bool RequestParser::Pop() {
286 return Pop<u8>() != 0;
287}
288
289template <>
290inline ResultCode RequestParser::Pop() {
206 return ResultCode{Pop<u32>()}; 291 return ResultCode{Pop<u32>()};
207} 292}
208 293
@@ -265,11 +350,4 @@ inline VAddr RequestParser::PopMappedBuffer(size_t* data_size,
265 return Pop<VAddr>(); 350 return Pop<VAddr>();
266} 351}
267 352
268template <typename T>
269void RequestParser::PopRaw(T& value) {
270 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
271 std::memcpy(&value, cmdbuf + index, sizeof(T));
272 index += (sizeof(T) + 3) / 4; // round up to word length
273}
274
275} // namespace IPC 353} // namespace IPC
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index e57b19c2d..366d1eacf 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -49,13 +49,13 @@ void SendParameter(const MessageParameter& parameter) {
49} 49}
50 50
51void Initialize(Service::Interface* self) { 51void Initialize(Service::Interface* self) {
52 u32* cmd_buff = Kernel::GetCommandBuffer(); 52 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
53 u32 app_id = cmd_buff[1]; 53 u32 app_id = rp.Pop<u32>();
54 u32 flags = cmd_buff[2]; 54 u32 flags = rp.Pop<u32>();
55 55 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
56 cmd_buff[2] = IPC::CopyHandleDesc(2); 56 rb.Push(RESULT_SUCCESS);
57 cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); 57 rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(),
58 cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); 58 Kernel::g_handle_table.Create(parameter_event).MoveFrom());
59 59
60 // TODO(bunnei): Check if these events are cleared every time Initialize is called. 60 // TODO(bunnei): Check if these events are cleared every time Initialize is called.
61 notification_event->Clear(); 61 notification_event->Clear();
@@ -64,18 +64,16 @@ void Initialize(Service::Interface* self) {
64 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); 64 ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
65 lock->Release(); 65 lock->Release();
66 66
67 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
68
69 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); 67 LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags);
70} 68}
71 69
72void GetSharedFont(Service::Interface* self) { 70void GetSharedFont(Service::Interface* self) {
73 u32* cmd_buff = Kernel::GetCommandBuffer(); 71 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
74 72 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
75 if (!shared_font_mem) { 73 if (!shared_font_mem) {
76 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 74 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
77 cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); 75 rb.Push<u32>(-1); // TODO: Find the right error code
78 cmd_buff[1] = -1; // TODO: Find the right error code 76 rb.Skip(1 + 2, true);
79 return; 77 return;
80 } 78 }
81 79
@@ -87,103 +85,110 @@ void GetSharedFont(Service::Interface* self) {
87 BCFNT::RelocateSharedFont(shared_font_mem, target_address); 85 BCFNT::RelocateSharedFont(shared_font_mem, target_address);
88 shared_font_relocated = true; 86 shared_font_relocated = true;
89 } 87 }
90 cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); 88
91 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 89 rb.Push(RESULT_SUCCESS); // No error
92 // Since the SharedMemory interface doesn't provide the address at which the memory was 90 // Since the SharedMemory interface doesn't provide the address at which the memory was
93 // allocated, the real APT service calculates this address by scanning the entire address space 91 // allocated, the real APT service calculates this address by scanning the entire address space
94 // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font. 92 // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font.
95 cmd_buff[2] = target_address; 93 rb.Push(target_address);
96 cmd_buff[3] = IPC::CopyHandleDesc(); 94 rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom());
97 cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
98} 95}
99 96
100void NotifyToWait(Service::Interface* self) { 97void NotifyToWait(Service::Interface* self) {
101 u32* cmd_buff = Kernel::GetCommandBuffer(); 98 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x43, 1, 0); // 0x430040
102 u32 app_id = cmd_buff[1]; 99 u32 app_id = rp.Pop<u32>();
103 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 100 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
101 rb.Push(RESULT_SUCCESS); // No error
104 LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); 102 LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
105} 103}
106 104
107void GetLockHandle(Service::Interface* self) { 105void GetLockHandle(Service::Interface* self) {
108 u32* cmd_buff = Kernel::GetCommandBuffer(); 106 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0); // 0x10040
107
109 // Bits [0:2] are the applet type (System, Library, etc) 108 // Bits [0:2] are the applet type (System, Library, etc)
110 // Bit 5 tells the application that there's a pending APT parameter, 109 // Bit 5 tells the application that there's a pending APT parameter,
111 // this will cause the app to wait until parameter_event is signaled. 110 // this will cause the app to wait until parameter_event is signaled.
112 u32 applet_attributes = cmd_buff[1]; 111 u32 applet_attributes = rp.Pop<u32>();
113 112 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
114 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 113 rb.Push(RESULT_SUCCESS); // No error
115 114 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
116 cmd_buff[2] = applet_attributes; // Applet Attributes, this value is passed to Enable. 115 rb.Push<u32>(0); // Least significant bit = power button state
117 cmd_buff[3] = 0; // Least significant bit = power button state 116 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom();
118 cmd_buff[4] = IPC::CopyHandleDesc(); 117 rb.PushCopyHandles(handle_copy);
119 cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom(); 118
120 119 LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy,
121 LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", cmd_buff[5],
122 applet_attributes); 120 applet_attributes);
123} 121}
124 122
125void Enable(Service::Interface* self) { 123void Enable(Service::Interface* self) {
126 u32* cmd_buff = Kernel::GetCommandBuffer(); 124 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
127 u32 attributes = cmd_buff[1]; 125 u32 attributes = rp.Pop<u32>();
128 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 126 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
129 parameter_event->Signal(); // Let the application know that it has been started 127 rb.Push(RESULT_SUCCESS); // No error
128 parameter_event->Signal(); // Let the application know that it has been started
130 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); 129 LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes);
131} 130}
132 131
133void GetAppletManInfo(Service::Interface* self) { 132void GetAppletManInfo(Service::Interface* self) {
134 u32* cmd_buff = Kernel::GetCommandBuffer(); 133 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0); // 0x50040
135 u32 unk = cmd_buff[1]; 134 u32 unk = rp.Pop<u32>();
136 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 135 IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
137 cmd_buff[2] = 0; 136 rb.Push(RESULT_SUCCESS); // No error
138 cmd_buff[3] = 0; 137 rb.Push<u32>(0);
139 cmd_buff[4] = static_cast<u32>(AppletId::HomeMenu); // Home menu AppID 138 rb.Push<u32>(0);
140 cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly 139 rb.Push(static_cast<u32>(AppletId::HomeMenu)); // Home menu AppID
140 rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly
141 141
142 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); 142 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
143} 143}
144 144
145void IsRegistered(Service::Interface* self) { 145void IsRegistered(Service::Interface* self) {
146 u32* cmd_buff = Kernel::GetCommandBuffer(); 146 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
147 u32 app_id = cmd_buff[1]; 147 u32 app_id = rp.Pop<u32>();
148 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 148 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
149 rb.Push(RESULT_SUCCESS); // No error
149 150
150 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable 151 // TODO(Subv): An application is considered "registered" if it has already called APT::Enable
151 // handle this properly once we implement multiprocess support. 152 // handle this properly once we implement multiprocess support.
152 cmd_buff[2] = 0; // Set to not registered by default 153 bool is_registered = false; // Set to not registered by default
153 154
154 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { 155 if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) {
155 cmd_buff[2] = HLE::Applets::IsLibraryAppletRunning() ? 1 : 0; 156 is_registered = HLE::Applets::IsLibraryAppletRunning();
156 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { 157 } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) {
157 cmd_buff[2] = 1; // Set to registered 158 is_registered = true; // Set to registered
158 } 159 }
160 rb.Push(is_registered);
161
159 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 162 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
160} 163}
161 164
162void InquireNotification(Service::Interface* self) { 165void InquireNotification(Service::Interface* self) {
163 u32* cmd_buff = Kernel::GetCommandBuffer(); 166 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0); // 0xB0040
164 u32 app_id = cmd_buff[1]; 167 u32 app_id = rp.Pop<u32>();
165 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 168 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
166 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type 169 rb.Push(RESULT_SUCCESS); // No error
170 rb.Push(static_cast<u32>(SignalType::None)); // Signal type
167 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); 171 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
168} 172}
169 173
170void SendParameter(Service::Interface* self) { 174void SendParameter(Service::Interface* self) {
171 u32* cmd_buff = Kernel::GetCommandBuffer(); 175 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104
172 u32 src_app_id = cmd_buff[1]; 176 u32 src_app_id = rp.Pop<u32>();
173 u32 dst_app_id = cmd_buff[2]; 177 u32 dst_app_id = rp.Pop<u32>();
174 u32 signal_type = cmd_buff[3]; 178 u32 signal_type = rp.Pop<u32>();
175 u32 buffer_size = cmd_buff[4]; 179 u32 buffer_size = rp.Pop<u32>();
176 u32 value = cmd_buff[5]; 180 Kernel::Handle handle = rp.PopHandle();
177 u32 handle = cmd_buff[6]; 181 size_t size;
178 u32 size = cmd_buff[7]; 182 VAddr buffer = rp.PopStaticBuffer(&size);
179 u32 buffer = cmd_buff[8];
180 183
181 std::shared_ptr<HLE::Applets::Applet> dest_applet = 184 std::shared_ptr<HLE::Applets::Applet> dest_applet =
182 HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); 185 HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id));
183 186
187 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
188
184 if (dest_applet == nullptr) { 189 if (dest_applet == nullptr) {
185 LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); 190 LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id);
186 cmd_buff[1] = -1; // TODO(Subv): Find the right error code 191 rb.Push<u32>(-1); // TODO(Subv): Find the right error code
187 return; 192 return;
188 } 193 }
189 194
@@ -195,88 +200,104 @@ void SendParameter(Service::Interface* self) {
195 param.buffer.resize(buffer_size); 200 param.buffer.resize(buffer_size);
196 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); 201 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
197 202
198 cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; 203 rb.Push(dest_applet->ReceiveParameter(param));
199 204
200 LOG_WARNING( 205 LOG_WARNING(Service_APT,
201 Service_APT, 206 "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
202 "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," 207 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
203 "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", 208 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
204 src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer);
205} 209}
206 210
207void ReceiveParameter(Service::Interface* self) { 211void ReceiveParameter(Service::Interface* self) {
208 u32* cmd_buff = Kernel::GetCommandBuffer(); 212 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080
209 u32 app_id = cmd_buff[1]; 213 u32 app_id = rp.Pop<u32>();
210 u32 buffer_size = cmd_buff[2]; 214 u32 buffer_size = rp.Pop<u32>();
211 VAddr buffer = cmd_buff[0x104 >> 2]; 215
212 216 size_t static_buff_size;
213 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 217 VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
214 cmd_buff[2] = next_parameter.sender_id; 218 if (buffer_size > static_buff_size)
215 cmd_buff[3] = next_parameter.signal; // Signal type 219 LOG_WARNING(
216 cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size 220 Service_APT,
217 cmd_buff[5] = 0x10; 221 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
218 cmd_buff[6] = 0; 222 buffer_size, static_buff_size);
219 if (next_parameter.object != nullptr) 223
220 cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); 224 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
221 cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2; 225 rb.Push(RESULT_SUCCESS); // No error
222 cmd_buff[8] = buffer; 226 rb.Push(next_parameter.sender_id);
227 rb.Push(next_parameter.signal); // Signal type
228 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !");
229 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
230
231 rb.PushMoveHandles((next_parameter.object != nullptr)
232 ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom()
233 : 0);
234 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
223 235
224 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); 236 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
225 237
226 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 238 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
227} 239}
228 240
229void GlanceParameter(Service::Interface* self) { 241void GlanceParameter(Service::Interface* self) {
230 u32* cmd_buff = Kernel::GetCommandBuffer(); 242 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080
231 u32 app_id = cmd_buff[1]; 243 u32 app_id = rp.Pop<u32>();
232 u32 buffer_size = cmd_buff[2]; 244 u32 buffer_size = rp.Pop<u32>();
233 VAddr buffer = cmd_buff[0x104 >> 2]; 245
246 size_t static_buff_size;
247 VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
248 if (buffer_size > static_buff_size)
249 LOG_WARNING(
250 Service_APT,
251 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
252 buffer_size, static_buff_size);
234 253
235 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 254 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
236 cmd_buff[2] = next_parameter.sender_id; 255 rb.Push(RESULT_SUCCESS); // No error
237 cmd_buff[3] = next_parameter.signal; // Signal type 256 rb.Push(next_parameter.sender_id);
238 cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size 257 rb.Push(next_parameter.signal); // Signal type
239 cmd_buff[5] = 0x10; 258 ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !");
240 cmd_buff[6] = 0; 259 rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
241 if (next_parameter.object != nullptr)
242 cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
243 cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
244 cmd_buff[8] = buffer;
245 260
246 Memory::WriteBlock(buffer, next_parameter.buffer.data(), 261 rb.PushCopyHandles((next_parameter.object != nullptr)
247 std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size())); 262 ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom()
263 : 0);
264 rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
248 265
249 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 266 Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
267
268 LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
250} 269}
251 270
252void CancelParameter(Service::Interface* self) { 271void CancelParameter(Service::Interface* self) {
253 u32* cmd_buff = Kernel::GetCommandBuffer(); 272 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
254 u32 flag1 = cmd_buff[1];
255 u32 unk = cmd_buff[2];
256 u32 flag2 = cmd_buff[3];
257 u32 app_id = cmd_buff[4];
258 273
259 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 274 u32 check_sender = rp.Pop<u32>();
260 cmd_buff[2] = 1; // Set to Success 275 u32 sender_appid = rp.Pop<u32>();
276 u32 check_receiver = rp.Pop<u32>();
277 u32 receiver_appid = rp.Pop<u32>();
278 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
279 rb.Push(RESULT_SUCCESS); // No error
280 rb.Push(true); // Set to Success
261 281
262 LOG_WARNING(Service_APT, 282 LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, "
263 "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", flag1, 283 "check_receiver=0x%08X, receiver_appid=0x%08X",
264 unk, flag2, app_id); 284 check_sender, sender_appid, check_receiver, receiver_appid);
265} 285}
266 286
267void PrepareToStartApplication(Service::Interface* self) { 287void PrepareToStartApplication(Service::Interface* self) {
268 u32* cmd_buff = Kernel::GetCommandBuffer(); 288 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 5, 0); // 0x00150140
269 u32 title_info1 = cmd_buff[1]; 289 u32 title_info1 = rp.Pop<u32>();
270 u32 title_info2 = cmd_buff[2]; 290 u32 title_info2 = rp.Pop<u32>();
271 u32 title_info3 = cmd_buff[3]; 291 u32 title_info3 = rp.Pop<u32>();
272 u32 title_info4 = cmd_buff[4]; 292 u32 title_info4 = rp.Pop<u32>();
273 u32 flags = cmd_buff[5]; 293 u32 flags = rp.Pop<u32>();
274 294
275 if (flags & 0x00000100) { 295 if (flags & 0x00000100) {
276 unknown_ns_state_field = 1; 296 unknown_ns_state_field = 1;
277 } 297 }
278 298
279 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 299 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
300 rb.Push(RESULT_SUCCESS); // No error
280 301
281 LOG_WARNING(Service_APT, 302 LOG_WARNING(Service_APT,
282 "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X," 303 "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X,"
@@ -285,172 +306,188 @@ void PrepareToStartApplication(Service::Interface* self) {
285} 306}
286 307
287void StartApplication(Service::Interface* self) { 308void StartApplication(Service::Interface* self) {
288 u32* cmd_buff = Kernel::GetCommandBuffer(); 309 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 3, 4); // 0x001B00C4
289 u32 buffer1_size = cmd_buff[1]; 310 u32 buffer1_size = rp.Pop<u32>();
290 u32 buffer2_size = cmd_buff[2]; 311 u32 buffer2_size = rp.Pop<u32>();
291 u32 flag = cmd_buff[3]; 312 u32 flag = rp.Pop<u32>();
292 u32 size1 = cmd_buff[4]; 313 size_t size1;
293 u32 buffer1_ptr = cmd_buff[5]; 314 VAddr buffer1_ptr = rp.PopStaticBuffer(&size1);
294 u32 size2 = cmd_buff[6]; 315 size_t size2;
295 u32 buffer2_ptr = cmd_buff[7]; 316 VAddr buffer2_ptr = rp.PopStaticBuffer(&size2);
296 317
297 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 318 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
319 rb.Push(RESULT_SUCCESS); // No error
298 320
299 LOG_WARNING(Service_APT, 321 LOG_WARNING(Service_APT,
300 "(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X," 322 "(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X,"
301 "size1=0x%08X, buffer1_ptr=0x%08X, size2=0x%08X, buffer2_ptr=0x%08X", 323 "size1=0x%08zX, buffer1_ptr=0x%08X, size2=0x%08zX, buffer2_ptr=0x%08X",
302 buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr); 324 buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr);
303} 325}
304 326
305void AppletUtility(Service::Interface* self) { 327void AppletUtility(Service::Interface* self) {
306 u32* cmd_buff = Kernel::GetCommandBuffer(); 328 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4B, 3, 2); // 0x004B00C2
307 329
308 // These are from 3dbrew - I'm not really sure what they're used for. 330 // These are from 3dbrew - I'm not really sure what they're used for.
309 u32 command = cmd_buff[1]; 331 u32 utility_command = rp.Pop<u32>();
310 u32 buffer1_size = cmd_buff[2]; 332 u32 input_size = rp.Pop<u32>();
311 u32 buffer2_size = cmd_buff[3]; 333 u32 output_size = rp.Pop<u32>();
312 u32 buffer1_addr = cmd_buff[5]; 334 VAddr input_addr = rp.PopStaticBuffer();
313 u32 buffer2_addr = cmd_buff[65]; 335
336 VAddr output_addr = rp.PeekStaticBuffer(0);
314 337
315 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 338 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
339 rb.Push(RESULT_SUCCESS); // No error
316 340
317 LOG_WARNING(Service_APT, 341 LOG_WARNING(Service_APT,
318 "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " 342 "(STUBBED) called command=0x%08X, input_size=0x%08X, output_size=0x%08X, "
319 "buffer1_addr=0x%08X, buffer2_addr=0x%08X", 343 "input_addr=0x%08X, output_addr=0x%08X",
320 command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); 344 utility_command, input_size, output_size, input_addr, output_addr);
321} 345}
322 346
323void SetAppCpuTimeLimit(Service::Interface* self) { 347void SetAppCpuTimeLimit(Service::Interface* self) {
324 u32* cmd_buff = Kernel::GetCommandBuffer(); 348 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4F, 2, 0); // 0x4F0080
325 u32 value = cmd_buff[1]; 349 u32 value = rp.Pop<u32>();
326 cpu_percent = cmd_buff[2]; 350 cpu_percent = rp.Pop<u32>();
327 351
328 if (value != 1) { 352 if (value != 1) {
329 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); 353 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
330 } 354 }
331 355
332 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 356 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
357 rb.Push(RESULT_SUCCESS); // No error
333 358
334 LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value); 359 LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value);
335} 360}
336 361
337void GetAppCpuTimeLimit(Service::Interface* self) { 362void GetAppCpuTimeLimit(Service::Interface* self) {
338 u32* cmd_buff = Kernel::GetCommandBuffer(); 363 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x50, 1, 0); // 0x500040
339 u32 value = cmd_buff[1]; 364 u32 value = rp.Pop<u32>();
340 365
341 if (value != 1) { 366 if (value != 1) {
342 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); 367 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
343 } 368 }
344 369
345 cmd_buff[1] = RESULT_SUCCESS.raw; // No error 370 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
346 cmd_buff[2] = cpu_percent; 371 rb.Push(RESULT_SUCCESS); // No error
372 rb.Push(cpu_percent);
347 373
348 LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); 374 LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value);
349} 375}
350 376
351void PrepareToStartLibraryApplet(Service::Interface* self) { 377void PrepareToStartLibraryApplet(Service::Interface* self) {
352 u32* cmd_buff = Kernel::GetCommandBuffer(); 378 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
353 AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); 379 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
380
381 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
354 auto applet = HLE::Applets::Applet::Get(applet_id); 382 auto applet = HLE::Applets::Applet::Get(applet_id);
355 if (applet) { 383 if (applet) {
356 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 384 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
357 cmd_buff[1] = RESULT_SUCCESS.raw; 385 rb.Push(RESULT_SUCCESS);
358 } else { 386 } else {
359 cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; 387 rb.Push(HLE::Applets::Applet::Create(applet_id));
360 } 388 }
361 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); 389 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
362} 390}
363 391
364void PreloadLibraryApplet(Service::Interface* self) { 392void PreloadLibraryApplet(Service::Interface* self) {
365 u32* cmd_buff = Kernel::GetCommandBuffer(); 393 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
366 AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); 394 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
395
396 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
367 auto applet = HLE::Applets::Applet::Get(applet_id); 397 auto applet = HLE::Applets::Applet::Get(applet_id);
368 if (applet) { 398 if (applet) {
369 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); 399 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
370 cmd_buff[1] = RESULT_SUCCESS.raw; 400 rb.Push(RESULT_SUCCESS);
371 } else { 401 } else {
372 cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; 402 rb.Push(HLE::Applets::Applet::Create(applet_id));
373 } 403 }
374 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); 404 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
375} 405}
376 406
377void StartLibraryApplet(Service::Interface* self) { 407void StartLibraryApplet(Service::Interface* self) {
378 u32* cmd_buff = Kernel::GetCommandBuffer(); 408 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084
379 AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); 409 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
380 std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id); 410 std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id);
381 411
382 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); 412 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
383 413
384 if (applet == nullptr) { 414 if (applet == nullptr) {
385 LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); 415 LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id);
386 cmd_buff[1] = -1; // TODO(Subv): Find the right error code 416 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false);
417 rb.Push<u32>(-1); // TODO(Subv): Find the right error code
387 return; 418 return;
388 } 419 }
389 420
390 size_t buffer_size = cmd_buff[2]; 421 size_t buffer_size = rp.Pop<u32>();
391 VAddr buffer_addr = cmd_buff[6]; 422 Kernel::Handle handle = rp.PopHandle();
423 VAddr buffer_addr = rp.PopStaticBuffer();
392 424
393 AppletStartupParameter parameter; 425 AppletStartupParameter parameter;
394 parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); 426 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
395 parameter.buffer.resize(buffer_size); 427 parameter.buffer.resize(buffer_size);
396 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size()); 428 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
397 429
398 cmd_buff[1] = applet->Start(parameter).raw; 430 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
431 rb.Push(applet->Start(parameter));
399} 432}
400 433
401void CancelLibraryApplet(Service::Interface* self) { 434void CancelLibraryApplet(Service::Interface* self) {
402 u32* cmd_buff = Kernel::GetCommandBuffer(); 435 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3B, 1, 0); // 0x003B0040
403 u32 exiting = cmd_buff[1] & 0xFF; 436 bool exiting = rp.Pop<bool>();
404 437
405 cmd_buff[1] = 1; // TODO: Find the return code meaning 438 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
439 rb.Push<u32>(1); // TODO: Find the return code meaning
406 440
407 LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting); 441 LOG_WARNING(Service_APT, "(STUBBED) called exiting=%d", exiting);
408} 442}
409 443
410void SetScreenCapPostPermission(Service::Interface* self) { 444void SetScreenCapPostPermission(Service::Interface* self) {
411 u32* cmd_buff = Kernel::GetCommandBuffer(); 445 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x55, 1, 0); // 0x00550040
412 446
413 screen_capture_post_permission = static_cast<ScreencapPostPermission>(cmd_buff[1] & 0xF); 447 screen_capture_post_permission = static_cast<ScreencapPostPermission>(rp.Pop<u32>() & 0xF);
414 448
415 cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0); 449 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
416 cmd_buff[1] = RESULT_SUCCESS.raw; 450 rb.Push(RESULT_SUCCESS); // No error
417 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", 451 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
418 screen_capture_post_permission); 452 screen_capture_post_permission);
419} 453}
420 454
421void GetScreenCapPostPermission(Service::Interface* self) { 455void GetScreenCapPostPermission(Service::Interface* self) {
422 u32* cmd_buff = Kernel::GetCommandBuffer(); 456 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x56, 0, 0); // 0x00560000
423 457
424 cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0); 458 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
425 cmd_buff[1] = RESULT_SUCCESS.raw; 459 rb.Push(RESULT_SUCCESS); // No error
426 cmd_buff[2] = static_cast<u32>(screen_capture_post_permission); 460 rb.Push(static_cast<u32>(screen_capture_post_permission));
427 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", 461 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
428 screen_capture_post_permission); 462 screen_capture_post_permission);
429} 463}
430 464
431void GetAppletInfo(Service::Interface* self) { 465void GetAppletInfo(Service::Interface* self) {
432 u32* cmd_buff = Kernel::GetCommandBuffer(); 466 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 1, 0); // 0x60040
433 auto app_id = static_cast<AppletId>(cmd_buff[1]); 467 auto app_id = static_cast<AppletId>(rp.Pop<u32>());
434 468
435 if (auto applet = HLE::Applets::Applet::Get(app_id)) { 469 if (auto applet = HLE::Applets::Applet::Get(app_id)) {
436 // TODO(Subv): Get the title id for the current applet and write it in the response[2-3] 470 // TODO(Subv): Get the title id for the current applet and write it in the response[2-3]
437 cmd_buff[1] = RESULT_SUCCESS.raw; 471 IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
438 cmd_buff[4] = static_cast<u32>(Service::FS::MediaType::NAND); 472 rb.Push(RESULT_SUCCESS);
439 cmd_buff[5] = 1; // Registered 473 u64 title_id = 0;
440 cmd_buff[6] = 1; // Loaded 474 rb.Push(title_id);
441 cmd_buff[7] = 0; // Applet Attributes 475 rb.Push(static_cast<u32>(Service::FS::MediaType::NAND));
476 rb.Push(true); // Registered
477 rb.Push(true); // Loaded
478 rb.Push<u32>(0); // Applet Attributes
442 } else { 479 } else {
443 cmd_buff[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, 480 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
444 ErrorSummary::NotFound, ErrorLevel::Status) 481 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
445 .raw; 482 ErrorLevel::Status));
446 } 483 }
447 LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); 484 LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
448} 485}
449 486
450void GetStartupArgument(Service::Interface* self) { 487void GetStartupArgument(Service::Interface* self) {
451 u32* cmd_buff = Kernel::GetCommandBuffer(); 488 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x51, 2, 0); // 0x00510080
452 u32 parameter_size = cmd_buff[1]; 489 u32 parameter_size = rp.Pop<u32>();
453 StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]); 490 StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>());
454 491
455 if (parameter_size >= 0x300) { 492 if (parameter_size >= 0x300) {
456 LOG_ERROR( 493 LOG_ERROR(
@@ -460,7 +497,14 @@ void GetStartupArgument(Service::Interface* self) {
460 return; 497 return;
461 } 498 }
462 499
463 u32 addr = cmd_buff[65]; 500 size_t static_buff_size;
501 VAddr addr = rp.PeekStaticBuffer(0, &static_buff_size);
502 if (parameter_size > static_buff_size)
503 LOG_WARNING(
504 Service_APT,
505 "parameter_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
506 parameter_size, static_buff_size);
507
464 if (addr && parameter_size) { 508 if (addr && parameter_size) {
465 Memory::ZeroBlock(addr, parameter_size); 509 Memory::ZeroBlock(addr, parameter_size);
466 } 510 }
@@ -468,8 +512,10 @@ void GetStartupArgument(Service::Interface* self) {
468 LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x", 512 LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x",
469 startup_argument_type, parameter_size); 513 startup_argument_type, parameter_size);
470 514
471 cmd_buff[1] = RESULT_SUCCESS.raw; 515 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
472 cmd_buff[2] = 0; 516 rb.Push(RESULT_SUCCESS);
517 rb.Push<u32>(0);
518 rb.PushStaticBuffer(addr, parameter_size, 0);
473} 519}
474 520
475void Wrap(Service::Interface* self) { 521void Wrap(Service::Interface* self) {
@@ -554,7 +600,7 @@ void Unwrap(Service::Interface* self) {
554 // Decrypts the ciphertext using AES-CCM 600 // Decrypts the ciphertext using AES-CCM
555 auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap); 601 auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap);
556 602
557 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 603 IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
558 if (!pdata.empty()) { 604 if (!pdata.empty()) {
559 // Splits the plaintext and put the nonce in between 605 // Splits the plaintext and put the nonce in between
560 Memory::WriteBlock(output, pdata.data(), nonce_offset); 606 Memory::WriteBlock(output, pdata.data(), nonce_offset);
@@ -574,25 +620,25 @@ void Unwrap(Service::Interface* self) {
574} 620}
575 621
576void CheckNew3DSApp(Service::Interface* self) { 622void CheckNew3DSApp(Service::Interface* self) {
577 u32* cmd_buff = Kernel::GetCommandBuffer(); 623 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x101, 0, 0); // 0x01010000
578 624
625 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
579 if (unknown_ns_state_field) { 626 if (unknown_ns_state_field) {
580 cmd_buff[1] = RESULT_SUCCESS.raw; 627 rb.Push(RESULT_SUCCESS);
581 cmd_buff[2] = 0; 628 rb.Push<u32>(0);
582 } else { 629 } else {
583 PTM::CheckNew3DS(self); 630 PTM::CheckNew3DS(rb);
584 } 631 }
585 632
586 cmd_buff[0] = IPC::MakeHeader(0x101, 2, 0);
587 LOG_WARNING(Service_APT, "(STUBBED) called"); 633 LOG_WARNING(Service_APT, "(STUBBED) called");
588} 634}
589 635
590void CheckNew3DS(Service::Interface* self) { 636void CheckNew3DS(Service::Interface* self) {
591 u32* cmd_buff = Kernel::GetCommandBuffer(); 637 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x102, 0, 0); // 0x01020000
638 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
592 639
593 PTM::CheckNew3DS(self); 640 PTM::CheckNew3DS(rb);
594 641
595 cmd_buff[0] = IPC::MakeHeader(0x102, 2, 0);
596 LOG_WARNING(Service_APT, "(STUBBED) called"); 642 LOG_WARNING(Service_APT, "(STUBBED) called");
597} 643}
598 644
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 6f13cde27..4ddb1bc90 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -3,6 +3,8 @@
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 <array>
7#include <cryptopp/sha.h>
6#include "common/file_util.h" 8#include "common/file_util.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "common/string_util.h" 10#include "common/string_util.h"
@@ -176,14 +178,29 @@ void SecureInfoGetRegion(Service::Interface* self) {
176} 178}
177 179
178void GenHashConsoleUnique(Service::Interface* self) { 180void GenHashConsoleUnique(Service::Interface* self) {
179 u32* cmd_buff = Kernel::GetCommandBuffer(); 181 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
180 u32 app_id_salt = cmd_buff[1]; 182 const u32 app_id_salt = rp.Pop<u32>() & 0x000FFFFF;
181 183
182 cmd_buff[1] = RESULT_SUCCESS.raw; 184 IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
183 cmd_buff[2] = 0x33646D6F ^ (app_id_salt & 0xFFFFF); // 3dmoo hash 185
184 cmd_buff[3] = 0x6F534841 ^ (app_id_salt & 0xFFFFF); 186 std::array<u8, 12> buffer;
187 const ResultCode result = GetConfigInfoBlock(ConsoleUniqueID2BlockID, 8, 2, buffer.data());
188 rb.Push(result);
189 if (result.IsSuccess()) {
190 std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
191 std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
192 CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer));
193 u32 low, high;
194 memcpy(&low, &hash[hash.size() - 8], sizeof(u32));
195 memcpy(&high, &hash[hash.size() - 4], sizeof(u32));
196 rb.Push(low);
197 rb.Push(high);
198 } else {
199 rb.Push<u32>(0);
200 rb.Push<u32>(0);
201 }
185 202
186 LOG_WARNING(Service_CFG, "(STUBBED) called app_id_salt=0x%X", app_id_salt); 203 LOG_DEBUG(Service_CFG, "called app_id_salt=0x%X", app_id_salt);
187} 204}
188 205
189void GetRegionCanadaUSA(Service::Interface* self) { 206void GetRegionCanadaUSA(Service::Interface* self) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index fb3acb507..b19e831fe 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,10 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <atomic>
5#include <cmath> 7#include <cmath>
8#include <memory>
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/core_timing.h" 10#include "core/core_timing.h"
8#include "core/frontend/emu_window.h" 11#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h"
9#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/shared_memory.h" 14#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/hid/hid.h" 15#include "core/hle/service/hid/hid.h"
@@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
44constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 48constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
45constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 49constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
46 50
51static std::atomic<bool> is_device_reload_pending;
52static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
53 buttons;
54static std::unique_ptr<Input::AnalogDevice> circle_pad;
55
47static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 56static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
48 // 30 degree and 60 degree are angular thresholds for directions 57 // 30 degree and 60 degree are angular thresholds for directions
49 constexpr float TAN30 = 0.577350269f; 58 constexpr float TAN30 = 0.577350269f;
@@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
74 return state; 83 return state;
75} 84}
76 85
86static void LoadInputDevices() {
87 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
88 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
89 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
90 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
91 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
92}
93
94static void UnloadInputDevices() {
95 for (auto& button : buttons) {
96 button.reset();
97 }
98 circle_pad.reset();
99}
100
77static void UpdatePadCallback(u64 userdata, int cycles_late) { 101static void UpdatePadCallback(u64 userdata, int cycles_late) {
78 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); 102 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
79 103
80 PadState state = VideoCore::g_emu_window->GetPadState(); 104 if (is_device_reload_pending.exchange(false))
105 LoadInputDevices();
106
107 PadState state;
108 using namespace Settings::NativeButton;
109 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
110 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
111 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
112 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
113 state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus());
114 state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus());
115 state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus());
116 state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus());
117 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
118 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
119 state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus());
120 state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus());
81 121
82 // Get current circle pad position and update circle pad direction 122 // Get current circle pad position and update circle pad direction
83 s16 circle_pad_x, circle_pad_y; 123 float circle_pad_x_f, circle_pad_y_f;
84 std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); 124 std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus();
125 constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
126 s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS);
127 s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS);
85 state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; 128 state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
86 129
87 mem->pad.current_state.hex = state.hex; 130 mem->pad.current_state.hex = state.hex;
@@ -313,6 +356,8 @@ void Init() {
313 AddService(new HID_U_Interface); 356 AddService(new HID_U_Interface);
314 AddService(new HID_SPVR_Interface); 357 AddService(new HID_SPVR_Interface);
315 358
359 is_device_reload_pending.store(true);
360
316 using Kernel::MemoryPermission; 361 using Kernel::MemoryPermission;
317 shared_mem = 362 shared_mem =
318 SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, 363 SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read,
@@ -350,6 +395,11 @@ void Shutdown() {
350 event_accelerometer = nullptr; 395 event_accelerometer = nullptr;
351 event_gyroscope = nullptr; 396 event_gyroscope = nullptr;
352 event_debug_pad = nullptr; 397 event_debug_pad = nullptr;
398 UnloadInputDevices();
399}
400
401void ReloadInputDevices() {
402 is_device_reload_pending.store(true);
353} 403}
354 404
355} // namespace HID 405} // namespace HID
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c7f4ee138..b505cdcd5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -39,13 +39,6 @@ struct PadState {
39 BitField<10, 1, u32> x; 39 BitField<10, 1, u32> x;
40 BitField<11, 1, u32> y; 40 BitField<11, 1, u32> y;
41 41
42 BitField<14, 1, u32> zl;
43 BitField<15, 1, u32> zr;
44
45 BitField<24, 1, u32> c_right;
46 BitField<25, 1, u32> c_left;
47 BitField<26, 1, u32> c_up;
48 BitField<27, 1, u32> c_down;
49 BitField<28, 1, u32> circle_right; 42 BitField<28, 1, u32> circle_right;
50 BitField<29, 1, u32> circle_left; 43 BitField<29, 1, u32> circle_left;
51 BitField<30, 1, u32> circle_up; 44 BitField<30, 1, u32> circle_up;
@@ -183,33 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A);
183#undef ASSERT_REG_POSITION 176#undef ASSERT_REG_POSITION
184#endif // !defined(_MSC_VER) 177#endif // !defined(_MSC_VER)
185 178
186// Pre-defined PadStates for single button presses
187const PadState PAD_NONE = {{0}};
188const PadState PAD_A = {{1u << 0}};
189const PadState PAD_B = {{1u << 1}};
190const PadState PAD_SELECT = {{1u << 2}};
191const PadState PAD_START = {{1u << 3}};
192const PadState PAD_RIGHT = {{1u << 4}};
193const PadState PAD_LEFT = {{1u << 5}};
194const PadState PAD_UP = {{1u << 6}};
195const PadState PAD_DOWN = {{1u << 7}};
196const PadState PAD_R = {{1u << 8}};
197const PadState PAD_L = {{1u << 9}};
198const PadState PAD_X = {{1u << 10}};
199const PadState PAD_Y = {{1u << 11}};
200
201const PadState PAD_ZL = {{1u << 14}};
202const PadState PAD_ZR = {{1u << 15}};
203
204const PadState PAD_C_RIGHT = {{1u << 24}};
205const PadState PAD_C_LEFT = {{1u << 25}};
206const PadState PAD_C_UP = {{1u << 26}};
207const PadState PAD_C_DOWN = {{1u << 27}};
208const PadState PAD_CIRCLE_RIGHT = {{1u << 28}};
209const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
210const PadState PAD_CIRCLE_UP = {{1u << 30}};
211const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
212
213/** 179/**
214 * HID::GetIPCHandles service function 180 * HID::GetIPCHandles service function
215 * Inputs: 181 * Inputs:
@@ -297,5 +263,8 @@ void Init();
297 263
298/// Shutdown HID service 264/// Shutdown HID service
299void Shutdown(); 265void Shutdown();
266
267/// Reload input devices. Used when input configuration changed
268void ReloadInputDevices();
300} 269}
301} 270}
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 8ff808fd9..e373ed47a 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -92,8 +92,7 @@ void GetSoftwareClosedFlag(Service::Interface* self) {
92 LOG_WARNING(Service_PTM, "(STUBBED) called"); 92 LOG_WARNING(Service_PTM, "(STUBBED) called");
93} 93}
94 94
95void CheckNew3DS(Service::Interface* self) { 95void CheckNew3DS(IPC::RequestBuilder& rb) {
96 u32* cmd_buff = Kernel::GetCommandBuffer();
97 const bool is_new_3ds = Settings::values.is_new_3ds; 96 const bool is_new_3ds = Settings::values.is_new_3ds;
98 97
99 if (is_new_3ds) { 98 if (is_new_3ds) {
@@ -101,12 +100,17 @@ void CheckNew3DS(Service::Interface* self) {
101 "settings. Citra does not fully support New 3DS emulation yet!"); 100 "settings. Citra does not fully support New 3DS emulation yet!");
102 } 101 }
103 102
104 cmd_buff[1] = RESULT_SUCCESS.raw; 103 rb.Push(RESULT_SUCCESS);
105 cmd_buff[2] = is_new_3ds ? 1 : 0; 104 rb.Push(is_new_3ds);
106 105
107 LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds)); 106 LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds));
108} 107}
109 108
109void CheckNew3DS(Service::Interface* self) {
110 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x40A, 0, 0); // 0x040A0000
111 CheckNew3DS(rb);
112}
113
110void Init() { 114void Init() {
111 AddService(new PTM_Gets); 115 AddService(new PTM_Gets);
112 AddService(new PTM_Play); 116 AddService(new PTM_Play);
@@ -134,9 +138,9 @@ void Init() {
134 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); 138 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
135 139
136 FileSys::Path gamecoin_path("/gamecoin.dat"); 140 FileSys::Path gamecoin_path("/gamecoin.dat");
141 Service::FS::CreateFileInArchive(*archive_result, gamecoin_path, sizeof(GameCoin));
137 FileSys::Mode open_mode = {}; 142 FileSys::Mode open_mode = {};
138 open_mode.write_flag.Assign(1); 143 open_mode.write_flag.Assign(1);
139 open_mode.create_flag.Assign(1);
140 // Open the file and write the default gamecoin information 144 // Open the file and write the default gamecoin information
141 auto gamecoin_result = 145 auto gamecoin_result =
142 Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); 146 Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index a1a628012..683fb445b 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/ipc_helpers.h"
8 9
9namespace Service { 10namespace Service {
10 11
@@ -97,6 +98,7 @@ void GetSoftwareClosedFlag(Interface* self);
97 * 2: u8 output: 0 = Old3DS, 1 = New3DS. 98 * 2: u8 output: 0 = Old3DS, 1 = New3DS.
98 */ 99 */
99void CheckNew3DS(Interface* self); 100void CheckNew3DS(Interface* self);
101void CheckNew3DS(IPC::RequestBuilder& rb);
100 102
101/// Initialize the PTM service 103/// Initialize the PTM service
102void Init(); 104void Init();
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index dcc5c3c90..530614e6f 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -362,18 +362,18 @@ static void Socket(Interface* self) {
362 return; 362 return;
363 } 363 }
364 364
365 u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol)); 365 u32 ret = static_cast<u32>(::socket(domain, type, protocol));
366 366
367 if ((s32)socket_handle != SOCKET_ERROR_VALUE) 367 if ((s32)ret != SOCKET_ERROR_VALUE)
368 open_sockets[socket_handle] = {socket_handle, true}; 368 open_sockets[ret] = {ret, true};
369 369
370 int result = 0; 370 int result = 0;
371 if ((s32)socket_handle == SOCKET_ERROR_VALUE) 371 if ((s32)ret == SOCKET_ERROR_VALUE)
372 result = TranslateError(GET_ERRNO); 372 ret = TranslateError(GET_ERRNO);
373 373
374 cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); 374 cmd_buffer[0] = IPC::MakeHeader(2, 2, 0);
375 cmd_buffer[1] = result; 375 cmd_buffer[1] = result;
376 cmd_buffer[2] = socket_handle; 376 cmd_buffer[2] = ret;
377} 377}
378 378
379static void Bind(Interface* self) { 379static void Bind(Interface* self) {
@@ -393,15 +393,15 @@ static void Bind(Interface* self) {
393 393
394 sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); 394 sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
395 395
396 int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); 396 int ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
397 397
398 int result = 0; 398 int result = 0;
399 if (res != 0) 399 if (ret != 0)
400 result = TranslateError(GET_ERRNO); 400 ret = TranslateError(GET_ERRNO);
401 401
402 cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); 402 cmd_buffer[0] = IPC::MakeHeader(5, 2, 0);
403 cmd_buffer[1] = result; 403 cmd_buffer[1] = result;
404 cmd_buffer[2] = res; 404 cmd_buffer[2] = ret;
405} 405}
406 406
407static void Fcntl(Interface* self) { 407static void Fcntl(Interface* self) {
@@ -426,8 +426,7 @@ static void Fcntl(Interface* self) {
426#else 426#else
427 int ret = ::fcntl(socket_handle, F_GETFL, 0); 427 int ret = ::fcntl(socket_handle, F_GETFL, 0);
428 if (ret == SOCKET_ERROR_VALUE) { 428 if (ret == SOCKET_ERROR_VALUE) {
429 result = TranslateError(GET_ERRNO); 429 posix_ret = TranslateError(GET_ERRNO);
430 posix_ret = -1;
431 return; 430 return;
432 } 431 }
433 posix_ret = 0; 432 posix_ret = 0;
@@ -439,8 +438,7 @@ static void Fcntl(Interface* self) {
439 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; 438 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
440 int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); 439 int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
441 if (ret == SOCKET_ERROR_VALUE) { 440 if (ret == SOCKET_ERROR_VALUE) {
442 result = TranslateError(GET_ERRNO); 441 posix_ret = TranslateError(GET_ERRNO);
443 posix_ret = -1;
444 return; 442 return;
445 } 443 }
446 auto iter = open_sockets.find(socket_handle); 444 auto iter = open_sockets.find(socket_handle);
@@ -449,8 +447,7 @@ static void Fcntl(Interface* self) {
449#else 447#else
450 int flags = ::fcntl(socket_handle, F_GETFL, 0); 448 int flags = ::fcntl(socket_handle, F_GETFL, 0);
451 if (flags == SOCKET_ERROR_VALUE) { 449 if (flags == SOCKET_ERROR_VALUE) {
452 result = TranslateError(GET_ERRNO); 450 posix_ret = TranslateError(GET_ERRNO);
453 posix_ret = -1;
454 return; 451 return;
455 } 452 }
456 453
@@ -460,15 +457,13 @@ static void Fcntl(Interface* self) {
460 457
461 int ret = ::fcntl(socket_handle, F_SETFL, flags); 458 int ret = ::fcntl(socket_handle, F_SETFL, flags);
462 if (ret == SOCKET_ERROR_VALUE) { 459 if (ret == SOCKET_ERROR_VALUE) {
463 result = TranslateError(GET_ERRNO); 460 posix_ret = TranslateError(GET_ERRNO);
464 posix_ret = -1;
465 return; 461 return;
466 } 462 }
467#endif 463#endif
468 } else { 464 } else {
469 LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd); 465 LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd);
470 result = TranslateError(EINVAL); // TODO: Find the correct error 466 posix_ret = TranslateError(EINVAL); // TODO: Find the correct error
471 posix_ret = -1;
472 return; 467 return;
473 } 468 }
474} 469}
@@ -481,7 +476,7 @@ static void Listen(Interface* self) {
481 int ret = ::listen(socket_handle, backlog); 476 int ret = ::listen(socket_handle, backlog);
482 int result = 0; 477 int result = 0;
483 if (ret != 0) 478 if (ret != 0)
484 result = TranslateError(GET_ERRNO); 479 ret = TranslateError(GET_ERRNO);
485 480
486 cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); 481 cmd_buffer[0] = IPC::MakeHeader(3, 2, 0);
487 cmd_buffer[1] = result; 482 cmd_buffer[1] = result;
@@ -504,7 +499,7 @@ static void Accept(Interface* self) {
504 499
505 int result = 0; 500 int result = 0;
506 if ((s32)ret == SOCKET_ERROR_VALUE) { 501 if ((s32)ret == SOCKET_ERROR_VALUE) {
507 result = TranslateError(GET_ERRNO); 502 ret = TranslateError(GET_ERRNO);
508 } else { 503 } else {
509 CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr); 504 CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
510 Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr)); 505 Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr));
@@ -545,7 +540,7 @@ static void Close(Interface* self) {
545 540
546 int result = 0; 541 int result = 0;
547 if (ret != 0) 542 if (ret != 0)
548 result = TranslateError(GET_ERRNO); 543 ret = TranslateError(GET_ERRNO);
549 544
550 cmd_buffer[2] = ret; 545 cmd_buffer[2] = ret;
551 cmd_buffer[1] = result; 546 cmd_buffer[1] = result;
@@ -589,7 +584,7 @@ static void SendTo(Interface* self) {
589 584
590 int result = 0; 585 int result = 0;
591 if (ret == SOCKET_ERROR_VALUE) 586 if (ret == SOCKET_ERROR_VALUE)
592 result = TranslateError(GET_ERRNO); 587 ret = TranslateError(GET_ERRNO);
593 588
594 cmd_buffer[2] = ret; 589 cmd_buffer[2] = ret;
595 cmd_buffer[1] = result; 590 cmd_buffer[1] = result;
@@ -638,7 +633,7 @@ static void RecvFrom(Interface* self) {
638 int result = 0; 633 int result = 0;
639 int total_received = ret; 634 int total_received = ret;
640 if (ret == SOCKET_ERROR_VALUE) { 635 if (ret == SOCKET_ERROR_VALUE) {
641 result = TranslateError(GET_ERRNO); 636 ret = TranslateError(GET_ERRNO);
642 total_received = 0; 637 total_received = 0;
643 } else { 638 } else {
644 // Write only the data we received to avoid overwriting parts of the buffer with zeros 639 // Write only the data we received to avoid overwriting parts of the buffer with zeros
@@ -673,7 +668,7 @@ static void Poll(Interface* self) {
673 std::vector<pollfd> platform_pollfd(nfds); 668 std::vector<pollfd> platform_pollfd(nfds);
674 std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform); 669 std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
675 670
676 const int ret = ::poll(platform_pollfd.data(), nfds, timeout); 671 int ret = ::poll(platform_pollfd.data(), nfds, timeout);
677 672
678 // Now update the output pollfd structure 673 // Now update the output pollfd structure
679 std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), 674 std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(),
@@ -683,7 +678,7 @@ static void Poll(Interface* self) {
683 678
684 int result = 0; 679 int result = 0;
685 if (ret == SOCKET_ERROR_VALUE) 680 if (ret == SOCKET_ERROR_VALUE)
686 result = TranslateError(GET_ERRNO); 681 ret = TranslateError(GET_ERRNO);
687 682
688 cmd_buffer[1] = result; 683 cmd_buffer[1] = result;
689 cmd_buffer[2] = ret; 684 cmd_buffer[2] = ret;
@@ -710,7 +705,7 @@ static void GetSockName(Interface* self) {
710 705
711 int result = 0; 706 int result = 0;
712 if (ret != 0) 707 if (ret != 0)
713 result = TranslateError(GET_ERRNO); 708 ret = TranslateError(GET_ERRNO);
714 709
715 cmd_buffer[2] = ret; 710 cmd_buffer[2] = ret;
716 cmd_buffer[1] = result; 711 cmd_buffer[1] = result;
@@ -724,7 +719,7 @@ static void Shutdown(Interface* self) {
724 int ret = ::shutdown(socket_handle, how); 719 int ret = ::shutdown(socket_handle, how);
725 int result = 0; 720 int result = 0;
726 if (ret != 0) 721 if (ret != 0)
727 result = TranslateError(GET_ERRNO); 722 ret = TranslateError(GET_ERRNO);
728 cmd_buffer[2] = ret; 723 cmd_buffer[2] = ret;
729 cmd_buffer[1] = result; 724 cmd_buffer[1] = result;
730} 725}
@@ -750,7 +745,7 @@ static void GetPeerName(Interface* self) {
750 745
751 int result = 0; 746 int result = 0;
752 if (ret != 0) 747 if (ret != 0)
753 result = TranslateError(GET_ERRNO); 748 ret = TranslateError(GET_ERRNO);
754 749
755 cmd_buffer[2] = ret; 750 cmd_buffer[2] = ret;
756 cmd_buffer[1] = result; 751 cmd_buffer[1] = result;
@@ -777,7 +772,7 @@ static void Connect(Interface* self) {
777 int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); 772 int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
778 int result = 0; 773 int result = 0;
779 if (ret != 0) 774 if (ret != 0)
780 result = TranslateError(GET_ERRNO); 775 ret = TranslateError(GET_ERRNO);
781 776
782 cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); 777 cmd_buffer[0] = IPC::MakeHeader(6, 2, 0);
783 cmd_buffer[1] = result; 778 cmd_buffer[1] = result;
@@ -815,7 +810,7 @@ static void GetSockOpt(Interface* self) {
815 int optname = TranslateSockOpt(cmd_buffer[3]); 810 int optname = TranslateSockOpt(cmd_buffer[3]);
816 socklen_t optlen = (socklen_t)cmd_buffer[4]; 811 socklen_t optlen = (socklen_t)cmd_buffer[4];
817 812
818 int ret = -1; 813 int ret = 0;
819 int err = 0; 814 int err = 0;
820 815
821 if (optname < 0) { 816 if (optname < 0) {
@@ -830,9 +825,8 @@ static void GetSockOpt(Interface* self) {
830 // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*) 825 // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
831 char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); 826 char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
832 827
833 ret = ::getsockopt(socket_handle, level, optname, optval, &optlen); 828 err = ::getsockopt(socket_handle, level, optname, optval, &optlen);
834 err = 0; 829 if (err == SOCKET_ERROR_VALUE) {
835 if (ret == SOCKET_ERROR_VALUE) {
836 err = TranslateError(GET_ERRNO); 830 err = TranslateError(GET_ERRNO);
837 } 831 }
838 } 832 }
@@ -849,7 +843,7 @@ static void SetSockOpt(Interface* self) {
849 u32 level = cmd_buffer[2]; 843 u32 level = cmd_buffer[2];
850 int optname = TranslateSockOpt(cmd_buffer[3]); 844 int optname = TranslateSockOpt(cmd_buffer[3]);
851 845
852 int ret = -1; 846 int ret = 0;
853 int err = 0; 847 int err = 0;
854 848
855 if (optname < 0) { 849 if (optname < 0) {
@@ -862,9 +856,8 @@ static void SetSockOpt(Interface* self) {
862 socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]); 856 socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
863 const char* optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8])); 857 const char* optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8]));
864 858
865 ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen)); 859 err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
866 err = 0; 860 if (err == SOCKET_ERROR_VALUE) {
867 if (ret == SOCKET_ERROR_VALUE) {
868 err = TranslateError(GET_ERRNO); 861 err = TranslateError(GET_ERRNO);
869 } 862 }
870 } 863 }
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index 907d9c8fa..c0837d49d 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -189,11 +189,9 @@ static void SetSpacialDithering(Interface* self) {
189 * 2 : u8, 0 = Disabled, 1 = Enabled 189 * 2 : u8, 0 = Disabled, 1 = Enabled
190 */ 190 */
191static void GetSpacialDithering(Interface* self) { 191static void GetSpacialDithering(Interface* self) {
192 u32* cmd_buff = Kernel::GetCommandBuffer(); 192 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0);
193 193 rb.Push(RESULT_SUCCESS);
194 cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); 194 rb.Push(spacial_dithering_enabled != 0);
195 cmd_buff[1] = RESULT_SUCCESS.raw;
196 cmd_buff[2] = spacial_dithering_enabled;
197 195
198 LOG_WARNING(Service_Y2R, "(STUBBED) called"); 196 LOG_WARNING(Service_Y2R, "(STUBBED) called");
199} 197}
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
index d0d92487d..5978ccdd4 100644
--- a/src/core/hle/shared_page.cpp
+++ b/src/core/hle/shared_page.cpp
@@ -6,6 +6,7 @@
6#include <cstring> 6#include <cstring>
7#include <ctime> 7#include <ctime>
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/ptm/ptm.h"
9#include "core/hle/shared_page.h" 10#include "core/hle/shared_page.h"
10 11
11//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -73,6 +74,12 @@ void Init() {
73 // Some games wait until this value becomes 0x1, before asking running_hw 74 // Some games wait until this value becomes 0x1, before asking running_hw
74 shared_page.unknown_value = 0x1; 75 shared_page.unknown_value = 0x1;
75 76
77 // Set to a completely full battery
78 shared_page.battery_state.charge_level.Assign(
79 static_cast<u8>(Service::PTM::ChargeLevels::CompletelyFull));
80 shared_page.battery_state.is_adapter_connected.Assign(1);
81 shared_page.battery_state.is_charging.Assign(1);
82
76 update_time_event = 83 update_time_event =
77 CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback); 84 CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback);
78 CoreTiming::ScheduleEvent(0, update_time_event); 85 CoreTiming::ScheduleEvent(0, update_time_event);
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
index 106e47efc..864695ae1 100644
--- a/src/core/hle/shared_page.h
+++ b/src/core/hle/shared_page.h
@@ -10,6 +10,7 @@
10 * write access, according to 3dbrew; this is not emulated) 10 * write access, according to 3dbrew; this is not emulated)
11 */ 11 */
12 12
13#include "common/bit_field.h"
13#include "common/common_funcs.h" 14#include "common/common_funcs.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "common/swap.h" 16#include "common/swap.h"
@@ -29,6 +30,13 @@ struct DateTime {
29}; 30};
30static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); 31static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
31 32
33union BatteryState {
34 u8 raw;
35 BitField<0, 1, u8> is_adapter_connected;
36 BitField<1, 1, u8> is_charging;
37 BitField<2, 3, u8> charge_level;
38};
39
32struct SharedPageDef { 40struct SharedPageDef {
33 // Most of these names are taken from the 3dbrew page linked above. 41 // Most of these names are taken from the 3dbrew page linked above.
34 u32_le date_time_counter; // 0 42 u32_le date_time_counter; // 0
@@ -44,7 +52,7 @@ struct SharedPageDef {
44 INSERT_PADDING_BYTES(0x80 - 0x68); // 68 52 INSERT_PADDING_BYTES(0x80 - 0x68); // 68
45 float_le sliderstate_3d; // 80 53 float_le sliderstate_3d; // 80
46 u8 ledstate_3d; // 84 54 u8 ledstate_3d; // 84
47 INSERT_PADDING_BYTES(1); // 85 55 BatteryState battery_state; // 85
48 u8 unknown_value; // 86 56 u8 unknown_value; // 86
49 INSERT_PADDING_BYTES(0xA0 - 0x87); // 87 57 INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
50 u64_le menu_title_id; // A0 58 u64_le menu_title_id; // A0
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 1baa80671..2db823c61 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -467,8 +467,8 @@ static void Break(u8 break_reason) {
467} 467}
468 468
469/// Used to output a message on a debug hardware unit - does nothing on a retail unit 469/// Used to output a message on a debug hardware unit - does nothing on a retail unit
470static void OutputDebugString(const char* string) { 470static void OutputDebugString(const char* string, int len) {
471 LOG_DEBUG(Debug_Emulated, "%s", string); 471 LOG_DEBUG(Debug_Emulated, "%.*s", len, string);
472} 472}
473 473
474/// Get resource limit 474/// Get resource limit
@@ -556,11 +556,21 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent
556 break; 556 break;
557 } 557 }
558 558
559 if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL || 559 if (processor_id == THREADPROCESSORID_ALL) {
560 (processor_id == THREADPROCESSORID_DEFAULT && 560 LOG_INFO(Kernel_SVC,
561 Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) { 561 "Newly created thread is allowed to be run in any Core, unimplemented.");
562 LOG_WARNING(Kernel_SVC, 562 }
563 "Newly created thread is allowed to be run in the SysCore, unimplemented."); 563
564 if (processor_id == THREADPROCESSORID_DEFAULT &&
565 Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1) {
566 LOG_WARNING(
567 Kernel_SVC,
568 "Newly created thread is allowed to be run in the SysCore (Core1), unimplemented.");
569 }
570
571 if (processor_id == THREADPROCESSORID_1) {
572 LOG_ERROR(Kernel_SVC,
573 "Newly created thread must run in the SysCore (Core1), unimplemented.");
564 } 574 }
565 575
566 CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, 576 CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority,
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 147bf8591..be719d74c 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -139,7 +139,7 @@ std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
139 type = filename_type; 139 type = filename_type;
140 } 140 }
141 141
142 LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type)); 142 LOG_DEBUG(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
143 143
144 return GetFileLoader(std::move(file), type, filename_filename, filename); 144 return GetFileLoader(std::move(file), type, filename_filename, filename);
145} 145}
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 98b8259d9..1a4e3efa8 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes>
6#include <cstring> 7#include <cstring>
7#include <memory> 8#include <memory>
8#include "common/logging/log.h" 9#include "common/logging/log.h"
@@ -253,7 +254,7 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
253 254
254 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... 255 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
255 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { 256 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
256 LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); 257 LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
257 ncch_offset = 0x4000; 258 ncch_offset = 0x4000;
258 file.Seek(ncch_offset, SEEK_SET); 259 file.Seek(ncch_offset, SEEK_SET);
259 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 260 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
@@ -277,8 +278,8 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
277 priority = exheader_header.arm11_system_local_caps.priority; 278 priority = exheader_header.arm11_system_local_caps.priority;
278 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; 279 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
279 280
280 LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name); 281 LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name);
281 LOG_INFO(Loader, "Program ID: %016llX", ncch_header.program_id); 282 LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
282 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); 283 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
283 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); 284 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
284 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); 285 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
@@ -336,6 +337,8 @@ ResultStatus AppLoader_NCCH::Load() {
336 if (result != ResultStatus::Success) 337 if (result != ResultStatus::Success)
337 return result; 338 return result;
338 339
340 LOG_INFO(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
341
339 is_loaded = true; // Set state to loaded 342 is_loaded = true; // Set state to loaded
340 343
341 result = LoadExec(); // Load the executable into memory for booting 344 result = LoadExec(); // Load the executable into memory for booting
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 3a32b70aa..a598f9f2f 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -4,6 +4,7 @@
4 4
5#include "audio_core/audio_core.h" 5#include "audio_core/audio_core.h"
6#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h"
7#include "settings.h" 8#include "settings.h"
8#include "video_core/video_core.h" 9#include "video_core/video_core.h"
9 10
@@ -29,6 +30,8 @@ void Apply() {
29 30
30 AudioCore::SelectSink(values.sink_id); 31 AudioCore::SelectSink(values.sink_id);
31 AudioCore::EnableStretching(values.enable_audio_stretching); 32 AudioCore::EnableStretching(values.enable_audio_stretching);
33
34 Service::HID::ReloadInputDevices();
32} 35}
33 36
34} // namespace 37} // namespace
diff --git a/src/core/settings.h b/src/core/settings.h
index b6c75531f..03c64c94c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -15,67 +15,70 @@ enum class LayoutOption {
15 Default, 15 Default,
16 SingleScreen, 16 SingleScreen,
17 LargeScreen, 17 LargeScreen,
18 Custom,
19}; 18};
20 19
21namespace NativeInput { 20namespace NativeButton {
22
23enum Values { 21enum Values {
24 // directly mapped keys
25 A, 22 A,
26 B, 23 B,
27 X, 24 X,
28 Y, 25 Y,
26 Up,
27 Down,
28 Left,
29 Right,
29 L, 30 L,
30 R, 31 R,
32 Start,
33 Select,
34
31 ZL, 35 ZL,
32 ZR, 36 ZR,
33 START, 37
34 SELECT, 38 Home,
35 HOME, 39
36 DUP, 40 NumButtons,
37 DDOWN,
38 DLEFT,
39 DRIGHT,
40 CUP,
41 CDOWN,
42 CLEFT,
43 CRIGHT,
44
45 // indirectly mapped keys
46 CIRCLE_UP,
47 CIRCLE_DOWN,
48 CIRCLE_LEFT,
49 CIRCLE_RIGHT,
50 CIRCLE_MODIFIER,
51
52 NUM_INPUTS
53}; 41};
54 42
55static const std::array<const char*, NUM_INPUTS> Mapping = {{ 43constexpr int BUTTON_HID_BEGIN = A;
56 // directly mapped keys 44constexpr int BUTTON_IR_BEGIN = ZL;
57 "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", 45constexpr int BUTTON_NS_BEGIN = Home;
58 "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", 46
59 "pad_cdown", "pad_cleft", "pad_cright", 47constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
48constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
49constexpr int BUTTON_NS_END = NumButtons;
50
51constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
52constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
53constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
60 54
61 // indirectly mapped keys 55static const std::array<const char*, NumButtons> mapping = {{
62 "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right", 56 "button_a", "button_b", "button_x", "button_y", "button_up", "button_down", "button_left",
63 "pad_circle_modifier", 57 "button_right", "button_l", "button_r", "button_start", "button_select", "button_zl",
58 "button_zr", "button_home",
64}}; 59}};
65static const std::array<Values, NUM_INPUTS> All = {{ 60} // namespace NativeButton
66 A, B, X, Y, L, R, ZL, ZR, 61
67 START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, 62namespace NativeAnalog {
68 CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, CIRCLE_MODIFIER, 63enum Values {
64 CirclePad,
65 CStick,
66
67 NumAnalogs,
68};
69
70static const std::array<const char*, NumAnalogs> mapping = {{
71 "circle_pad", "c_stick",
69}}; 72}};
70} 73} // namespace NumAnalog
71 74
72struct Values { 75struct Values {
73 // CheckNew3DS 76 // CheckNew3DS
74 bool is_new_3ds; 77 bool is_new_3ds;
75 78
76 // Controls 79 // Controls
77 std::array<int, NativeInput::NUM_INPUTS> input_mappings; 80 std::array<std::string, NativeButton::NumButtons> buttons;
78 float pad_circle_modifier_scale; 81 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
79 82
80 // Core 83 // Core
81 bool use_cpu_jit; 84 bool use_cpu_jit;
@@ -95,6 +98,15 @@ struct Values {
95 98
96 LayoutOption layout_option; 99 LayoutOption layout_option;
97 bool swap_screen; 100 bool swap_screen;
101 bool custom_layout;
102 u16 custom_top_left;
103 u16 custom_top_top;
104 u16 custom_top_right;
105 u16 custom_top_bottom;
106 u16 custom_bottom_left;
107 u16 custom_bottom_top;
108 u16 custom_bottom_right;
109 u16 custom_bottom_bottom;
98 110
99 float bg_red; 111 float bg_red;
100 float bg_green; 112 float bg_green;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
new file mode 100644
index 000000000..cfe5caaa3
--- /dev/null
+++ b/src/input_common/CMakeLists.txt
@@ -0,0 +1,27 @@
1set(SRCS
2 analog_from_button.cpp
3 keyboard.cpp
4 main.cpp
5 )
6
7set(HEADERS
8 analog_from_button.h
9 keyboard.h
10 main.h
11 )
12
13if(SDL2_FOUND)
14 set(SRCS ${SRCS} sdl/sdl.cpp)
15 set(HEADERS ${HEADERS} sdl/sdl.h)
16 include_directories(${SDL2_INCLUDE_DIR})
17endif()
18
19create_directory_groups(${SRCS} ${HEADERS})
20
21add_library(input_common STATIC ${SRCS} ${HEADERS})
22target_link_libraries(input_common common core)
23
24if(SDL2_FOUND)
25 target_link_libraries(input_common ${SDL2_LIBRARY})
26 set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2)
27endif()
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
new file mode 100755
index 000000000..e1a260762
--- /dev/null
+++ b/src/input_common/analog_from_button.cpp
@@ -0,0 +1,58 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/analog_from_button.h"
6
7namespace InputCommon {
8
9class Analog final : public Input::AnalogDevice {
10public:
11 using Button = std::unique_ptr<Input::ButtonDevice>;
12
13 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
14 float modifier_scale_)
15 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
16 right(std::move(right_)), modifier(std::move(modifier_)),
17 modifier_scale(modifier_scale_) {}
18
19 std::tuple<float, float> GetStatus() const override {
20 constexpr float SQRT_HALF = 0.707106781f;
21 int x = 0, y = 0;
22
23 if (right->GetStatus())
24 ++x;
25 if (left->GetStatus())
26 --x;
27 if (up->GetStatus())
28 ++y;
29 if (down->GetStatus())
30 --y;
31
32 float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
33 return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
34 y * coef * (x == 0 ? 1.0f : SQRT_HALF));
35 }
36
37private:
38 Button up;
39 Button down;
40 Button left;
41 Button right;
42 Button modifier;
43 float modifier_scale;
44};
45
46std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
47 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
48 auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
49 auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
50 auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
51 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
52 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
53 auto modifier_scale = params.Get("modifier_scale", 0.5f);
54 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
55 std::move(right), std::move(modifier), modifier_scale);
56}
57
58} // namespace InputCommon
diff --git a/src/input_common/analog_from_button.h b/src/input_common/analog_from_button.h
new file mode 100755
index 000000000..bbd583dd9
--- /dev/null
+++ b/src/input_common/analog_from_button.h
@@ -0,0 +1,31 @@
1// Copyright 2017 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 "core/frontend/input.h"
9
10namespace InputCommon {
11
12/**
13 * An analog device factory that takes direction button devices and combines them into a analog
14 * device.
15 */
16class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> {
17public:
18 /**
19 * Creates an analog device from direction button devices
20 * @param params contains parameters for creating the device:
21 * - "up": a serialized ParamPackage for creating a button device for up direction
22 * - "down": a serialized ParamPackage for creating a button device for down direction
23 * - "left": a serialized ParamPackage for creating a button device for left direction
24 * - "right": a serialized ParamPackage for creating a button device for right direction
25 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
26 * - "modifier_scale": a float for the multiplier the modifier gives to the position
27 */
28 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
29};
30
31} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
new file mode 100644
index 000000000..0f0d10f23
--- /dev/null
+++ b/src/input_common/keyboard.cpp
@@ -0,0 +1,93 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include "input_common/keyboard.h"
9
10namespace InputCommon {
11
12class KeyButton final : public Input::ButtonDevice {
13public:
14 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
15 : key_button_list(key_button_list_) {}
16
17 ~KeyButton();
18
19 bool GetStatus() const override {
20 return status.load();
21 }
22
23 friend class KeyButtonList;
24
25private:
26 std::shared_ptr<KeyButtonList> key_button_list;
27 std::atomic<bool> status{false};
28};
29
30struct KeyButtonPair {
31 int key_code;
32 KeyButton* key_button;
33};
34
35class KeyButtonList {
36public:
37 void AddKeyButton(int key_code, KeyButton* key_button) {
38 std::lock_guard<std::mutex> guard(mutex);
39 list.push_back(KeyButtonPair{key_code, key_button});
40 }
41
42 void RemoveKeyButton(const KeyButton* key_button) {
43 std::lock_guard<std::mutex> guard(mutex);
44 list.remove_if(
45 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
46 }
47
48 void ChangeKeyStatus(int key_code, bool pressed) {
49 std::lock_guard<std::mutex> guard(mutex);
50 for (const KeyButtonPair& pair : list) {
51 if (pair.key_code == key_code)
52 pair.key_button->status.store(pressed);
53 }
54 }
55
56 void ChangeAllKeyStatus(bool pressed) {
57 std::lock_guard<std::mutex> guard(mutex);
58 for (const KeyButtonPair& pair : list) {
59 pair.key_button->status.store(pressed);
60 }
61 }
62
63private:
64 std::mutex mutex;
65 std::list<KeyButtonPair> list;
66};
67
68Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
69
70KeyButton::~KeyButton() {
71 key_button_list->RemoveKeyButton(this);
72}
73
74std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
75 int key_code = params.Get("code", 0);
76 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
77 key_button_list->AddKeyButton(key_code, button.get());
78 return std::move(button);
79}
80
81void Keyboard::PressKey(int key_code) {
82 key_button_list->ChangeKeyStatus(key_code, true);
83}
84
85void Keyboard::ReleaseKey(int key_code) {
86 key_button_list->ChangeKeyStatus(key_code, false);
87}
88
89void Keyboard::ReleaseAllKeys() {
90 key_button_list->ChangeAllKeyStatus(false);
91}
92
93} // namespace InputCommon
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h
new file mode 100644
index 000000000..861950472
--- /dev/null
+++ b/src/input_common/keyboard.h
@@ -0,0 +1,47 @@
1// Copyright 2017 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 "core/frontend/input.h"
9
10namespace InputCommon {
11
12class KeyButtonList;
13
14/**
15 * A button device factory representing a keyboard. It receives keyboard events and forward them
16 * to all button devices it created.
17 */
18class Keyboard final : public Input::Factory<Input::ButtonDevice> {
19public:
20 Keyboard();
21
22 /**
23 * Creates a button device from a keyboard key
24 * @param params contains parameters for creating the device:
25 * - "code": the code of the key to bind with the button
26 */
27 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
28
29 /**
30 * Sets the status of all buttons bound with the key to pressed
31 * @param key_code the code of the key to press
32 */
33 void PressKey(int key_code);
34
35 /**
36 * Sets the status of all buttons bound with the key to released
37 * @param key_code the code of the key to release
38 */
39 void ReleaseKey(int key_code);
40
41 void ReleaseAllKeys();
42
43private:
44 std::shared_ptr<KeyButtonList> key_button_list;
45};
46
47} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
new file mode 100644
index 000000000..699f41e6b
--- /dev/null
+++ b/src/input_common/main.cpp
@@ -0,0 +1,63 @@
1// Copyright 2017 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 "common/param_package.h"
7#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h"
9#include "input_common/main.h"
10#ifdef HAVE_SDL2
11#include "input_common/sdl/sdl.h"
12#endif
13
14namespace InputCommon {
15
16static std::shared_ptr<Keyboard> keyboard;
17
18void Init() {
19 keyboard = std::make_shared<InputCommon::Keyboard>();
20 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
21 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
22 std::make_shared<InputCommon::AnalogFromButton>());
23#ifdef HAVE_SDL2
24 SDL::Init();
25#endif
26}
27
28void Shutdown() {
29 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
30 keyboard.reset();
31 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
32
33#ifdef HAVE_SDL2
34 SDL::Shutdown();
35#endif
36}
37
38Keyboard* GetKeyboard() {
39 return keyboard.get();
40}
41
42std::string GenerateKeyboardParam(int key_code) {
43 Common::ParamPackage param{
44 {"engine", "keyboard"}, {"code", std::to_string(key_code)},
45 };
46 return param.Serialize();
47}
48
49std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
50 int key_modifier, float modifier_scale) {
51 Common::ParamPackage circle_pad_param{
52 {"engine", "analog_from_button"},
53 {"up", GenerateKeyboardParam(key_up)},
54 {"down", GenerateKeyboardParam(key_down)},
55 {"left", GenerateKeyboardParam(key_left)},
56 {"right", GenerateKeyboardParam(key_right)},
57 {"modifier", GenerateKeyboardParam(key_modifier)},
58 {"modifier_scale", std::to_string(modifier_scale)},
59 };
60 return circle_pad_param.Serialize();
61}
62
63} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
new file mode 100644
index 000000000..140bbd014
--- /dev/null
+++ b/src/input_common/main.h
@@ -0,0 +1,29 @@
1// Copyright 2017 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 <string>
8
9namespace InputCommon {
10
11/// Initializes and registers all built-in input device factories.
12void Init();
13
14/// Unresisters all build-in input device factories and shut them down.
15void Shutdown();
16
17class Keyboard;
18
19/// Gets the keyboard button device factory.
20Keyboard* GetKeyboard();
21
22/// Generates a serialized param package for creating a keyboard button device
23std::string GenerateKeyboardParam(int key_code);
24
25/// Generates a serialized param package for creating an analog device taking input from keyboard
26std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
27 int key_modifier, float modifier_scale);
28
29} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
new file mode 100644
index 000000000..ae0206909
--- /dev/null
+++ b/src/input_common/sdl/sdl.cpp
@@ -0,0 +1,202 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cmath>
6#include <memory>
7#include <string>
8#include <tuple>
9#include <unordered_map>
10#include <SDL.h>
11#include "common/math_util.h"
12#include "input_common/sdl/sdl.h"
13
14namespace InputCommon {
15
16namespace SDL {
17
18class SDLJoystick;
19class SDLButtonFactory;
20class SDLAnalogFactory;
21static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list;
22static std::shared_ptr<SDLButtonFactory> button_factory;
23static std::shared_ptr<SDLAnalogFactory> analog_factory;
24
25static bool initialized = false;
26
27class SDLJoystick {
28public:
29 explicit SDLJoystick(int joystick_index)
30 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} {
31 if (!joystick) {
32 LOG_ERROR(Input, "failed to open joystick %d", joystick_index);
33 }
34 }
35
36 bool GetButton(int button) const {
37 if (!joystick)
38 return {};
39 SDL_JoystickUpdate();
40 return SDL_JoystickGetButton(joystick.get(), button) == 1;
41 }
42
43 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
44 if (!joystick)
45 return {};
46 SDL_JoystickUpdate();
47 float x = SDL_JoystickGetAxis(joystick.get(), axis_x) / 32767.0f;
48 float y = SDL_JoystickGetAxis(joystick.get(), axis_y) / 32767.0f;
49 y = -y; // 3DS uses an y-axis inverse from SDL
50
51 // Make sure the coordinates are in the unit circle,
52 // otherwise normalize it.
53 float r = x * x + y * y;
54 if (r > 1.0f) {
55 r = std::sqrt(r);
56 x /= r;
57 y /= r;
58 }
59
60 return std::make_tuple(x, y);
61 }
62
63 bool GetHatDirection(int hat, Uint8 direction) const {
64 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
65 }
66
67private:
68 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
69};
70
71class SDLButton final : public Input::ButtonDevice {
72public:
73 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
74 : joystick(joystick_), button(button_) {}
75
76 bool GetStatus() const override {
77 return joystick->GetButton(button);
78 }
79
80private:
81 std::shared_ptr<SDLJoystick> joystick;
82 int button;
83};
84
85class SDLDirectionButton final : public Input::ButtonDevice {
86public:
87 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
88 : joystick(joystick_), hat(hat_), direction(direction_) {}
89
90 bool GetStatus() const override {
91 return joystick->GetHatDirection(hat, direction);
92 }
93
94private:
95 std::shared_ptr<SDLJoystick> joystick;
96 int hat;
97 Uint8 direction;
98};
99
100class SDLAnalog final : public Input::AnalogDevice {
101public:
102 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
103 : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {}
104
105 std::tuple<float, float> GetStatus() const override {
106 return joystick->GetAnalog(axis_x, axis_y);
107 }
108
109private:
110 std::shared_ptr<SDLJoystick> joystick;
111 int axis_x;
112 int axis_y;
113};
114
115static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) {
116 std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock();
117 if (!joystick) {
118 joystick = std::make_shared<SDLJoystick>(joystick_index);
119 joystick_list[joystick_index] = joystick;
120 }
121 return joystick;
122}
123
124/// A button device factory that creates button devices from SDL joystick
125class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
126public:
127 /**
128 * Creates a button device from a joystick button
129 * @param params contains parameters for creating the device:
130 * - "joystick": the index of the joystick to bind
131 * - "button"(optional): the index of the button to bind
132 * - "hat"(optional): the index of the hat to bind as direction buttons
133 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
134 * "down", "left" or "right"
135 */
136 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
137 const int joystick_index = params.Get("joystick", 0);
138
139 if (params.Has("hat")) {
140 const int hat = params.Get("hat", 0);
141 const std::string direction_name = params.Get("direction", "");
142 Uint8 direction;
143 if (direction_name == "up") {
144 direction = SDL_HAT_UP;
145 } else if (direction_name == "down") {
146 direction = SDL_HAT_DOWN;
147 } else if (direction_name == "left") {
148 direction = SDL_HAT_LEFT;
149 } else if (direction_name == "right") {
150 direction = SDL_HAT_RIGHT;
151 } else {
152 direction = 0;
153 }
154 return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat,
155 direction);
156 }
157
158 const int button = params.Get("button", 0);
159 return std::make_unique<SDLButton>(GetJoystick(joystick_index), button);
160 }
161};
162
163/// An analog device factory that creates analog devices from SDL joystick
164class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
165public:
166 /**
167 * Creates analog device from joystick axes
168 * @param params contains parameters for creating the device:
169 * - "joystick": the index of the joystick to bind
170 * - "axis_x": the index of the axis to be bind as x-axis
171 * - "axis_y": the index of the axis to be bind as y-axis
172 */
173 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
174 const int joystick_index = params.Get("joystick", 0);
175 const int axis_x = params.Get("axis_x", 0);
176 const int axis_y = params.Get("axis_y", 1);
177 return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y);
178 }
179};
180
181void Init() {
182 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
183 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: %s", SDL_GetError());
184 } else {
185 using namespace Input;
186 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
187 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
188 initialized = true;
189 }
190}
191
192void Shutdown() {
193 if (initialized) {
194 using namespace Input;
195 UnregisterFactory<ButtonDevice>("sdl");
196 UnregisterFactory<AnalogDevice>("sdl");
197 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
198 }
199}
200
201} // namespace SDL
202} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
new file mode 100644
index 000000000..3e72debcc
--- /dev/null
+++ b/src/input_common/sdl/sdl.h
@@ -0,0 +1,19 @@
1// Copyright 2017 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 "core/frontend/input.h"
8
9namespace InputCommon {
10namespace SDL {
11
12/// Initializes and registers SDL device factories
13void Init();
14
15/// Unresisters SDL device factories and shut them down.
16void Shutdown();
17
18} // namespace SDL
19} // namespace InputCommon
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index b47156ca4..d1144ba77 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,6 +1,7 @@
1set(SRCS 1set(SRCS
2 glad.cpp 2 glad.cpp
3 tests.cpp 3 tests.cpp
4 common/param_package.cpp
4 core/file_sys/path_parser.cpp 5 core/file_sys/path_parser.cpp
5 ) 6 )
6 7
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
new file mode 100644
index 000000000..efec2cc86
--- /dev/null
+++ b/src/tests/common/param_package.cpp
@@ -0,0 +1,25 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch.hpp>
6#include <math.h>
7#include "common/param_package.h"
8
9namespace Common {
10
11TEST_CASE("ParamPackage", "[common]") {
12 ParamPackage original{
13 {"abc", "xyz"}, {"def", "42"}, {"jkl", "$$:1:$2$,3"},
14 };
15 original.Set("ghi", 3.14f);
16 ParamPackage copy(original.Serialize());
17 REQUIRE(copy.Get("abc", "") == "xyz");
18 REQUIRE(copy.Get("def", 0) == 42);
19 REQUIRE(std::abs(copy.Get("ghi", 0.0f) - 3.14f) < 0.01f);
20 REQUIRE(copy.Get("jkl", "") == "$$:1:$2$,3");
21 REQUIRE(copy.Get("mno", "uvw") == "uvw");
22 REQUIRE(copy.Get("abc", 42) == 42);
23}
24
25} // namespace Common
diff --git a/src/video_core/regs_framebuffer.h b/src/video_core/regs_framebuffer.h
index 366782080..9ddc79243 100644
--- a/src/video_core/regs_framebuffer.h
+++ b/src/video_core/regs_framebuffer.h
@@ -89,8 +89,8 @@ struct FramebufferRegs {
89 }; 89 };
90 90
91 union { 91 union {
92 BitField<0, 8, BlendEquation> blend_equation_rgb; 92 BitField<0, 3, BlendEquation> blend_equation_rgb;
93 BitField<8, 8, BlendEquation> blend_equation_a; 93 BitField<8, 3, BlendEquation> blend_equation_a;
94 94
95 BitField<16, 4, BlendFactor> factor_source_rgb; 95 BitField<16, 4, BlendFactor> factor_source_rgb;
96 BitField<20, 4, BlendFactor> factor_dest_rgb; 96 BitField<20, 4, BlendFactor> factor_dest_rgb;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 0818a87b3..456443e86 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -17,6 +17,7 @@
17#include "common/vector_math.h" 17#include "common/vector_math.h"
18#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
19#include "core/memory.h" 19#include "core/memory.h"
20#include "core/settings.h"
20#include "video_core/pica_state.h" 21#include "video_core/pica_state.h"
21#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 22#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
22#include "video_core/renderer_opengl/gl_state.h" 23#include "video_core/renderer_opengl/gl_state.h"