summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-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
85 files changed, 2041 insertions, 1096 deletions
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)
add_subdirectory(core)
add_subdirectory(video_core)
add_subdirectory(audio_core)
+add_subdirectory(input_common)
add_subdirectory(tests)
if (ENABLE_SDL2)
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})
include_directories(${SDL2_INCLUDE_DIR})
add_executable(citra ${SRCS} ${HEADERS})
-target_link_libraries(citra core video_core audio_core common)
+target_link_libraries(citra core video_core audio_core common input_common)
target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
if (MSVC)
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 @@
#include "citra/default_ini.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/param_package.h"
#include "config.h"
#include "core/settings.h"
+#include "input_common/main.h"
Config::Config() {
// 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) {
return true;
}
-static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
- // directly mapped keys
- SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
- SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
- SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
- SDL_SCANCODE_L,
-
- // indirectly mapped keys
- SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
+static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
+ SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
+ SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
+ SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
};
+static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
+ {
+ SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
+ },
+ {
+ SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, SDL_SCANCODE_D,
+ },
+}};
+
void Config::ReadValues() {
// Controls
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- Settings::values.input_mappings[Settings::NativeInput::All[i]] =
- sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.buttons[i] =
+ sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
+ if (Settings::values.buttons[i].empty())
+ Settings::values.buttons[i] = default_param;
+ }
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.analogs[i] =
+ sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
+ if (Settings::values.analogs[i].empty())
+ Settings::values.analogs[i] = default_param;
}
- Settings::values.pad_circle_modifier_scale =
- (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
// Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
@@ -77,6 +94,23 @@ void Config::ReadValues() {
Settings::values.layout_option =
static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0));
Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false);
+ Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false);
+ Settings::values.custom_top_left =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0));
+ Settings::values.custom_top_top =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_top", 0));
+ Settings::values.custom_top_right =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_right", 400));
+ Settings::values.custom_top_bottom =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_bottom", 240));
+ Settings::values.custom_bottom_left =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_left", 40));
+ Settings::values.custom_bottom_top =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_top", 240));
+ Settings::values.custom_bottom_right =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360));
+ Settings::values.custom_bottom_bottom =
+ static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480));
// Audio
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 {
const char* sdl2_config_file = R"(
[Controls]
-pad_start =
-pad_select =
-pad_home =
-pad_dup =
-pad_ddown =
-pad_dleft =
-pad_dright =
-pad_a =
-pad_b =
-pad_x =
-pad_y =
-pad_l =
-pad_r =
-pad_zl =
-pad_zr =
-pad_cup =
-pad_cdown =
-pad_cleft =
-pad_cright =
-pad_circle_up =
-pad_circle_down =
-pad_circle_left =
-pad_circle_right =
-pad_circle_modifier =
-
-# The applied modifier scale to circle pad.
-# Must be in range of 0.0-1.0. Defaults to 0.5
-pad_circle_modifier_scale =
+# The input devices and parameters for each 3DS native input
+# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
+# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
+
+# for button input, the following devices are avaible:
+# - "keyboard" (default) for keyboard input. Required parameters:
+# - "code": the code of the key to bind
+# - "sdl" for joystick input using SDL. Required parameters:
+# - "joystick": the index of the joystick to bind
+# - "button"(optional): the index of the button to bind
+# - "hat"(optional): the index of the hat to bind as direction buttons
+# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
+button_a=
+button_b=
+button_x=
+button_y=
+button_up=
+button_down=
+button_left=
+button_right=
+button_l=
+button_r=
+button_start=
+button_select=
+button_zl=
+button_zr=
+button_home=
+
+# for analog input, the following devices are avaible:
+# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
+# - "up", "down", "left", "right": sub-devices for each direction.
+# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
+# - "modifier": sub-devices as a modifier.
+# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
+# Must be in range of 0.0-1.0. Defaults to 0.5
+# - "sdl" for joystick input using SDL. Required parameters:
+# - "joystick": the index of the joystick to bind
+# - "axis_x": the index of the axis to bind as x-axis (default to 0)
+# - "axis_y": the index of the axis to bind as y-axis (default to 1)
+circle_pad=
+c_stick=
[Core]
# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
@@ -71,6 +84,21 @@ bg_green =
# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
layout_option =
+# Toggle custom layout (using the settings below) on or off.
+# 0 (default): Off , 1: On
+custom_layout =
+
+# Screen placement when using Custom layout option
+# 0x, 0y is the top left corner of the render window.
+custom_top_left =
+custom_top_top =
+custom_top_right =
+custom_top_bottom =
+custom_bottom_left =
+custom_bottom_top =
+custom_bottom_right =
+custom_bottom_bottom =
+
#Whether to toggle frame limiter on or off.
# 0: Off , 1 (default): On
toggle_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 @@
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
-#include "core/frontend/key_map.h"
-#include "core/hle/service/hid/hid.h"
#include "core/settings.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
#include "video_core/video_core.h"
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
- KeyMap::PressKey(*this, {key, keyboard_id});
+ InputCommon::GetKeyboard()->PressKey(key);
} else if (state == SDL_RELEASED) {
- KeyMap::ReleaseKey(*this, {key, keyboard_id});
+ InputCommon::GetKeyboard()->ReleaseKey(key);
}
}
@@ -57,9 +57,8 @@ void EmuWindow_SDL2::OnResize() {
}
EmuWindow_SDL2::EmuWindow_SDL2() {
- keyboard_id = KeyMap::NewDeviceId();
+ InputCommon::Init();
- ReloadSetKeymaps();
motion_emu = std::make_unique<Motion::MotionEmu>(*this);
SDL_SetMainReady();
@@ -117,6 +116,7 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
SDL_GL_DeleteContext(gl_context);
SDL_Quit();
motion_emu = nullptr;
+ InputCommon::Shutdown();
}
void EmuWindow_SDL2::SwapBuffers() {
@@ -169,15 +169,6 @@ void EmuWindow_SDL2::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
-void EmuWindow_SDL2::ReloadSetKeymaps() {
- KeyMap::ClearKeyMapping(keyboard_id);
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping(
- {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
- KeyMap::mapping_targets[i]);
- }
-}
-
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
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:
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;
- /// Load keymap from configuration
- void ReloadSetKeymaps() override;
-
private:
/// Called by PollEvents when a key is pressed or released.
void OnKeyEvent(int key, u8 state);
@@ -61,9 +58,6 @@ private:
/// The OpenGL context associated with the window
SDL_GLContext gl_context;
- /// Device id of keyboard for use with KeyMap
- int keyboard_id;
-
/// Motion sensors emulation
std::unique_ptr<Motion::MotionEmu> motion_emu;
};
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)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
set(SRCS
- config.cpp
+ configuration/config.cpp
+ configuration/configure_audio.cpp
+ configuration/configure_debug.cpp
+ configuration/configure_dialog.cpp
+ configuration/configure_general.cpp
+ configuration/configure_graphics.cpp
+ configuration/configure_input.cpp
+ configuration/configure_system.cpp
debugger/callstack.cpp
debugger/disassembler.cpp
debugger/graphics/graphics.cpp
@@ -19,13 +26,6 @@ set(SRCS
util/spinbox.cpp
util/util.cpp
bootmanager.cpp
- configure_audio.cpp
- configure_debug.cpp
- configure_dialog.cpp
- configure_general.cpp
- configure_graphics.cpp
- configure_system.cpp
- configure_input.cpp
game_list.cpp
hotkeys.cpp
main.cpp
@@ -35,7 +35,14 @@ set(SRCS
)
set(HEADERS
- config.h
+ configuration/config.h
+ configuration/configure_audio.h
+ configuration/configure_debug.h
+ configuration/configure_dialog.h
+ configuration/configure_general.h
+ configuration/configure_graphics.h
+ configuration/configure_input.h
+ configuration/configure_system.h
debugger/callstack.h
debugger/disassembler.h
debugger/graphics/graphics.h
@@ -52,13 +59,6 @@ set(HEADERS
util/spinbox.h
util/util.h
bootmanager.h
- configure_audio.h
- configure_debug.h
- configure_dialog.h
- configure_general.h
- configure_graphics.h
- configure_system.h
- configure_input.h
game_list.h
game_list_p.h
hotkeys.h
@@ -67,16 +67,16 @@ set(HEADERS
)
set(UIS
+ configuration/configure.ui
+ configuration/configure_audio.ui
+ configuration/configure_debug.ui
+ configuration/configure_general.ui
+ configuration/configure_graphics.ui
+ configuration/configure_input.ui
+ configuration/configure_system.ui
debugger/callstack.ui
debugger/disassembler.ui
debugger/registers.ui
- configure.ui
- configure_audio.ui
- configure_debug.ui
- configure_general.ui
- configure_graphics.ui
- configure_system.ui
- configure_input.ui
hotkeys.ui
main.ui
)
@@ -97,7 +97,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
-target_link_libraries(citra-qt core video_core audio_core common)
+target_link_libraries(citra-qt core video_core audio_core common input_common)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)
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 @@
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/core.h"
-#include "core/frontend/key_map.h"
+#include "core/settings.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/video_core.h"
@@ -99,14 +101,17 @@ private:
};
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
- : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) {
+ : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title));
- keyboard_id = KeyMap::NewDeviceId();
- ReloadSetKeymaps();
+ InputCommon::Init();
+}
+
+GRenderWindow::~GRenderWindow() {
+ InputCommon::Shutdown();
}
void GRenderWindow::moveContext() {
@@ -197,11 +202,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
- KeyMap::PressKey(*this, {event->key(), keyboard_id});
+ InputCommon::GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
- KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
+ InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -230,13 +235,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
motion_emu->EndTilt();
}
-void GRenderWindow::ReloadSetKeymaps() {
- KeyMap::ClearKeyMapping(keyboard_id);
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping(
- {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
- KeyMap::mapping_targets[i]);
- }
+void GRenderWindow::focusOutEvent(QFocusEvent* event) {
+ QWidget::focusOutEvent(event);
+ InputCommon::GetKeyboard()->ReleaseAllKeys();
}
void 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 {
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
+ ~GRenderWindow();
// EmuWindow implementation
void SwapBuffers() override;
@@ -127,7 +128,7 @@ public:
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
- void ReloadSetKeymaps() override;
+ void focusOutEvent(QFocusEvent* event) override;
void OnClientAreaResized(unsigned width, unsigned height);
@@ -152,9 +153,6 @@ private:
QByteArray geometry;
- /// Device id of keyboard for use with KeyMap
- int keyboard_id;
-
EmuThread* emu_thread;
/// 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 @@
// Refer to the license.txt file included.
#include <QSettings>
-#include "citra_qt/config.h"
+#include "citra_qt/configuration/config.h"
#include "citra_qt/ui_settings.h"
#include "common/file_util.h"
+#include "input_common/main.h"
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -16,25 +17,46 @@ Config::Config() {
Reload();
}
-const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
- // directly mapped keys
- Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
- Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
- Qt::Key_K, Qt::Key_J, Qt::Key_L,
-
- // indirectly mapped keys
- Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
+ Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
+ Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
};
+const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
+ {
+ Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+ },
+ {
+ Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D,
+ },
+}};
+
void Config::ReadValues() {
qt_config->beginGroup("Controls");
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- Settings::values.input_mappings[Settings::NativeInput::All[i]] =
- qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
- .toInt();
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.buttons[i] =
+ qt_config
+ ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.buttons[i].empty())
+ Settings::values.buttons[i] = default_param;
+ }
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.analogs[i] =
+ qt_config
+ ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.analogs[i].empty())
+ Settings::values.analogs[i] = default_param;
}
- Settings::values.pad_circle_modifier_scale =
- qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
+
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -57,6 +79,15 @@ void Config::ReadValues() {
Settings::values.layout_option =
static_cast<Settings::LayoutOption>(qt_config->value("layout_option").toInt());
Settings::values.swap_screen = qt_config->value("swap_screen", false).toBool();
+ Settings::values.custom_layout = qt_config->value("custom_layout", false).toBool();
+ Settings::values.custom_top_left = qt_config->value("custom_top_left", 0).toInt();
+ Settings::values.custom_top_top = qt_config->value("custom_top_top", 0).toInt();
+ Settings::values.custom_top_right = qt_config->value("custom_top_right", 400).toInt();
+ Settings::values.custom_top_bottom = qt_config->value("custom_top_bottom", 240).toInt();
+ Settings::values.custom_bottom_left = qt_config->value("custom_bottom_left", 40).toInt();
+ Settings::values.custom_bottom_top = qt_config->value("custom_bottom_top", 240).toInt();
+ Settings::values.custom_bottom_right = qt_config->value("custom_bottom_right", 360).toInt();
+ Settings::values.custom_bottom_bottom = qt_config->value("custom_bottom_bottom", 480).toInt();
qt_config->endGroup();
qt_config->beginGroup("Audio");
@@ -155,12 +186,14 @@ void Config::ReadValues() {
void Config::SaveValues() {
qt_config->beginGroup("Controls");
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
- Settings::values.input_mappings[Settings::NativeInput::All[i]]);
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(Settings::values.buttons[i]));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(Settings::values.analogs[i]));
}
- qt_config->setValue("pad_circle_modifier_scale",
- (double)Settings::values.pad_circle_modifier_scale);
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -183,6 +216,15 @@ void Config::SaveValues() {
qt_config->beginGroup("Layout");
qt_config->setValue("layout_option", static_cast<int>(Settings::values.layout_option));
qt_config->setValue("swap_screen", Settings::values.swap_screen);
+ qt_config->setValue("custom_layout", Settings::values.custom_layout);
+ qt_config->setValue("custom_top_left", Settings::values.custom_top_left);
+ qt_config->setValue("custom_top_top", Settings::values.custom_top_top);
+ qt_config->setValue("custom_top_right", Settings::values.custom_top_right);
+ qt_config->setValue("custom_top_bottom", Settings::values.custom_top_bottom);
+ qt_config->setValue("custom_bottom_left", Settings::values.custom_bottom_left);
+ qt_config->setValue("custom_bottom_top", Settings::values.custom_bottom_top);
+ qt_config->setValue("custom_bottom_right", Settings::values.custom_bottom_right);
+ qt_config->setValue("custom_bottom_bottom", Settings::values.custom_bottom_bottom);
qt_config->endGroup();
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 @@
#pragma once
+#include <array>
#include <string>
#include <QVariant>
#include "core/settings.h"
@@ -23,5 +24,7 @@ public:
void Reload();
void Save();
- static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
+
+ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
+ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
};
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 @@
<customwidget>
<class>ConfigureGeneral</class>
<extends>QWidget</extends>
- <header>configure_general.h</header>
+ <header>configuration/configure_general.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureSystem</class>
<extends>QWidget</extends>
- <header>configure_system.h</header>
+ <header>configuration/configure_system.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureAudio</class>
<extends>QWidget</extends>
- <header>configure_audio.h</header>
+ <header>configuration/configure_audio.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureDebug</class>
<extends>QWidget</extends>
- <header>configure_debug.h</header>
+ <header>configuration/configure_debug.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureInput</class>
<extends>QWidget</extends>
- <header>configure_input.h</header>
+ <header>configuration/configure_input.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureGraphics</class>
<extends>QWidget</extends>
- <header>configure_graphics.h</header>
+ <header>configuration/configure_graphics.h</header>
<container>1</container>
</customwidget>
</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 @@
#include "audio_core/audio_core.h"
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
-#include "citra_qt/configure_audio.h"
+#include "citra_qt/configuration/configure_audio.h"
#include "core/settings.h"
#include "ui_configure_audio.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "citra_qt/configure_debug.h"
+#include "citra_qt/configuration/configure_debug.h"
#include "core/settings.h"
#include "ui_configure_debug.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "citra_qt/config.h"
-#include "citra_qt/configure_dialog.h"
+#include "citra_qt/configuration/config.h"
+#include "citra_qt/configuration/configure_dialog.h"
#include "core/settings.h"
#include "ui_configure.h"
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "citra_qt/configure_general.h"
+#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
#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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "citra_qt/configure_graphics.h"
+#include "citra_qt/configuration/configure_graphics.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
@@ -14,6 +14,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
this->setConfiguration();
ui->toggle_vsync->setEnabled(!Core::System::GetInstance().IsPoweredOn());
+
+ ui->layoutBox->setEnabled(!Settings::values.custom_layout);
}
ConfigureGraphics::~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 @@
</layout>
</item>
<item>
- <widget class="QGroupBox" name="groupBox2">
+ <widget class="QGroupBox" name="layoutBox">
<property name="title">
<string>Layout</string>
</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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <QTimer>
+#include "citra_qt/configuration/config.h"
+#include "citra_qt/configuration/configure_input.h"
+#include "common/param_package.h"
+#include "input_common/main.h"
+
+const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
+ ConfigureInput::analog_sub_buttons{{
+ "up", "down", "left", "right", "modifier",
+ }};
+
+static QString getKeyName(int key_code) {
+ switch (key_code) {
+ case Qt::Key_Shift:
+ return QObject::tr("Shift");
+ case Qt::Key_Control:
+ return QObject::tr("Ctrl");
+ case Qt::Key_Alt:
+ return QObject::tr("Alt");
+ case Qt::Key_Meta:
+ return "";
+ default:
+ return QKeySequence(key_code).toString();
+ }
+}
+
+static void SetButtonKey(int key, Common::ParamPackage& button_param) {
+ button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)};
+}
+
+static void SetAnalogKey(int key, Common::ParamPackage& analog_param,
+ const std::string& button_name) {
+ if (analog_param.Get("engine", "") != "analog_from_button") {
+ analog_param = {
+ {"engine", "analog_from_button"}, {"modifier_scale", "0.5"},
+ };
+ }
+ analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key));
+}
+
+ConfigureInput::ConfigureInput(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
+ timer(std::make_unique<QTimer>()) {
+
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+
+ button_map = {
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp,
+ ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR,
+ ui->buttonStart, ui->buttonSelect, ui->buttonZL, ui->buttonZR, ui->buttonHome,
+ };
+
+ analog_map = {{
+ {
+ ui->buttonCircleUp, ui->buttonCircleDown, ui->buttonCircleLeft, ui->buttonCircleRight,
+ ui->buttonCircleMod,
+ },
+ {
+ ui->buttonCStickUp, ui->buttonCStickDown, ui->buttonCStickLeft, ui->buttonCStickRight,
+ nullptr,
+ },
+ }};
+
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ if (button_map[button_id])
+ connect(button_map[button_id], &QPushButton::released, [=]() {
+ handleClick(button_map[button_id],
+ [=](int key) { SetButtonKey(key, buttons_param[button_id]); });
+ });
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ if (analog_map[analog_id][sub_button_id] != nullptr) {
+ connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() {
+ handleClick(analog_map[analog_id][sub_button_id], [=](int key) {
+ SetAnalogKey(key, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
+ });
+ });
+ }
+ }
+ }
+
+ connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
+
+ timer->setSingleShot(true);
+ connect(timer.get(), &QTimer::timeout, [this]() {
+ releaseKeyboard();
+ releaseMouse();
+ key_setter = boost::none;
+ updateButtonLabels();
+ });
+
+ this->loadConfiguration();
+
+ // TODO(wwylele): enable these when the input emulation for them is implemented
+ ui->buttonZL->setEnabled(false);
+ ui->buttonZR->setEnabled(false);
+ ui->buttonHome->setEnabled(false);
+ ui->buttonCStickUp->setEnabled(false);
+ ui->buttonCStickDown->setEnabled(false);
+ ui->buttonCStickLeft->setEnabled(false);
+ ui->buttonCStickRight->setEnabled(false);
+}
+
+void ConfigureInput::applyConfiguration() {
+ std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+ std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+ Settings::Apply();
+}
+
+void ConfigureInput::loadConfiguration() {
+ std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
+ buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
+ analogs_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ updateButtonLabels();
+}
+
+void ConfigureInput::restoreDefaults() {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]);
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ SetAnalogKey(Config::default_analogs[analog_id][sub_button_id],
+ analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ }
+ }
+ updateButtonLabels();
+ applyConfiguration();
+}
+
+void ConfigureInput::updateButtonLabels() {
+ QString non_keyboard(tr("[non-keyboard]"));
+
+ auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) {
+ if (param.Get("engine", "") != "keyboard") {
+ return non_keyboard;
+ } else {
+ return getKeyName(param.Get("code", 0));
+ }
+ };
+
+ for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
+ button_map[button]->setText(KeyToText(buttons_param[button]));
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
+ for (QPushButton* button : analog_map[analog_id]) {
+ if (button)
+ button->setText(non_keyboard);
+ }
+ } else {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ Common::ParamPackage param(
+ analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], ""));
+ if (analog_map[analog_id][sub_button_id])
+ analog_map[analog_id][sub_button_id]->setText(KeyToText(param));
+ }
+ }
+ }
+}
+
+void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) {
+ button->setText(tr("[press key]"));
+ button->setFocus();
+
+ key_setter = new_key_setter;
+
+ grabKeyboard();
+ grabMouse();
+ timer->start(5000); // Cancel after 5 seconds
+}
+
+void ConfigureInput::keyPressEvent(QKeyEvent* event) {
+ releaseKeyboard();
+ releaseMouse();
+
+ if (!key_setter || !event)
+ return;
+
+ if (event->key() != Qt::Key_Escape)
+ (*key_setter)(event->key());
+
+ updateButtonLabels();
+ key_setter = boost::none;
+ timer->stop();
+}
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include <string>
+#include <QKeyEvent>
+#include <QWidget>
+#include <boost/optional.hpp>
+#include "common/param_package.h"
+#include "core/settings.h"
+#include "ui_configure_input.h"
+
+class QPushButton;
+class QString;
+class QTimer;
+
+namespace Ui {
+class ConfigureInput;
+}
+
+class ConfigureInput : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInput(QWidget* parent = nullptr);
+
+ /// Save all button configurations to settings file
+ void applyConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureInput> ui;
+
+ std::unique_ptr<QTimer> timer;
+
+ /// This will be the the setting function when an input is awaiting configuration.
+ boost::optional<std::function<void(int)>> key_setter;
+
+ std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
+ std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
+
+ /// Each button input is represented by a QPushButton.
+ std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+
+ /// Each analog input is represented by five QPushButtons which represents up, down, left, right
+ /// and modifier
+ std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
+ analog_map;
+
+ static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
+
+ /// Load configuration settings.
+ void loadConfiguration();
+ /// Restore all buttons to their default values.
+ void restoreDefaults();
+ /// Update UI to reflect current configuration.
+ void updateButtonLabels();
+
+ /// Called when the button was pressed.
+ void handleClick(QPushButton* button, std::function<void(int)> new_key_setter);
+ /// Handle key press events.
+ void keyPressEvent(QKeyEvent* event) override;
+};
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "citra_qt/configure_system.h"
+#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
#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 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <memory>
-#include <utility>
-#include <QTimer>
-#include "citra_qt/config.h"
-#include "citra_qt/configure_input.h"
-
-static QString getKeyName(Qt::Key key_code) {
- switch (key_code) {
- case Qt::Key_Shift:
- return QObject::tr("Shift");
- case Qt::Key_Control:
- return QObject::tr("Ctrl");
- case Qt::Key_Alt:
- return QObject::tr("Alt");
- case Qt::Key_Meta:
- return "";
- default:
- return QKeySequence(key_code).toString();
- }
-}
-
-ConfigureInput::ConfigureInput(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
- timer(std::make_unique<QTimer>()) {
-
- ui->setupUi(this);
- setFocusPolicy(Qt::ClickFocus);
-
- button_map = {
- {Settings::NativeInput::Values::A, ui->buttonA},
- {Settings::NativeInput::Values::B, ui->buttonB},
- {Settings::NativeInput::Values::X, ui->buttonX},
- {Settings::NativeInput::Values::Y, ui->buttonY},
- {Settings::NativeInput::Values::L, ui->buttonL},
- {Settings::NativeInput::Values::R, ui->buttonR},
- {Settings::NativeInput::Values::ZL, ui->buttonZL},
- {Settings::NativeInput::Values::ZR, ui->buttonZR},
- {Settings::NativeInput::Values::START, ui->buttonStart},
- {Settings::NativeInput::Values::SELECT, ui->buttonSelect},
- {Settings::NativeInput::Values::HOME, ui->buttonHome},
- {Settings::NativeInput::Values::DUP, ui->buttonDpadUp},
- {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown},
- {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft},
- {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight},
- {Settings::NativeInput::Values::CUP, ui->buttonCStickUp},
- {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown},
- {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft},
- {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight},
- {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp},
- {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown},
- {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft},
- {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight},
- {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod},
- };
-
- for (const auto& entry : button_map) {
- const Settings::NativeInput::Values input_id = entry.first;
- connect(entry.second, &QPushButton::released,
- [this, input_id]() { handleClick(input_id); });
- }
-
- connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
-
- timer->setSingleShot(true);
- connect(timer.get(), &QTimer::timeout, [this]() {
- releaseKeyboard();
- releaseMouse();
- current_input_id = boost::none;
- updateButtonLabels();
- });
-
- this->loadConfiguration();
-}
-
-void ConfigureInput::applyConfiguration() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]);
- }
- Settings::Apply();
-}
-
-void ConfigureInput::loadConfiguration() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]);
- }
- updateButtonLabels();
-}
-
-void ConfigureInput::restoreDefaults() {
- for (const auto& input_id : Settings::NativeInput::All) {
- const size_t index = static_cast<size_t>(input_id);
- key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt());
- }
- updateButtonLabels();
- applyConfiguration();
-}
-
-void ConfigureInput::updateButtonLabels() {
- for (const auto& input_id : Settings::NativeInput::All) {
- button_map[input_id]->setText(getKeyName(key_map[input_id]));
- }
-}
-
-void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) {
- QPushButton* button = button_map[input_id];
- button->setText(tr("[press key]"));
- button->setFocus();
-
- current_input_id = input_id;
-
- grabKeyboard();
- grabMouse();
- timer->start(5000); // Cancel after 5 seconds
-}
-
-void ConfigureInput::keyPressEvent(QKeyEvent* event) {
- releaseKeyboard();
- releaseMouse();
-
- if (!current_input_id || !event)
- return;
-
- if (event->key() != Qt::Key_Escape)
- setInput(*current_input_id, static_cast<Qt::Key>(event->key()));
-
- updateButtonLabels();
- current_input_id = boost::none;
- timer->stop();
-}
-
-void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) {
- // Remove duplicates
- for (auto& pair : key_map) {
- if (pair.second == key_pressed)
- pair.second = Qt::Key_unknown;
- }
-
- key_map[input_id] = key_pressed;
-}
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 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <QKeyEvent>
-#include <QWidget>
-#include <boost/optional.hpp>
-#include "core/settings.h"
-#include "ui_configure_input.h"
-
-class QPushButton;
-class QString;
-class QTimer;
-
-namespace Ui {
-class ConfigureInput;
-}
-
-class ConfigureInput : public QWidget {
- Q_OBJECT
-
-public:
- explicit ConfigureInput(QWidget* parent = nullptr);
-
- /// Save all button configurations to settings file
- void applyConfiguration();
-
-private:
- std::unique_ptr<Ui::ConfigureInput> ui;
-
- /// This input is currently awaiting configuration.
- /// (i.e.: its corresponding QPushButton has been pressed.)
- boost::optional<Settings::NativeInput::Values> current_input_id;
- std::unique_ptr<QTimer> timer;
-
- /// Each input is represented by a QPushButton.
- std::map<Settings::NativeInput::Values, QPushButton*> button_map;
- /// Each input is configured to respond to the press of a Qt::Key.
- std::map<Settings::NativeInput::Values, Qt::Key> key_map;
-
- /// Load configuration settings.
- void loadConfiguration();
- /// Restore all buttons to their default values.
- void restoreDefaults();
- /// Update UI to reflect current configuration.
- void updateButtonLabels();
-
- /// Called when the button corresponding to input_id was pressed.
- void handleClick(Settings::NativeInput::Values input_id);
- /// Handle key press events.
- void keyPressEvent(QKeyEvent* event) override;
- /// Configure input input_id to respond to key key_pressed.
- void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed);
-};
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 @@
#include <QtGui>
#include <QtWidgets>
#include "citra_qt/bootmanager.h"
-#include "citra_qt/config.h"
-#include "citra_qt/configure_dialog.h"
+#include "citra_qt/configuration/config.h"
+#include "citra_qt/configuration/configure_dialog.h"
#include "citra_qt/debugger/callstack.h"
#include "citra_qt/debugger/disassembler.h"
#include "citra_qt/debugger/graphics/graphics.h"
@@ -612,7 +612,6 @@ void GMainWindow::OnConfigure() {
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
- render_window->ReloadSetKeymaps();
config->Save();
}
}
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
memory_util.cpp
microprofile.cpp
misc.cpp
+ param_package.cpp
scm_rev.cpp
string_util.cpp
symbols.cpp
@@ -66,6 +67,7 @@ set(HEADERS
memory_util.h
microprofile.h
microprofileui.h
+ param_package.h
platform.h
quaternion.h
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) {
#endif
if (result < 0) {
- LOG_WARNING(Common_Filesystem, "stat failed on %s: %s", filename.c_str(),
- GetLastErrorMsg());
+ LOG_DEBUG(Common_Filesystem, "stat failed on %s: %s", filename.c_str(), GetLastErrorMsg());
return false;
}
@@ -129,12 +128,12 @@ bool IsDirectory(const std::string& filename) {
// Deletes a given filename, return true on success
// Doesn't supports deleting a directory
bool Delete(const std::string& filename) {
- LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
+ LOG_TRACE(Common_Filesystem, "file %s", filename.c_str());
// Return true because we care about the file no
// being there, not the actual delete.
if (!Exists(filename)) {
- LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
+ LOG_DEBUG(Common_Filesystem, "%s does not exist", filename.c_str());
return true;
}
@@ -169,8 +168,7 @@ bool CreateDir(const std::string& path) {
return true;
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
- LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists",
- path.c_str());
+ LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
return true;
}
LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
@@ -182,7 +180,7 @@ bool CreateDir(const std::string& path) {
int err = errno;
if (err == EEXIST) {
- LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
+ LOG_DEBUG(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
return true;
}
@@ -197,7 +195,7 @@ bool CreateFullPath(const std::string& fullPath) {
LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
if (FileUtil::Exists(fullPath)) {
- LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
+ LOG_DEBUG(Common_Filesystem, "path exists %s", fullPath.c_str());
return true;
}
@@ -229,7 +227,7 @@ bool CreateFullPath(const std::string& fullPath) {
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string& filename) {
- LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
+ LOG_TRACE(Common_Filesystem, "directory %s", filename.c_str());
// check if a directory
if (!FileUtil::IsDirectory(filename)) {
@@ -693,6 +691,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ } else {
+ LOG_INFO(Common_Filesystem, "Using the local user directory");
}
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 @@
#include "common/assert.h"
#include "common/framebuffer_layout.h"
+#include "core/settings.h"
#include "video_core/video_core.h"
namespace Layout {
@@ -135,4 +136,22 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
res.bottom_screen = swapped ? large_screen : small_screen;
return res;
}
+
+FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+
+ FramebufferLayout res{width, height, true, true, {}, {}};
+
+ MathUtil::Rectangle<unsigned> top_screen{
+ Settings::values.custom_top_left, Settings::values.custom_top_top,
+ Settings::values.custom_top_right, Settings::values.custom_top_bottom};
+ MathUtil::Rectangle<unsigned> bot_screen{
+ Settings::values.custom_bottom_left, Settings::values.custom_bottom_top,
+ Settings::values.custom_bottom_right, Settings::values.custom_bottom_bottom};
+
+ res.top_screen = top_screen;
+ res.bottom_screen = bot_screen;
+ return res;
+}
}
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
* @return Newly created FramebufferLayout object with default screen regions initialized
*/
FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
+
+/**
+ * Factory method for constructing a custom FramebufferLayout
+ * @param width Window framebuffer width in pixels
+ * @param height Window framebuffer height in pixels
+ * @return Newly created FramebufferLayout object with default screen regions initialized
+ */
+FramebufferLayout CustomFrameLayout(unsigned width, unsigned height);
}
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 {
CLS(Audio) \
SUB(Audio, DSP) \
SUB(Audio, Sink) \
+ CLS(Input) \
CLS(Loader)
// 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 {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
+ Input, ///< Input emulation
Count ///< Total number of logging classes
};
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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <vector>
+#include "common/logging/log.h"
+#include "common/param_package.h"
+#include "common/string_util.h"
+
+namespace Common {
+
+constexpr char KEY_VALUE_SEPARATOR = ':';
+constexpr char PARAM_SEPARATOR = ',';
+constexpr char ESCAPE_CHARACTER = '$';
+const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'};
+const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'};
+const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'};
+
+ParamPackage::ParamPackage(const std::string& serialized) {
+ std::vector<std::string> pairs;
+ Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
+
+ for (const std::string& pair : pairs) {
+ std::vector<std::string> key_value;
+ Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
+ if (key_value.size() != 2) {
+ LOG_ERROR(Common, "invalid key pair %s", pair.c_str());
+ continue;
+ }
+
+ for (std::string& part : key_value) {
+ part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
+ part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
+ part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
+ }
+
+ Set(key_value[0], key_value[1]);
+ }
+}
+
+ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : data(list) {}
+
+std::string ParamPackage::Serialize() const {
+ if (data.empty())
+ return "";
+
+ std::string result;
+
+ for (const auto& pair : data) {
+ std::array<std::string, 2> key_value{{pair.first, pair.second}};
+ for (std::string& part : key_value) {
+ part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
+ part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
+ part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
+ }
+ result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
+ }
+
+ result.pop_back(); // discard the trailing PARAM_SEPARATOR
+ return result;
+}
+
+std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const {
+ auto pair = data.find(key);
+ if (pair == data.end()) {
+ LOG_DEBUG(Common, "key %s not found", key.c_str());
+ return default_value;
+ }
+
+ return pair->second;
+}
+
+int ParamPackage::Get(const std::string& key, int default_value) const {
+ auto pair = data.find(key);
+ if (pair == data.end()) {
+ LOG_DEBUG(Common, "key %s not found", key.c_str());
+ return default_value;
+ }
+
+ try {
+ return std::stoi(pair->second);
+ } catch (const std::logic_error&) {
+ LOG_ERROR(Common, "failed to convert %s to int", pair->second.c_str());
+ return default_value;
+ }
+}
+
+float ParamPackage::Get(const std::string& key, float default_value) const {
+ auto pair = data.find(key);
+ if (pair == data.end()) {
+ LOG_DEBUG(Common, "key %s not found", key.c_str());
+ return default_value;
+ }
+
+ try {
+ return std::stof(pair->second);
+ } catch (const std::logic_error&) {
+ LOG_ERROR(Common, "failed to convert %s to float", pair->second.c_str());
+ return default_value;
+ }
+}
+
+void ParamPackage::Set(const std::string& key, const std::string& value) {
+ data[key] = value;
+}
+
+void ParamPackage::Set(const std::string& key, int value) {
+ data[key] = std::to_string(value);
+}
+
+void ParamPackage::Set(const std::string& key, float value) {
+ data[key] = std::to_string(value);
+}
+
+bool ParamPackage::Has(const std::string& key) const {
+ return data.find(key) != data.end();
+}
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <initializer_list>
+#include <string>
+#include <unordered_map>
+
+namespace Common {
+
+/// A string-based key-value container supporting serializing to and deserializing from a string
+class ParamPackage {
+public:
+ using DataType = std::unordered_map<std::string, std::string>;
+
+ ParamPackage() = default;
+ explicit ParamPackage(const std::string& serialized);
+ ParamPackage(std::initializer_list<DataType::value_type> list);
+ ParamPackage(const ParamPackage& other) = default;
+ ParamPackage(ParamPackage&& other) = default;
+
+ ParamPackage& operator=(const ParamPackage& other) = default;
+ ParamPackage& operator=(ParamPackage&& other) = default;
+
+ std::string Serialize() const;
+ std::string Get(const std::string& key, const std::string& default_value) const;
+ int Get(const std::string& key, int default_value) const;
+ float Get(const std::string& key, float default_value) const;
+ void Set(const std::string& key, const std::string& value);
+ void Set(const std::string& key, int value);
+ void Set(const std::string& key, float value);
+ bool Has(const std::string& key) const;
+
+private:
+ DataType data;
+};
+
+} // 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 @@
#include "common/common_types.h"
#include "cpu_detect.h"
-namespace Common {
-
-#ifndef _MSC_VER
+#ifdef _MSC_VER
+#include <intrin.h>
+#else
#if defined(__DragonFly__) || defined(__FreeBSD__)
// clang-format off
@@ -37,13 +37,15 @@ static inline void __cpuid(int info[4], int function_id) {
}
#define _XCR_XFEATURE_ENABLED_MASK 0
-static u64 _xgetbv(u32 index) {
+static inline u64 _xgetbv(u32 index) {
u32 eax, edx;
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
return ((u64)edx << 32) | eax;
}
-#endif // ifndef _MSC_VER
+#endif // _MSC_VER
+
+namespace Common {
// Detects the various CPU features
static 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
frontend/camera/factory.cpp
frontend/camera/interface.cpp
frontend/emu_window.cpp
- frontend/key_map.cpp
frontend/motion_emu.cpp
gdbstub/gdbstub.cpp
hle/config_mem.cpp
@@ -218,7 +217,7 @@ set(HEADERS
frontend/camera/factory.h
frontend/camera/interface.h
frontend/emu_window.h
- frontend/key_map.h
+ frontend/input.h
frontend/motion_emu.h
gdbstub/gdbstub.h
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) {
ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location,
bool shared)
: shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
- LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
+ LOG_DEBUG(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
}
bool 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 {
ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
: sdmc_directory(sdmc_directory) {
- LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
+ LOG_DEBUG(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
}
bool 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
ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
: sdmc_directory(mount_point) {
- LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
+ LOG_DEBUG(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
}
bool 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
ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory)
: mount_point(GetSaveDataContainerPath(sdmc_directory)) {
- LOG_INFO(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
+ LOG_DEBUG(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
}
ResultVal<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 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
-#include "core/frontend/key_map.h"
+#include "core/settings.h"
#include "video_core/video_core.h"
-void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
- pad_state.hex |= pad.hex;
-}
-
-void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
- pad_state.hex &= ~pad.hex;
-}
-
-void EmuWindow::CirclePadUpdated(float x, float y) {
- constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
-
- // Make sure the coordinates are in the unit circle,
- // otherwise normalize it.
- float r = x * x + y * y;
- if (r > 1) {
- r = std::sqrt(r);
- x /= r;
- y /= r;
- }
-
- circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
- circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
-}
-
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -113,17 +89,21 @@ void EmuWindow::GyroscopeChanged(float x, float y, float z) {
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
Layout::FramebufferLayout layout;
- switch (Settings::values.layout_option) {
- case Settings::LayoutOption::SingleScreen:
- layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen);
- break;
- case Settings::LayoutOption::LargeScreen:
- layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
- break;
- case Settings::LayoutOption::Default:
- default:
- layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
- break;
+ if (Settings::values.custom_layout == true) {
+ layout = Layout::CustomFrameLayout(width, height);
+ } else {
+ switch (Settings::values.layout_option) {
+ case Settings::LayoutOption::SingleScreen:
+ layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen);
+ break;
+ case Settings::LayoutOption::LargeScreen:
+ layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
+ break;
+ case Settings::LayoutOption::Default:
+ default:
+ layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
+ break;
+ }
}
NotifyFramebufferLayoutChanged(layout);
}
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 @@
#include "common/common_types.h"
#include "common/framebuffer_layout.h"
#include "common/math_util.h"
-#include "core/hle/service/hid/hid.h"
/**
* Abstraction class used to provide an interface between emulation code and the frontend
@@ -52,30 +51,6 @@ public:
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
virtual void DoneCurrent() = 0;
- virtual void ReloadSetKeymaps() = 0;
-
- /**
- * Signals a button press action to the HID module.
- * @param pad_state indicates which button to press
- * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
- */
- void ButtonPressed(Service::HID::PadState pad_state);
-
- /**
- * Signals a button release action to the HID module.
- * @param pad_state indicates which button to press
- * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
- */
- void ButtonReleased(Service::HID::PadState pad_state);
-
- /**
- * Signals a circle pad change action to the HID module.
- * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
- * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
- * @note the coordinates will be normalized if the radius is larger than 1
- */
- void CirclePadUpdated(float x, float y);
-
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
@@ -115,27 +90,6 @@ public:
void GyroscopeChanged(float x, float y, float z);
/**
- * Gets the current pad state (which buttons are pressed).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @note This doesn't include analog input like circle pad direction
- * @todo Fix this function to be thread-safe.
- * @return PadState object indicating the current pad state
- */
- Service::HID::PadState GetPadState() const {
- return pad_state;
- }
-
- /**
- * Gets the current circle pad state.
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @todo Fix this function to be thread-safe.
- * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
- */
- std::tuple<s16, s16> GetCirclePadState() const {
- return std::make_tuple(circle_pad_x, circle_pad_y);
- }
-
- /**
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
* @note This should be called by the core emu thread to get a state set by the window thread.
* @todo Fix this function to be thread-safe.
@@ -230,11 +184,8 @@ protected:
// TODO: Find a better place to set this.
config.min_client_area_size = std::make_pair(400u, 480u);
active_config = config;
- pad_state.hex = 0;
touch_x = 0;
touch_y = 0;
- circle_pad_x = 0;
- circle_pad_y = 0;
touch_pressed = false;
accel_x = 0;
accel_y = -512;
@@ -304,9 +255,6 @@ private:
u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
- s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
- s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
-
std::mutex accel_mutex;
s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
@@ -321,6 +269,4 @@ private:
* Clip the provided coordinates to be inside the touchscreen area.
*/
std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
-
- Service::HID::PadState pad_state;
};
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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include "common/logging/log.h"
+#include "common/param_package.h"
+
+namespace Input {
+
+/// An abstract class template for an input device (a button, an analog input, etc.).
+template <typename StatusType>
+class InputDevice {
+public:
+ virtual ~InputDevice() = default;
+ virtual StatusType GetStatus() const {
+ return {};
+ }
+};
+
+/// An abstract class template for a factory that can create input devices.
+template <typename InputDeviceType>
+class Factory {
+public:
+ virtual ~Factory() = default;
+ virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
+};
+
+namespace Impl {
+
+template <typename InputDeviceType>
+using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
+
+template <typename InputDeviceType>
+struct FactoryList {
+ static FactoryListType<InputDeviceType> list;
+};
+
+template <typename InputDeviceType>
+FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
+
+} // namespace Impl
+
+/**
+ * Registers an input device factory.
+ * @tparam InputDeviceType the type of input devices the factory can create
+ * @param name the name of the factory. Will be used to match the "engine" parameter when creating
+ * a device
+ * @param factory the factory object to register
+ */
+template <typename InputDeviceType>
+void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
+ auto pair = std::make_pair(name, std::move(factory));
+ if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
+ LOG_ERROR(Input, "Factory %s already registered", name.c_str());
+ }
+}
+
+/**
+ * Unregisters an input device factory.
+ * @tparam InputDeviceType the type of input devices the factory can create
+ * @param name the name of the factory to unregister
+ */
+template <typename InputDeviceType>
+void UnregisterFactory(const std::string& name) {
+ if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
+ LOG_ERROR(Input, "Factory %s not registered", name.c_str());
+ }
+}
+
+/**
+ * Create an input device from given paramters.
+ * @tparam InputDeviceType the type of input devices to create
+ * @param params a serialized ParamPackage string contains all parameters for creating the device
+ */
+template <typename InputDeviceType>
+std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
+ const Common::ParamPackage package(params);
+ const std::string engine = package.Get("engine", "null");
+ const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
+ const auto pair = factory_list.find(engine);
+ if (pair == factory_list.end()) {
+ if (engine != "null") {
+ LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str());
+ }
+ return std::make_unique<InputDeviceType>();
+ }
+ return pair->second->Create(package);
+}
+
+/**
+ * A button device is an input device that returns bool as status.
+ * true for pressed; false for released.
+ */
+using ButtonDevice = InputDevice<bool>;
+
+/**
+ * An analog device is an input device that returns a tuple of x and y coordinates as status. The
+ * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up
+ * direction
+ */
+using AnalogDevice = InputDevice<std::tuple<float, float>>;
+
+} // 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 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <map>
-#include "core/frontend/emu_window.h"
-#include "core/frontend/key_map.h"
-
-namespace KeyMap {
-
-// TODO (wwylele): currently we treat c-stick as four direction buttons
-// and map it directly to EmuWindow::ButtonPressed.
-// It should go the analog input way like circle pad does.
-const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
- Service::HID::PAD_A,
- Service::HID::PAD_B,
- Service::HID::PAD_X,
- Service::HID::PAD_Y,
- Service::HID::PAD_L,
- Service::HID::PAD_R,
- Service::HID::PAD_ZL,
- Service::HID::PAD_ZR,
- Service::HID::PAD_START,
- Service::HID::PAD_SELECT,
- Service::HID::PAD_NONE,
- Service::HID::PAD_UP,
- Service::HID::PAD_DOWN,
- Service::HID::PAD_LEFT,
- Service::HID::PAD_RIGHT,
- Service::HID::PAD_C_UP,
- Service::HID::PAD_C_DOWN,
- Service::HID::PAD_C_LEFT,
- Service::HID::PAD_C_RIGHT,
-
- IndirectTarget::CirclePadUp,
- IndirectTarget::CirclePadDown,
- IndirectTarget::CirclePadLeft,
- IndirectTarget::CirclePadRight,
- IndirectTarget::CirclePadModifier,
-}};
-
-static std::map<HostDeviceKey, KeyTarget> key_map;
-static int next_device_id = 0;
-
-static bool circle_pad_up = false;
-static bool circle_pad_down = false;
-static bool circle_pad_left = false;
-static bool circle_pad_right = false;
-static bool circle_pad_modifier = false;
-
-static void UpdateCirclePad(EmuWindow& emu_window) {
- constexpr float SQRT_HALF = 0.707106781f;
- int x = 0, y = 0;
-
- if (circle_pad_right)
- ++x;
- if (circle_pad_left)
- --x;
- if (circle_pad_up)
- ++y;
- if (circle_pad_down)
- --y;
-
- float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f;
- emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF),
- y * modifier * (x == 0 ? 1.0f : SQRT_HALF));
-}
-
-int NewDeviceId() {
- return next_device_id++;
-}
-
-void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
- key_map[key] = target;
-}
-
-void ClearKeyMapping(int device_id) {
- auto iter = key_map.begin();
- while (iter != key_map.end()) {
- if (iter->first.device_id == device_id)
- key_map.erase(iter++);
- else
- ++iter;
- }
-}
-
-void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
- auto target = key_map.find(key);
- if (target == key_map.end())
- return;
-
- if (target->second.direct) {
- emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
- } else {
- switch (target->second.target.indirect_target) {
- case IndirectTarget::CirclePadUp:
- circle_pad_up = true;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadDown:
- circle_pad_down = true;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadLeft:
- circle_pad_left = true;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadRight:
- circle_pad_right = true;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadModifier:
- circle_pad_modifier = true;
- UpdateCirclePad(emu_window);
- break;
- }
- }
-}
-
-void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key) {
- auto target = key_map.find(key);
- if (target == key_map.end())
- return;
-
- if (target->second.direct) {
- emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
- } else {
- switch (target->second.target.indirect_target) {
- case IndirectTarget::CirclePadUp:
- circle_pad_up = false;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadDown:
- circle_pad_down = false;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadLeft:
- circle_pad_left = false;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadRight:
- circle_pad_right = false;
- UpdateCirclePad(emu_window);
- break;
- case IndirectTarget::CirclePadModifier:
- circle_pad_modifier = false;
- UpdateCirclePad(emu_window);
- break;
- }
- }
-}
-}
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 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <tuple>
-#include "core/hle/service/hid/hid.h"
-
-class EmuWindow;
-
-namespace KeyMap {
-
-/**
- * Represents key mapping targets that are not real 3DS buttons.
- * They will be handled by KeyMap and translated to 3DS input.
- */
-enum class IndirectTarget {
- CirclePadUp,
- CirclePadDown,
- CirclePadLeft,
- CirclePadRight,
- CirclePadModifier,
-};
-
-/**
- * Represents a key mapping target. It can be a PadState that represents real 3DS buttons,
- * or an IndirectTarget.
- */
-struct KeyTarget {
- bool direct;
- union {
- u32 direct_target_hex;
- IndirectTarget indirect_target;
- } target;
-
- KeyTarget() : direct(true) {
- target.direct_target_hex = 0;
- }
-
- KeyTarget(Service::HID::PadState pad) : direct(true) {
- target.direct_target_hex = pad.hex;
- }
-
- KeyTarget(IndirectTarget i) : direct(false) {
- target.indirect_target = i;
- }
-};
-
-/**
- * Represents a key for a specific host device.
- */
-struct HostDeviceKey {
- int key_code;
- int device_id; ///< Uniquely identifies a host device
-
- bool operator<(const HostDeviceKey& other) const {
- return std::tie(key_code, device_id) < std::tie(other.key_code, other.device_id);
- }
-
- bool operator==(const HostDeviceKey& other) const {
- return std::tie(key_code, device_id) == std::tie(other.key_code, other.device_id);
- }
-};
-
-extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
-
-/**
- * Generates a new device id, which uniquely identifies a host device within KeyMap.
- */
-int NewDeviceId();
-
-/**
- * Maps a device-specific key to a target (a PadState or an IndirectTarget).
- */
-void SetKeyMapping(HostDeviceKey key, KeyTarget target);
-
-/**
- * Clears all key mappings belonging to one device.
- */
-void ClearKeyMapping(int device_id);
-
-/**
- * Maps a key press action and call the corresponding function in EmuWindow
- */
-void PressKey(EmuWindow& emu_window, HostDeviceKey key);
-
-/**
- * Maps a key release action and call the corresponding function in EmuWindow
- */
-void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
-}
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() {
func(((s64)PARAM(1) << 32) | PARAM(0));
}
-template <void func(const char*)>
+template <void func(const char*, int len)>
void Wrap() {
- func((char*)Memory::GetPointer(PARAM(0)));
+ func((char*)Memory::GetPointer(PARAM(0)), PARAM(1));
}
template <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 @@
namespace Kernel {
-static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
+/// Offset into command buffer of header
+static const int kCommandHeaderOffset = 0x80;
/**
* Returns a pointer to the command buffer in the current thread's TLS
@@ -26,8 +27,8 @@ inline u32* GetCommandBuffer(const int offset = 0) {
offset);
}
-static const int kStaticBuffersOffset =
- 0x100; ///< Offset into static buffers, relative to command buffer header
+/// Offset into static buffers, relative to command buffer header
+static const int kStaticBuffersOffset = 0x100;
/**
* 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:
DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
header.raw);
}
+
+ void Skip(unsigned size_in_words, bool set_to_null) {
+ if (set_to_null)
+ memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
+ index += size_in_words;
+ }
+
+ /**
+ * @brief Retrieves the address of a static buffer, used when a buffer is needed for output
+ * @param buffer_id The index of the static buffer
+ * @param data_size If non-null, will store the size of the buffer
+ */
+ VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const {
+ u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2;
+ if (data_size)
+ *data_size = StaticBufferDescInfo{static_buffer[0]}.size;
+ return static_buffer[1];
+ }
};
class RequestBuilder : public RequestHelperBase {
@@ -50,14 +68,8 @@ public:
template <typename T>
void Push(T value);
- void Push(u32 value) {
- cmdbuf[index++] = value;
- }
template <typename First, typename... Other>
- void Push(const First& first_value, const Other&... other_values) {
- Push(first_value);
- Push(other_values...);
- }
+ void Push(const First& first_value, const Other&... other_values);
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
@@ -65,59 +77,96 @@ public:
* @note: The input class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
- void PushRaw(const T& value) {
- static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
- std::memcpy(cmdbuf + index, &value, sizeof(T));
- index += (sizeof(T) + 3) / 4; // round up to word length
- }
+ void PushRaw(const T& value);
// TODO : ensure that translate params are added after all regular params
template <typename... H>
- void PushCopyHandles(H... handles) {
- Push(CopyHandleDesc(sizeof...(H)));
- Push(static_cast<Kernel::Handle>(handles)...);
- }
+ void PushCopyHandles(H... handles);
template <typename... H>
- void PushMoveHandles(H... handles) {
- Push(MoveHandleDesc(sizeof...(H)));
- Push(static_cast<Kernel::Handle>(handles)...);
- }
+ void PushMoveHandles(H... handles);
- void PushCurrentPIDHandle() {
- Push(CallingPidDesc());
- Push(u32(0));
- }
+ void PushCurrentPIDHandle();
- void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) {
- Push(StaticBufferDesc(size, buffer_id));
- Push(buffer_vaddr);
- }
+ void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id);
- void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms) {
- Push(MappedBufferDesc(size, perms));
- Push(buffer_vaddr);
- }
+ void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms);
};
/// Push ///
template <>
-inline void RequestBuilder::Push<u32>(u32 value) {
- Push(value);
+inline void RequestBuilder::Push(u32 value) {
+ cmdbuf[index++] = value;
+}
+
+template <typename T>
+void RequestBuilder::PushRaw(const T& value) {
+ static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
+ std::memcpy(cmdbuf + index, &value, sizeof(T));
+ index += (sizeof(T) + 3) / 4; // round up to word length
}
template <>
-inline void RequestBuilder::Push<u64>(u64 value) {
+inline void RequestBuilder::Push(u8 value) {
+ PushRaw(value);
+}
+
+template <>
+inline void RequestBuilder::Push(u16 value) {
+ PushRaw(value);
+}
+
+template <>
+inline void RequestBuilder::Push(u64 value) {
Push(static_cast<u32>(value));
Push(static_cast<u32>(value >> 32));
}
template <>
-inline void RequestBuilder::Push<ResultCode>(ResultCode value) {
+inline void RequestBuilder::Push(bool value) {
+ Push(static_cast<u8>(value));
+}
+
+template <>
+inline void RequestBuilder::Push(ResultCode value) {
Push(value.raw);
}
+template <typename First, typename... Other>
+void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
+ Push(first_value);
+ Push(other_values...);
+}
+
+template <typename... H>
+inline void RequestBuilder::PushCopyHandles(H... handles) {
+ Push(CopyHandleDesc(sizeof...(H)));
+ Push(static_cast<Kernel::Handle>(handles)...);
+}
+
+template <typename... H>
+inline void RequestBuilder::PushMoveHandles(H... handles) {
+ Push(MoveHandleDesc(sizeof...(H)));
+ Push(static_cast<Kernel::Handle>(handles)...);
+}
+
+inline void RequestBuilder::PushCurrentPIDHandle() {
+ Push(CallingPidDesc());
+ Push(u32(0));
+}
+
+inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) {
+ Push(StaticBufferDesc(size, buffer_id));
+ Push(buffer_vaddr);
+}
+
+inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size,
+ MappedBufferPermissions perms) {
+ Push(MappedBufferDesc(size, perms));
+ Push(buffer_vaddr);
+}
+
class RequestParser : public RequestHelperBase {
public:
RequestParser(u32* command_buffer, Header command_header)
@@ -185,24 +234,60 @@ public:
*/
template <typename T>
void PopRaw(T& value);
+
+ /**
+ * @brief Reads the next normal parameters as a struct, by copying it into a new value
+ * @note: The output class must be correctly packed/padded to fit hardware layout.
+ */
+ template <typename T>
+ T PopRaw();
};
/// Pop ///
template <>
-inline u32 RequestParser::Pop<u32>() {
+inline u32 RequestParser::Pop() {
return cmdbuf[index++];
}
+template <typename T>
+void RequestParser::PopRaw(T& value) {
+ static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
+ std::memcpy(&value, cmdbuf + index, sizeof(T));
+ index += (sizeof(T) + 3) / 4; // round up to word length
+}
+
+template <typename T>
+T RequestParser::PopRaw() {
+ T value;
+ PopRaw(value);
+ return value;
+}
+
+template <>
+inline u8 RequestParser::Pop() {
+ return PopRaw<u8>();
+}
+
template <>
-inline u64 RequestParser::Pop<u64>() {
+inline u16 RequestParser::Pop() {
+ return PopRaw<u16>();
+}
+
+template <>
+inline u64 RequestParser::Pop() {
const u64 lsw = Pop<u32>();
const u64 msw = Pop<u32>();
return msw << 32 | lsw;
}
template <>
-inline ResultCode RequestParser::Pop<ResultCode>() {
+inline bool RequestParser::Pop() {
+ return Pop<u8>() != 0;
+}
+
+template <>
+inline ResultCode RequestParser::Pop() {
return ResultCode{Pop<u32>()};
}
@@ -265,11 +350,4 @@ inline VAddr RequestParser::PopMappedBuffer(size_t* data_size,
return Pop<VAddr>();
}
-template <typename T>
-void RequestParser::PopRaw(T& value) {
- static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
- std::memcpy(&value, cmdbuf + index, sizeof(T));
- index += (sizeof(T) + 3) / 4; // round up to word length
-}
-
} // 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) {
}
void Initialize(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- u32 flags = cmd_buff[2];
-
- cmd_buff[2] = IPC::CopyHandleDesc(2);
- cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom();
- cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
+ u32 app_id = rp.Pop<u32>();
+ u32 flags = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(),
+ Kernel::g_handle_table.Create(parameter_event).MoveFrom());
// TODO(bunnei): Check if these events are cleared every time Initialize is called.
notification_event->Clear();
@@ -64,18 +64,16 @@ void Initialize(Service::Interface* self) {
ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
lock->Release();
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags);
}
void GetSharedFont(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
if (!shared_font_mem) {
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
- cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
- cmd_buff[1] = -1; // TODO: Find the right error code
+ rb.Push<u32>(-1); // TODO: Find the right error code
+ rb.Skip(1 + 2, true);
return;
}
@@ -87,103 +85,110 @@ void GetSharedFont(Service::Interface* self) {
BCFNT::RelocateSharedFont(shared_font_mem, target_address);
shared_font_relocated = true;
}
- cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ rb.Push(RESULT_SUCCESS); // No error
// Since the SharedMemory interface doesn't provide the address at which the memory was
// allocated, the real APT service calculates this address by scanning the entire address space
// (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font.
- cmd_buff[2] = target_address;
- cmd_buff[3] = IPC::CopyHandleDesc();
- cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
+ rb.Push(target_address);
+ rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom());
}
void NotifyToWait(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x43, 1, 0); // 0x430040
+ u32 app_id = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
}
void GetLockHandle(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0); // 0x10040
+
// Bits [0:2] are the applet type (System, Library, etc)
// Bit 5 tells the application that there's a pending APT parameter,
// this will cause the app to wait until parameter_event is signaled.
- u32 applet_attributes = cmd_buff[1];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
- cmd_buff[2] = applet_attributes; // Applet Attributes, this value is passed to Enable.
- cmd_buff[3] = 0; // Least significant bit = power button state
- cmd_buff[4] = IPC::CopyHandleDesc();
- cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom();
-
- LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", cmd_buff[5],
+ u32 applet_attributes = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
+ rb.Push<u32>(0); // Least significant bit = power button state
+ Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom();
+ rb.PushCopyHandles(handle_copy);
+
+ LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy,
applet_attributes);
}
void Enable(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 attributes = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- parameter_event->Signal(); // Let the application know that it has been started
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
+ u32 attributes = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ parameter_event->Signal(); // Let the application know that it has been started
LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes);
}
void GetAppletManInfo(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0;
- cmd_buff[3] = 0;
- cmd_buff[4] = static_cast<u32>(AppletId::HomeMenu); // Home menu AppID
- cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0); // 0x50040
+ u32 unk = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ rb.Push(static_cast<u32>(AppletId::HomeMenu)); // Home menu AppID
+ rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly
LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
void IsRegistered(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
+ u32 app_id = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS); // No error
// TODO(Subv): An application is considered "registered" if it has already called APT::Enable
// handle this properly once we implement multiprocess support.
- cmd_buff[2] = 0; // Set to not registered by default
+ bool is_registered = false; // Set to not registered by default
if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) {
- cmd_buff[2] = HLE::Applets::IsLibraryAppletRunning() ? 1 : 0;
+ is_registered = HLE::Applets::IsLibraryAppletRunning();
} else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) {
- cmd_buff[2] = 1; // Set to registered
+ is_registered = true; // Set to registered
}
+ rb.Push(is_registered);
+
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
void InquireNotification(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0); // 0xB0040
+ u32 app_id = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(static_cast<u32>(SignalType::None)); // Signal type
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
void SendParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 src_app_id = cmd_buff[1];
- u32 dst_app_id = cmd_buff[2];
- u32 signal_type = cmd_buff[3];
- u32 buffer_size = cmd_buff[4];
- u32 value = cmd_buff[5];
- u32 handle = cmd_buff[6];
- u32 size = cmd_buff[7];
- u32 buffer = cmd_buff[8];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104
+ u32 src_app_id = rp.Pop<u32>();
+ u32 dst_app_id = rp.Pop<u32>();
+ u32 signal_type = rp.Pop<u32>();
+ u32 buffer_size = rp.Pop<u32>();
+ Kernel::Handle handle = rp.PopHandle();
+ size_t size;
+ VAddr buffer = rp.PopStaticBuffer(&size);
std::shared_ptr<HLE::Applets::Applet> dest_applet =
HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id));
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+
if (dest_applet == nullptr) {
LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id);
- cmd_buff[1] = -1; // TODO(Subv): Find the right error code
+ rb.Push<u32>(-1); // TODO(Subv): Find the right error code
return;
}
@@ -195,88 +200,104 @@ void SendParameter(Service::Interface* self) {
param.buffer.resize(buffer_size);
Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
- cmd_buff[1] = dest_applet->ReceiveParameter(param).raw;
+ rb.Push(dest_applet->ReceiveParameter(param));
- LOG_WARNING(
- Service_APT,
- "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
- "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X",
- src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, buffer);
+ LOG_WARNING(Service_APT,
+ "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
+ "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
+ src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
}
void ReceiveParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- u32 buffer_size = cmd_buff[2];
- VAddr buffer = cmd_buff[0x104 >> 2];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = next_parameter.sender_id;
- cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
- cmd_buff[5] = 0x10;
- cmd_buff[6] = 0;
- if (next_parameter.object != nullptr)
- cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
- cmd_buff[8] = buffer;
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080
+ u32 app_id = rp.Pop<u32>();
+ u32 buffer_size = rp.Pop<u32>();
+
+ size_t static_buff_size;
+ VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
+ if (buffer_size > static_buff_size)
+ LOG_WARNING(
+ Service_APT,
+ "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
+ buffer_size, static_buff_size);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(next_parameter.sender_id);
+ rb.Push(next_parameter.signal); // Signal type
+ ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !");
+ rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
+
+ rb.PushMoveHandles((next_parameter.object != nullptr)
+ ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom()
+ : 0);
+ rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
- LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+ LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
}
void GlanceParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- u32 buffer_size = cmd_buff[2];
- VAddr buffer = cmd_buff[0x104 >> 2];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080
+ u32 app_id = rp.Pop<u32>();
+ u32 buffer_size = rp.Pop<u32>();
+
+ size_t static_buff_size;
+ VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
+ if (buffer_size > static_buff_size)
+ LOG_WARNING(
+ Service_APT,
+ "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
+ buffer_size, static_buff_size);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = next_parameter.sender_id;
- cmd_buff[3] = next_parameter.signal; // Signal type
- cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size
- cmd_buff[5] = 0x10;
- cmd_buff[6] = 0;
- if (next_parameter.object != nullptr)
- cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom();
- cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2;
- cmd_buff[8] = buffer;
+ IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(next_parameter.sender_id);
+ rb.Push(next_parameter.signal); // Signal type
+ ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !");
+ rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size
- Memory::WriteBlock(buffer, next_parameter.buffer.data(),
- std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size()));
+ rb.PushCopyHandles((next_parameter.object != nullptr)
+ ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom()
+ : 0);
+ rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0);
- LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+ Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size());
+
+ LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
}
void CancelParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 flag1 = cmd_buff[1];
- u32 unk = cmd_buff[2];
- u32 flag2 = cmd_buff[3];
- u32 app_id = cmd_buff[4];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 1; // Set to Success
+ u32 check_sender = rp.Pop<u32>();
+ u32 sender_appid = rp.Pop<u32>();
+ u32 check_receiver = rp.Pop<u32>();
+ u32 receiver_appid = rp.Pop<u32>();
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(true); // Set to Success
- LOG_WARNING(Service_APT,
- "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", flag1,
- unk, flag2, app_id);
+ LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, "
+ "check_receiver=0x%08X, receiver_appid=0x%08X",
+ check_sender, sender_appid, check_receiver, receiver_appid);
}
void PrepareToStartApplication(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 title_info1 = cmd_buff[1];
- u32 title_info2 = cmd_buff[2];
- u32 title_info3 = cmd_buff[3];
- u32 title_info4 = cmd_buff[4];
- u32 flags = cmd_buff[5];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 5, 0); // 0x00150140
+ u32 title_info1 = rp.Pop<u32>();
+ u32 title_info2 = rp.Pop<u32>();
+ u32 title_info3 = rp.Pop<u32>();
+ u32 title_info4 = rp.Pop<u32>();
+ u32 flags = rp.Pop<u32>();
if (flags & 0x00000100) {
unknown_ns_state_field = 1;
}
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT,
"(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X,"
@@ -285,172 +306,188 @@ void PrepareToStartApplication(Service::Interface* self) {
}
void StartApplication(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 buffer1_size = cmd_buff[1];
- u32 buffer2_size = cmd_buff[2];
- u32 flag = cmd_buff[3];
- u32 size1 = cmd_buff[4];
- u32 buffer1_ptr = cmd_buff[5];
- u32 size2 = cmd_buff[6];
- u32 buffer2_ptr = cmd_buff[7];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 3, 4); // 0x001B00C4
+ u32 buffer1_size = rp.Pop<u32>();
+ u32 buffer2_size = rp.Pop<u32>();
+ u32 flag = rp.Pop<u32>();
+ size_t size1;
+ VAddr buffer1_ptr = rp.PopStaticBuffer(&size1);
+ size_t size2;
+ VAddr buffer2_ptr = rp.PopStaticBuffer(&size2);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT,
"(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X,"
- "size1=0x%08X, buffer1_ptr=0x%08X, size2=0x%08X, buffer2_ptr=0x%08X",
+ "size1=0x%08zX, buffer1_ptr=0x%08X, size2=0x%08zX, buffer2_ptr=0x%08X",
buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr);
}
void AppletUtility(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4B, 3, 2); // 0x004B00C2
// These are from 3dbrew - I'm not really sure what they're used for.
- u32 command = cmd_buff[1];
- u32 buffer1_size = cmd_buff[2];
- u32 buffer2_size = cmd_buff[3];
- u32 buffer1_addr = cmd_buff[5];
- u32 buffer2_addr = cmd_buff[65];
+ u32 utility_command = rp.Pop<u32>();
+ u32 input_size = rp.Pop<u32>();
+ u32 output_size = rp.Pop<u32>();
+ VAddr input_addr = rp.PopStaticBuffer();
+
+ VAddr output_addr = rp.PeekStaticBuffer(0);
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT,
- "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, "
- "buffer1_addr=0x%08X, buffer2_addr=0x%08X",
- command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr);
+ "(STUBBED) called command=0x%08X, input_size=0x%08X, output_size=0x%08X, "
+ "input_addr=0x%08X, output_addr=0x%08X",
+ utility_command, input_size, output_size, input_addr, output_addr);
}
void SetAppCpuTimeLimit(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 value = cmd_buff[1];
- cpu_percent = cmd_buff[2];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4F, 2, 0); // 0x4F0080
+ u32 value = rp.Pop<u32>();
+ cpu_percent = rp.Pop<u32>();
if (value != 1) {
LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
}
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value);
}
void GetAppCpuTimeLimit(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 value = cmd_buff[1];
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x50, 1, 0); // 0x500040
+ u32 value = rp.Pop<u32>();
if (value != 1) {
LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
}
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = cpu_percent;
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(cpu_percent);
LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value);
}
void PrepareToStartLibraryApplet(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- AppletId applet_id = static_cast<AppletId>(cmd_buff[1]);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
+ AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
auto applet = HLE::Applets::Applet::Get(applet_id);
if (applet) {
LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
- cmd_buff[1] = RESULT_SUCCESS.raw;
+ rb.Push(RESULT_SUCCESS);
} else {
- cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw;
+ rb.Push(HLE::Applets::Applet::Create(applet_id));
}
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
}
void PreloadLibraryApplet(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- AppletId applet_id = static_cast<AppletId>(cmd_buff[1]);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
+ AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
auto applet = HLE::Applets::Applet::Get(applet_id);
if (applet) {
LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
- cmd_buff[1] = RESULT_SUCCESS.raw;
+ rb.Push(RESULT_SUCCESS);
} else {
- cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw;
+ rb.Push(HLE::Applets::Applet::Create(applet_id));
}
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
}
void StartLibraryApplet(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- AppletId applet_id = static_cast<AppletId>(cmd_buff[1]);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084
+ AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id);
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
if (applet == nullptr) {
LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id);
- cmd_buff[1] = -1; // TODO(Subv): Find the right error code
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false);
+ rb.Push<u32>(-1); // TODO(Subv): Find the right error code
return;
}
- size_t buffer_size = cmd_buff[2];
- VAddr buffer_addr = cmd_buff[6];
+ size_t buffer_size = rp.Pop<u32>();
+ Kernel::Handle handle = rp.PopHandle();
+ VAddr buffer_addr = rp.PopStaticBuffer();
AppletStartupParameter parameter;
- parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]);
+ parameter.object = Kernel::g_handle_table.GetGeneric(handle);
parameter.buffer.resize(buffer_size);
Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
- cmd_buff[1] = applet->Start(parameter).raw;
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(applet->Start(parameter));
}
void CancelLibraryApplet(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 exiting = cmd_buff[1] & 0xFF;
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3B, 1, 0); // 0x003B0040
+ bool exiting = rp.Pop<bool>();
- cmd_buff[1] = 1; // TODO: Find the return code meaning
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push<u32>(1); // TODO: Find the return code meaning
- LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting);
+ LOG_WARNING(Service_APT, "(STUBBED) called exiting=%d", exiting);
}
void SetScreenCapPostPermission(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x55, 1, 0); // 0x00550040
- screen_capture_post_permission = static_cast<ScreencapPostPermission>(cmd_buff[1] & 0xF);
+ screen_capture_post_permission = static_cast<ScreencapPostPermission>(rp.Pop<u32>() & 0xF);
- cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0);
- cmd_buff[1] = RESULT_SUCCESS.raw;
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
screen_capture_post_permission);
}
void GetScreenCapPostPermission(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x56, 0, 0); // 0x00560000
- cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0);
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = static_cast<u32>(screen_capture_post_permission);
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
+ rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(static_cast<u32>(screen_capture_post_permission));
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
screen_capture_post_permission);
}
void GetAppletInfo(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- auto app_id = static_cast<AppletId>(cmd_buff[1]);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 1, 0); // 0x60040
+ auto app_id = static_cast<AppletId>(rp.Pop<u32>());
if (auto applet = HLE::Applets::Applet::Get(app_id)) {
// TODO(Subv): Get the title id for the current applet and write it in the response[2-3]
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[4] = static_cast<u32>(Service::FS::MediaType::NAND);
- cmd_buff[5] = 1; // Registered
- cmd_buff[6] = 1; // Loaded
- cmd_buff[7] = 0; // Applet Attributes
+ IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
+ rb.Push(RESULT_SUCCESS);
+ u64 title_id = 0;
+ rb.Push(title_id);
+ rb.Push(static_cast<u32>(Service::FS::MediaType::NAND));
+ rb.Push(true); // Registered
+ rb.Push(true); // Loaded
+ rb.Push<u32>(0); // Applet Attributes
} else {
- cmd_buff[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
- ErrorSummary::NotFound, ErrorLevel::Status)
- .raw;
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
+ ErrorLevel::Status));
}
LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
}
void GetStartupArgument(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 parameter_size = cmd_buff[1];
- StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x51, 2, 0); // 0x00510080
+ u32 parameter_size = rp.Pop<u32>();
+ StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>());
if (parameter_size >= 0x300) {
LOG_ERROR(
@@ -460,7 +497,14 @@ void GetStartupArgument(Service::Interface* self) {
return;
}
- u32 addr = cmd_buff[65];
+ size_t static_buff_size;
+ VAddr addr = rp.PeekStaticBuffer(0, &static_buff_size);
+ if (parameter_size > static_buff_size)
+ LOG_WARNING(
+ Service_APT,
+ "parameter_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
+ parameter_size, static_buff_size);
+
if (addr && parameter_size) {
Memory::ZeroBlock(addr, parameter_size);
}
@@ -468,8 +512,10 @@ void GetStartupArgument(Service::Interface* self) {
LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x",
startup_argument_type, parameter_size);
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = 0;
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ rb.PushStaticBuffer(addr, parameter_size, 0);
}
void Wrap(Service::Interface* self) {
@@ -554,7 +600,7 @@ void Unwrap(Service::Interface* self) {
// Decrypts the ciphertext using AES-CCM
auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap);
- IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
if (!pdata.empty()) {
// Splits the plaintext and put the nonce in between
Memory::WriteBlock(output, pdata.data(), nonce_offset);
@@ -574,25 +620,25 @@ void Unwrap(Service::Interface* self) {
}
void CheckNew3DSApp(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x101, 0, 0); // 0x01010000
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
if (unknown_ns_state_field) {
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = 0;
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
} else {
- PTM::CheckNew3DS(self);
+ PTM::CheckNew3DS(rb);
}
- cmd_buff[0] = IPC::MakeHeader(0x101, 2, 0);
LOG_WARNING(Service_APT, "(STUBBED) called");
}
void CheckNew3DS(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x102, 0, 0); // 0x01020000
+ IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
- PTM::CheckNew3DS(self);
+ PTM::CheckNew3DS(rb);
- cmd_buff[0] = IPC::MakeHeader(0x102, 2, 0);
LOG_WARNING(Service_APT, "(STUBBED) called");
}
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 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <array>
+#include <cryptopp/sha.h>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -176,14 +178,29 @@ void SecureInfoGetRegion(Service::Interface* self) {
}
void GenHashConsoleUnique(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id_salt = cmd_buff[1];
-
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = 0x33646D6F ^ (app_id_salt & 0xFFFFF); // 3dmoo hash
- cmd_buff[3] = 0x6F534841 ^ (app_id_salt & 0xFFFFF);
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
+ const u32 app_id_salt = rp.Pop<u32>() & 0x000FFFFF;
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
+
+ std::array<u8, 12> buffer;
+ const ResultCode result = GetConfigInfoBlock(ConsoleUniqueID2BlockID, 8, 2, buffer.data());
+ rb.Push(result);
+ if (result.IsSuccess()) {
+ std::memcpy(&buffer[8], &app_id_salt, sizeof(u32));
+ std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash;
+ CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer));
+ u32 low, high;
+ memcpy(&low, &hash[hash.size() - 8], sizeof(u32));
+ memcpy(&high, &hash[hash.size() - 4], sizeof(u32));
+ rb.Push(low);
+ rb.Push(high);
+ } else {
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ }
- LOG_WARNING(Service_CFG, "(STUBBED) called app_id_salt=0x%X", app_id_salt);
+ LOG_DEBUG(Service_CFG, "called app_id_salt=0x%X", app_id_salt);
}
void 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
+#include <atomic>
#include <cmath>
+#include <memory>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/hid.h"
@@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
+static std::atomic<bool> is_device_reload_pending;
+static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ buttons;
+static std::unique_ptr<Input::AnalogDevice> circle_pad;
+
static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
// 30 degree and 60 degree are angular thresholds for directions
constexpr float TAN30 = 0.577350269f;
@@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
return state;
}
+static void LoadInputDevices() {
+ std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+ Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
+ buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ circle_pad = Input::CreateDevice<Input::AnalogDevice>(
+ Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
+}
+
+static void UnloadInputDevices() {
+ for (auto& button : buttons) {
+ button.reset();
+ }
+ circle_pad.reset();
+}
+
static void UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
- PadState state = VideoCore::g_emu_window->GetPadState();
+ if (is_device_reload_pending.exchange(false))
+ LoadInputDevices();
+
+ PadState state;
+ using namespace Settings::NativeButton;
+ state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+ state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+ state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+ state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+ state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus());
+ state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus());
+ state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus());
+ state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus());
+ state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+ state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+ state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus());
+ state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus());
// Get current circle pad position and update circle pad direction
- s16 circle_pad_x, circle_pad_y;
- std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState();
+ float circle_pad_x_f, circle_pad_y_f;
+ std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus();
+ constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
+ s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS);
+ s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS);
state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
mem->pad.current_state.hex = state.hex;
@@ -313,6 +356,8 @@ void Init() {
AddService(new HID_U_Interface);
AddService(new HID_SPVR_Interface);
+ is_device_reload_pending.store(true);
+
using Kernel::MemoryPermission;
shared_mem =
SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read,
@@ -350,6 +395,11 @@ void Shutdown() {
event_accelerometer = nullptr;
event_gyroscope = nullptr;
event_debug_pad = nullptr;
+ UnloadInputDevices();
+}
+
+void ReloadInputDevices() {
+ is_device_reload_pending.store(true);
}
} // 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 {
BitField<10, 1, u32> x;
BitField<11, 1, u32> y;
- BitField<14, 1, u32> zl;
- BitField<15, 1, u32> zr;
-
- BitField<24, 1, u32> c_right;
- BitField<25, 1, u32> c_left;
- BitField<26, 1, u32> c_up;
- BitField<27, 1, u32> c_down;
BitField<28, 1, u32> circle_right;
BitField<29, 1, u32> circle_left;
BitField<30, 1, u32> circle_up;
@@ -183,33 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A);
#undef ASSERT_REG_POSITION
#endif // !defined(_MSC_VER)
-// Pre-defined PadStates for single button presses
-const PadState PAD_NONE = {{0}};
-const PadState PAD_A = {{1u << 0}};
-const PadState PAD_B = {{1u << 1}};
-const PadState PAD_SELECT = {{1u << 2}};
-const PadState PAD_START = {{1u << 3}};
-const PadState PAD_RIGHT = {{1u << 4}};
-const PadState PAD_LEFT = {{1u << 5}};
-const PadState PAD_UP = {{1u << 6}};
-const PadState PAD_DOWN = {{1u << 7}};
-const PadState PAD_R = {{1u << 8}};
-const PadState PAD_L = {{1u << 9}};
-const PadState PAD_X = {{1u << 10}};
-const PadState PAD_Y = {{1u << 11}};
-
-const PadState PAD_ZL = {{1u << 14}};
-const PadState PAD_ZR = {{1u << 15}};
-
-const PadState PAD_C_RIGHT = {{1u << 24}};
-const PadState PAD_C_LEFT = {{1u << 25}};
-const PadState PAD_C_UP = {{1u << 26}};
-const PadState PAD_C_DOWN = {{1u << 27}};
-const PadState PAD_CIRCLE_RIGHT = {{1u << 28}};
-const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
-const PadState PAD_CIRCLE_UP = {{1u << 30}};
-const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
-
/**
* HID::GetIPCHandles service function
* Inputs:
@@ -297,5 +263,8 @@ void Init();
/// Shutdown HID service
void Shutdown();
+
+/// Reload input devices. Used when input configuration changed
+void ReloadInputDevices();
}
}
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) {
LOG_WARNING(Service_PTM, "(STUBBED) called");
}
-void CheckNew3DS(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
+void CheckNew3DS(IPC::RequestBuilder& rb) {
const bool is_new_3ds = Settings::values.is_new_3ds;
if (is_new_3ds) {
@@ -101,12 +100,17 @@ void CheckNew3DS(Service::Interface* self) {
"settings. Citra does not fully support New 3DS emulation yet!");
}
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = is_new_3ds ? 1 : 0;
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(is_new_3ds);
LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds));
}
+void CheckNew3DS(Service::Interface* self) {
+ IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x40A, 0, 0); // 0x040A0000
+ CheckNew3DS(rb);
+}
+
void Init() {
AddService(new PTM_Gets);
AddService(new PTM_Play);
@@ -134,9 +138,9 @@ void Init() {
ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
FileSys::Path gamecoin_path("/gamecoin.dat");
+ Service::FS::CreateFileInArchive(*archive_result, gamecoin_path, sizeof(GameCoin));
FileSys::Mode open_mode = {};
open_mode.write_flag.Assign(1);
- open_mode.create_flag.Assign(1);
// Open the file and write the default gamecoin information
auto gamecoin_result =
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 @@
#pragma once
#include "common/common_types.h"
+#include "core/hle/ipc_helpers.h"
namespace Service {
@@ -97,6 +98,7 @@ void GetSoftwareClosedFlag(Interface* self);
* 2: u8 output: 0 = Old3DS, 1 = New3DS.
*/
void CheckNew3DS(Interface* self);
+void CheckNew3DS(IPC::RequestBuilder& rb);
/// Initialize the PTM service
void 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) {
return;
}
- u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol));
+ u32 ret = static_cast<u32>(::socket(domain, type, protocol));
- if ((s32)socket_handle != SOCKET_ERROR_VALUE)
- open_sockets[socket_handle] = {socket_handle, true};
+ if ((s32)ret != SOCKET_ERROR_VALUE)
+ open_sockets[ret] = {ret, true};
int result = 0;
- if ((s32)socket_handle == SOCKET_ERROR_VALUE)
- result = TranslateError(GET_ERRNO);
+ if ((s32)ret == SOCKET_ERROR_VALUE)
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[0] = IPC::MakeHeader(2, 2, 0);
cmd_buffer[1] = result;
- cmd_buffer[2] = socket_handle;
+ cmd_buffer[2] = ret;
}
static void Bind(Interface* self) {
@@ -393,15 +393,15 @@ static void Bind(Interface* self) {
sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
- int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
+ int ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
int result = 0;
- if (res != 0)
- result = TranslateError(GET_ERRNO);
+ if (ret != 0)
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[0] = IPC::MakeHeader(5, 2, 0);
cmd_buffer[1] = result;
- cmd_buffer[2] = res;
+ cmd_buffer[2] = ret;
}
static void Fcntl(Interface* self) {
@@ -426,8 +426,7 @@ static void Fcntl(Interface* self) {
#else
int ret = ::fcntl(socket_handle, F_GETFL, 0);
if (ret == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
- posix_ret = -1;
+ posix_ret = TranslateError(GET_ERRNO);
return;
}
posix_ret = 0;
@@ -439,8 +438,7 @@ static void Fcntl(Interface* self) {
unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
if (ret == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
- posix_ret = -1;
+ posix_ret = TranslateError(GET_ERRNO);
return;
}
auto iter = open_sockets.find(socket_handle);
@@ -449,8 +447,7 @@ static void Fcntl(Interface* self) {
#else
int flags = ::fcntl(socket_handle, F_GETFL, 0);
if (flags == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
- posix_ret = -1;
+ posix_ret = TranslateError(GET_ERRNO);
return;
}
@@ -460,15 +457,13 @@ static void Fcntl(Interface* self) {
int ret = ::fcntl(socket_handle, F_SETFL, flags);
if (ret == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
- posix_ret = -1;
+ posix_ret = TranslateError(GET_ERRNO);
return;
}
#endif
} else {
LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd);
- result = TranslateError(EINVAL); // TODO: Find the correct error
- posix_ret = -1;
+ posix_ret = TranslateError(EINVAL); // TODO: Find the correct error
return;
}
}
@@ -481,7 +476,7 @@ static void Listen(Interface* self) {
int ret = ::listen(socket_handle, backlog);
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[0] = IPC::MakeHeader(3, 2, 0);
cmd_buffer[1] = result;
@@ -504,7 +499,7 @@ static void Accept(Interface* self) {
int result = 0;
if ((s32)ret == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
} else {
CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr));
@@ -545,7 +540,7 @@ static void Close(Interface* self) {
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
@@ -589,7 +584,7 @@ static void SendTo(Interface* self) {
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
@@ -638,7 +633,7 @@ static void RecvFrom(Interface* self) {
int result = 0;
int total_received = ret;
if (ret == SOCKET_ERROR_VALUE) {
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
total_received = 0;
} else {
// Write only the data we received to avoid overwriting parts of the buffer with zeros
@@ -673,7 +668,7 @@ static void Poll(Interface* self) {
std::vector<pollfd> platform_pollfd(nfds);
std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
- const int ret = ::poll(platform_pollfd.data(), nfds, timeout);
+ int ret = ::poll(platform_pollfd.data(), nfds, timeout);
// Now update the output pollfd structure
std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(),
@@ -683,7 +678,7 @@ static void Poll(Interface* self) {
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[1] = result;
cmd_buffer[2] = ret;
@@ -710,7 +705,7 @@ static void GetSockName(Interface* self) {
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
@@ -724,7 +719,7 @@ static void Shutdown(Interface* self) {
int ret = ::shutdown(socket_handle, how);
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
@@ -750,7 +745,7 @@ static void GetPeerName(Interface* self) {
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
@@ -777,7 +772,7 @@ static void Connect(Interface* self) {
int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
int result = 0;
if (ret != 0)
- result = TranslateError(GET_ERRNO);
+ ret = TranslateError(GET_ERRNO);
cmd_buffer[0] = IPC::MakeHeader(6, 2, 0);
cmd_buffer[1] = result;
@@ -815,7 +810,7 @@ static void GetSockOpt(Interface* self) {
int optname = TranslateSockOpt(cmd_buffer[3]);
socklen_t optlen = (socklen_t)cmd_buffer[4];
- int ret = -1;
+ int ret = 0;
int err = 0;
if (optname < 0) {
@@ -830,9 +825,8 @@ static void GetSockOpt(Interface* self) {
// >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
- ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
- err = 0;
- if (ret == SOCKET_ERROR_VALUE) {
+ err = ::getsockopt(socket_handle, level, optname, optval, &optlen);
+ if (err == SOCKET_ERROR_VALUE) {
err = TranslateError(GET_ERRNO);
}
}
@@ -849,7 +843,7 @@ static void SetSockOpt(Interface* self) {
u32 level = cmd_buffer[2];
int optname = TranslateSockOpt(cmd_buffer[3]);
- int ret = -1;
+ int ret = 0;
int err = 0;
if (optname < 0) {
@@ -862,9 +856,8 @@ static void SetSockOpt(Interface* self) {
socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
const char* optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8]));
- ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
- err = 0;
- if (ret == SOCKET_ERROR_VALUE) {
+ err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
+ if (err == SOCKET_ERROR_VALUE) {
err = TranslateError(GET_ERRNO);
}
}
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) {
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
static void GetSpacialDithering(Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0);
- cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = spacial_dithering_enabled;
+ IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0);
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(spacial_dithering_enabled != 0);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
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 @@
#include <cstring>
#include <ctime>
#include "core/core_timing.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/shared_page.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -73,6 +74,12 @@ void Init() {
// Some games wait until this value becomes 0x1, before asking running_hw
shared_page.unknown_value = 0x1;
+ // Set to a completely full battery
+ shared_page.battery_state.charge_level.Assign(
+ static_cast<u8>(Service::PTM::ChargeLevels::CompletelyFull));
+ shared_page.battery_state.is_adapter_connected.Assign(1);
+ shared_page.battery_state.is_charging.Assign(1);
+
update_time_event =
CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback);
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 @@
* write access, according to 3dbrew; this is not emulated)
*/
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -29,6 +30,13 @@ struct DateTime {
};
static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
+union BatteryState {
+ u8 raw;
+ BitField<0, 1, u8> is_adapter_connected;
+ BitField<1, 1, u8> is_charging;
+ BitField<2, 3, u8> charge_level;
+};
+
struct SharedPageDef {
// Most of these names are taken from the 3dbrew page linked above.
u32_le date_time_counter; // 0
@@ -44,7 +52,7 @@ struct SharedPageDef {
INSERT_PADDING_BYTES(0x80 - 0x68); // 68
float_le sliderstate_3d; // 80
u8 ledstate_3d; // 84
- INSERT_PADDING_BYTES(1); // 85
+ BatteryState battery_state; // 85
u8 unknown_value; // 86
INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
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) {
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-static void OutputDebugString(const char* string) {
- LOG_DEBUG(Debug_Emulated, "%s", string);
+static void OutputDebugString(const char* string, int len) {
+ LOG_DEBUG(Debug_Emulated, "%.*s", len, string);
}
/// Get resource limit
@@ -556,11 +556,21 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent
break;
}
- if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL ||
- (processor_id == THREADPROCESSORID_DEFAULT &&
- Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) {
- LOG_WARNING(Kernel_SVC,
- "Newly created thread is allowed to be run in the SysCore, unimplemented.");
+ if (processor_id == THREADPROCESSORID_ALL) {
+ LOG_INFO(Kernel_SVC,
+ "Newly created thread is allowed to be run in any Core, unimplemented.");
+ }
+
+ if (processor_id == THREADPROCESSORID_DEFAULT &&
+ Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1) {
+ LOG_WARNING(
+ Kernel_SVC,
+ "Newly created thread is allowed to be run in the SysCore (Core1), unimplemented.");
+ }
+
+ if (processor_id == THREADPROCESSORID_1) {
+ LOG_ERROR(Kernel_SVC,
+ "Newly created thread must run in the SysCore (Core1), unimplemented.");
}
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) {
type = filename_type;
}
- LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
+ LOG_DEBUG(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
return GetFileLoader(std::move(file), type, filename_filename, filename);
}
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 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <cinttypes>
#include <cstring>
#include <memory>
#include "common/logging/log.h"
@@ -253,7 +254,7 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
- LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
+ LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
ncch_offset = 0x4000;
file.Seek(ncch_offset, SEEK_SET);
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
@@ -277,8 +278,8 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
priority = exheader_header.arm11_system_local_caps.priority;
resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
- LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
- LOG_INFO(Loader, "Program ID: %016llX", ncch_header.program_id);
+ LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name);
+ LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
@@ -336,6 +337,8 @@ ResultStatus AppLoader_NCCH::Load() {
if (result != ResultStatus::Success)
return result;
+ LOG_INFO(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
+
is_loaded = true; // Set state to loaded
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 @@
#include "audio_core/audio_core.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/service/hid/hid.h"
#include "settings.h"
#include "video_core/video_core.h"
@@ -29,6 +30,8 @@ void Apply() {
AudioCore::SelectSink(values.sink_id);
AudioCore::EnableStretching(values.enable_audio_stretching);
+
+ Service::HID::ReloadInputDevices();
}
} // 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 {
Default,
SingleScreen,
LargeScreen,
- Custom,
};
-namespace NativeInput {
-
+namespace NativeButton {
enum Values {
- // directly mapped keys
A,
B,
X,
Y,
+ Up,
+ Down,
+ Left,
+ Right,
L,
R,
+ Start,
+ Select,
+
ZL,
ZR,
- START,
- SELECT,
- HOME,
- DUP,
- DDOWN,
- DLEFT,
- DRIGHT,
- CUP,
- CDOWN,
- CLEFT,
- CRIGHT,
-
- // indirectly mapped keys
- CIRCLE_UP,
- CIRCLE_DOWN,
- CIRCLE_LEFT,
- CIRCLE_RIGHT,
- CIRCLE_MODIFIER,
-
- NUM_INPUTS
+
+ Home,
+
+ NumButtons,
};
-static const std::array<const char*, NUM_INPUTS> Mapping = {{
- // directly mapped keys
- "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start",
- "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup",
- "pad_cdown", "pad_cleft", "pad_cright",
+constexpr int BUTTON_HID_BEGIN = A;
+constexpr int BUTTON_IR_BEGIN = ZL;
+constexpr int BUTTON_NS_BEGIN = Home;
+
+constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
+constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
+constexpr int BUTTON_NS_END = NumButtons;
+
+constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
+constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
+constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
- // indirectly mapped keys
- "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right",
- "pad_circle_modifier",
+static const std::array<const char*, NumButtons> mapping = {{
+ "button_a", "button_b", "button_x", "button_y", "button_up", "button_down", "button_left",
+ "button_right", "button_l", "button_r", "button_start", "button_select", "button_zl",
+ "button_zr", "button_home",
}};
-static const std::array<Values, NUM_INPUTS> All = {{
- A, B, X, Y, L, R, ZL, ZR,
- START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP,
- CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, CIRCLE_MODIFIER,
+} // namespace NativeButton
+
+namespace NativeAnalog {
+enum Values {
+ CirclePad,
+ CStick,
+
+ NumAnalogs,
+};
+
+static const std::array<const char*, NumAnalogs> mapping = {{
+ "circle_pad", "c_stick",
}};
-}
+} // namespace NumAnalog
struct Values {
// CheckNew3DS
bool is_new_3ds;
// Controls
- std::array<int, NativeInput::NUM_INPUTS> input_mappings;
- float pad_circle_modifier_scale;
+ std::array<std::string, NativeButton::NumButtons> buttons;
+ std::array<std::string, NativeAnalog::NumAnalogs> analogs;
// Core
bool use_cpu_jit;
@@ -95,6 +98,15 @@ struct Values {
LayoutOption layout_option;
bool swap_screen;
+ bool custom_layout;
+ u16 custom_top_left;
+ u16 custom_top_top;
+ u16 custom_top_right;
+ u16 custom_top_bottom;
+ u16 custom_bottom_left;
+ u16 custom_bottom_top;
+ u16 custom_bottom_right;
+ u16 custom_bottom_bottom;
float bg_red;
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 @@
+set(SRCS
+ analog_from_button.cpp
+ keyboard.cpp
+ main.cpp
+ )
+
+set(HEADERS
+ analog_from_button.h
+ keyboard.h
+ main.h
+ )
+
+if(SDL2_FOUND)
+ set(SRCS ${SRCS} sdl/sdl.cpp)
+ set(HEADERS ${HEADERS} sdl/sdl.h)
+ include_directories(${SDL2_INCLUDE_DIR})
+endif()
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(input_common STATIC ${SRCS} ${HEADERS})
+target_link_libraries(input_common common core)
+
+if(SDL2_FOUND)
+ target_link_libraries(input_common ${SDL2_LIBRARY})
+ set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2)
+endif()
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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "input_common/analog_from_button.h"
+
+namespace InputCommon {
+
+class Analog final : public Input::AnalogDevice {
+public:
+ using Button = std::unique_ptr<Input::ButtonDevice>;
+
+ Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
+ float modifier_scale_)
+ : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
+ right(std::move(right_)), modifier(std::move(modifier_)),
+ modifier_scale(modifier_scale_) {}
+
+ std::tuple<float, float> GetStatus() const override {
+ constexpr float SQRT_HALF = 0.707106781f;
+ int x = 0, y = 0;
+
+ if (right->GetStatus())
+ ++x;
+ if (left->GetStatus())
+ --x;
+ if (up->GetStatus())
+ ++y;
+ if (down->GetStatus())
+ --y;
+
+ float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
+ return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
+ y * coef * (x == 0 ? 1.0f : SQRT_HALF));
+ }
+
+private:
+ Button up;
+ Button down;
+ Button left;
+ Button right;
+ Button modifier;
+ float modifier_scale;
+};
+
+std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
+ const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
+ auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
+ auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
+ auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
+ auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
+ auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
+ auto modifier_scale = params.Get("modifier_scale", 0.5f);
+ return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
+ std::move(right), std::move(modifier), modifier_scale);
+}
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+/**
+ * An analog device factory that takes direction button devices and combines them into a analog
+ * device.
+ */
+class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> {
+public:
+ /**
+ * Creates an analog device from direction button devices
+ * @param params contains parameters for creating the device:
+ * - "up": a serialized ParamPackage for creating a button device for up direction
+ * - "down": a serialized ParamPackage for creating a button device for down direction
+ * - "left": a serialized ParamPackage for creating a button device for left direction
+ * - "right": a serialized ParamPackage for creating a button device for right direction
+ * - "modifier": a serialized ParamPackage for creating a button device as the modifier
+ * - "modifier_scale": a float for the multiplier the modifier gives to the position
+ */
+ std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
+};
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <atomic>
+#include <list>
+#include <mutex>
+#include "input_common/keyboard.h"
+
+namespace InputCommon {
+
+class KeyButton final : public Input::ButtonDevice {
+public:
+ explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
+ : key_button_list(key_button_list_) {}
+
+ ~KeyButton();
+
+ bool GetStatus() const override {
+ return status.load();
+ }
+
+ friend class KeyButtonList;
+
+private:
+ std::shared_ptr<KeyButtonList> key_button_list;
+ std::atomic<bool> status{false};
+};
+
+struct KeyButtonPair {
+ int key_code;
+ KeyButton* key_button;
+};
+
+class KeyButtonList {
+public:
+ void AddKeyButton(int key_code, KeyButton* key_button) {
+ std::lock_guard<std::mutex> guard(mutex);
+ list.push_back(KeyButtonPair{key_code, key_button});
+ }
+
+ void RemoveKeyButton(const KeyButton* key_button) {
+ std::lock_guard<std::mutex> guard(mutex);
+ list.remove_if(
+ [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
+ }
+
+ void ChangeKeyStatus(int key_code, bool pressed) {
+ std::lock_guard<std::mutex> guard(mutex);
+ for (const KeyButtonPair& pair : list) {
+ if (pair.key_code == key_code)
+ pair.key_button->status.store(pressed);
+ }
+ }
+
+ void ChangeAllKeyStatus(bool pressed) {
+ std::lock_guard<std::mutex> guard(mutex);
+ for (const KeyButtonPair& pair : list) {
+ pair.key_button->status.store(pressed);
+ }
+ }
+
+private:
+ std::mutex mutex;
+ std::list<KeyButtonPair> list;
+};
+
+Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
+
+KeyButton::~KeyButton() {
+ key_button_list->RemoveKeyButton(this);
+}
+
+std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
+ int key_code = params.Get("code", 0);
+ std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
+ key_button_list->AddKeyButton(key_code, button.get());
+ return std::move(button);
+}
+
+void Keyboard::PressKey(int key_code) {
+ key_button_list->ChangeKeyStatus(key_code, true);
+}
+
+void Keyboard::ReleaseKey(int key_code) {
+ key_button_list->ChangeKeyStatus(key_code, false);
+}
+
+void Keyboard::ReleaseAllKeys() {
+ key_button_list->ChangeAllKeyStatus(false);
+}
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+class KeyButtonList;
+
+/**
+ * A button device factory representing a keyboard. It receives keyboard events and forward them
+ * to all button devices it created.
+ */
+class Keyboard final : public Input::Factory<Input::ButtonDevice> {
+public:
+ Keyboard();
+
+ /**
+ * Creates a button device from a keyboard key
+ * @param params contains parameters for creating the device:
+ * - "code": the code of the key to bind with the button
+ */
+ std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
+
+ /**
+ * Sets the status of all buttons bound with the key to pressed
+ * @param key_code the code of the key to press
+ */
+ void PressKey(int key_code);
+
+ /**
+ * Sets the status of all buttons bound with the key to released
+ * @param key_code the code of the key to release
+ */
+ void ReleaseKey(int key_code);
+
+ void ReleaseAllKeys();
+
+private:
+ std::shared_ptr<KeyButtonList> key_button_list;
+};
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/param_package.h"
+#include "input_common/analog_from_button.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
+#ifdef HAVE_SDL2
+#include "input_common/sdl/sdl.h"
+#endif
+
+namespace InputCommon {
+
+static std::shared_ptr<Keyboard> keyboard;
+
+void Init() {
+ keyboard = std::make_shared<InputCommon::Keyboard>();
+ Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+ Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
+ std::make_shared<InputCommon::AnalogFromButton>());
+#ifdef HAVE_SDL2
+ SDL::Init();
+#endif
+}
+
+void Shutdown() {
+ Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
+ keyboard.reset();
+ Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
+
+#ifdef HAVE_SDL2
+ SDL::Shutdown();
+#endif
+}
+
+Keyboard* GetKeyboard() {
+ return keyboard.get();
+}
+
+std::string GenerateKeyboardParam(int key_code) {
+ Common::ParamPackage param{
+ {"engine", "keyboard"}, {"code", std::to_string(key_code)},
+ };
+ return param.Serialize();
+}
+
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+ int key_modifier, float modifier_scale) {
+ Common::ParamPackage circle_pad_param{
+ {"engine", "analog_from_button"},
+ {"up", GenerateKeyboardParam(key_up)},
+ {"down", GenerateKeyboardParam(key_down)},
+ {"left", GenerateKeyboardParam(key_left)},
+ {"right", GenerateKeyboardParam(key_right)},
+ {"modifier", GenerateKeyboardParam(key_modifier)},
+ {"modifier_scale", std::to_string(modifier_scale)},
+ };
+ return circle_pad_param.Serialize();
+}
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+namespace InputCommon {
+
+/// Initializes and registers all built-in input device factories.
+void Init();
+
+/// Unresisters all build-in input device factories and shut them down.
+void Shutdown();
+
+class Keyboard;
+
+/// Gets the keyboard button device factory.
+Keyboard* GetKeyboard();
+
+/// Generates a serialized param package for creating a keyboard button device
+std::string GenerateKeyboardParam(int key_code);
+
+/// Generates a serialized param package for creating an analog device taking input from keyboard
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+ int key_modifier, float modifier_scale);
+
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cmath>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <SDL.h>
+#include "common/math_util.h"
+#include "input_common/sdl/sdl.h"
+
+namespace InputCommon {
+
+namespace SDL {
+
+class SDLJoystick;
+class SDLButtonFactory;
+class SDLAnalogFactory;
+static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list;
+static std::shared_ptr<SDLButtonFactory> button_factory;
+static std::shared_ptr<SDLAnalogFactory> analog_factory;
+
+static bool initialized = false;
+
+class SDLJoystick {
+public:
+ explicit SDLJoystick(int joystick_index)
+ : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} {
+ if (!joystick) {
+ LOG_ERROR(Input, "failed to open joystick %d", joystick_index);
+ }
+ }
+
+ bool GetButton(int button) const {
+ if (!joystick)
+ return {};
+ SDL_JoystickUpdate();
+ return SDL_JoystickGetButton(joystick.get(), button) == 1;
+ }
+
+ std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
+ if (!joystick)
+ return {};
+ SDL_JoystickUpdate();
+ float x = SDL_JoystickGetAxis(joystick.get(), axis_x) / 32767.0f;
+ float y = SDL_JoystickGetAxis(joystick.get(), axis_y) / 32767.0f;
+ y = -y; // 3DS uses an y-axis inverse from SDL
+
+ // Make sure the coordinates are in the unit circle,
+ // otherwise normalize it.
+ float r = x * x + y * y;
+ if (r > 1.0f) {
+ r = std::sqrt(r);
+ x /= r;
+ y /= r;
+ }
+
+ return std::make_tuple(x, y);
+ }
+
+ bool GetHatDirection(int hat, Uint8 direction) const {
+ return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
+ }
+
+private:
+ std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
+};
+
+class SDLButton final : public Input::ButtonDevice {
+public:
+ explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
+ : joystick(joystick_), button(button_) {}
+
+ bool GetStatus() const override {
+ return joystick->GetButton(button);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int button;
+};
+
+class SDLDirectionButton final : public Input::ButtonDevice {
+public:
+ explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
+ : joystick(joystick_), hat(hat_), direction(direction_) {}
+
+ bool GetStatus() const override {
+ return joystick->GetHatDirection(hat, direction);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int hat;
+ Uint8 direction;
+};
+
+class SDLAnalog final : public Input::AnalogDevice {
+public:
+ SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
+ : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {}
+
+ std::tuple<float, float> GetStatus() const override {
+ return joystick->GetAnalog(axis_x, axis_y);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int axis_x;
+ int axis_y;
+};
+
+static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) {
+ std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock();
+ if (!joystick) {
+ joystick = std::make_shared<SDLJoystick>(joystick_index);
+ joystick_list[joystick_index] = joystick;
+ }
+ return joystick;
+}
+
+/// A button device factory that creates button devices from SDL joystick
+class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
+public:
+ /**
+ * Creates a button device from a joystick button
+ * @param params contains parameters for creating the device:
+ * - "joystick": the index of the joystick to bind
+ * - "button"(optional): the index of the button to bind
+ * - "hat"(optional): the index of the hat to bind as direction buttons
+ * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
+ * "down", "left" or "right"
+ */
+ std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
+ const int joystick_index = params.Get("joystick", 0);
+
+ if (params.Has("hat")) {
+ const int hat = params.Get("hat", 0);
+ const std::string direction_name = params.Get("direction", "");
+ Uint8 direction;
+ if (direction_name == "up") {
+ direction = SDL_HAT_UP;
+ } else if (direction_name == "down") {
+ direction = SDL_HAT_DOWN;
+ } else if (direction_name == "left") {
+ direction = SDL_HAT_LEFT;
+ } else if (direction_name == "right") {
+ direction = SDL_HAT_RIGHT;
+ } else {
+ direction = 0;
+ }
+ return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat,
+ direction);
+ }
+
+ const int button = params.Get("button", 0);
+ return std::make_unique<SDLButton>(GetJoystick(joystick_index), button);
+ }
+};
+
+/// An analog device factory that creates analog devices from SDL joystick
+class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
+public:
+ /**
+ * Creates analog device from joystick axes
+ * @param params contains parameters for creating the device:
+ * - "joystick": the index of the joystick to bind
+ * - "axis_x": the index of the axis to be bind as x-axis
+ * - "axis_y": the index of the axis to be bind as y-axis
+ */
+ std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
+ const int joystick_index = params.Get("joystick", 0);
+ const int axis_x = params.Get("axis_x", 0);
+ const int axis_y = params.Get("axis_y", 1);
+ return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y);
+ }
+};
+
+void Init() {
+ if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
+ LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: %s", SDL_GetError());
+ } else {
+ using namespace Input;
+ RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
+ RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
+ initialized = true;
+ }
+}
+
+void Shutdown() {
+ if (initialized) {
+ using namespace Input;
+ UnregisterFactory<ButtonDevice>("sdl");
+ UnregisterFactory<AnalogDevice>("sdl");
+ SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+ }
+}
+
+} // namespace SDL
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+namespace SDL {
+
+/// Initializes and registers SDL device factories
+void Init();
+
+/// Unresisters SDL device factories and shut them down.
+void Shutdown();
+
+} // namespace SDL
+} // 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 @@
set(SRCS
glad.cpp
tests.cpp
+ common/param_package.cpp
core/file_sys/path_parser.cpp
)
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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <catch.hpp>
+#include <math.h>
+#include "common/param_package.h"
+
+namespace Common {
+
+TEST_CASE("ParamPackage", "[common]") {
+ ParamPackage original{
+ {"abc", "xyz"}, {"def", "42"}, {"jkl", "$$:1:$2$,3"},
+ };
+ original.Set("ghi", 3.14f);
+ ParamPackage copy(original.Serialize());
+ REQUIRE(copy.Get("abc", "") == "xyz");
+ REQUIRE(copy.Get("def", 0) == 42);
+ REQUIRE(std::abs(copy.Get("ghi", 0.0f) - 3.14f) < 0.01f);
+ REQUIRE(copy.Get("jkl", "") == "$$:1:$2$,3");
+ REQUIRE(copy.Get("mno", "uvw") == "uvw");
+ REQUIRE(copy.Get("abc", 42) == 42);
+}
+
+} // 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 {
};
union {
- BitField<0, 8, BlendEquation> blend_equation_rgb;
- BitField<8, 8, BlendEquation> blend_equation_a;
+ BitField<0, 3, BlendEquation> blend_equation_rgb;
+ BitField<8, 3, BlendEquation> blend_equation_a;
BitField<16, 4, BlendFactor> factor_source_rgb;
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 @@
#include "common/vector_math.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
+#include "core/settings.h"
#include "video_core/pica_state.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"