diff --git a/Data/Sys/GameSettings/RBK.ini b/Data/Sys/GameSettings/RBK.ini new file mode 100644 index 0000000000..3fb30e2eb9 --- /dev/null +++ b/Data/Sys/GameSettings/RBK.ini @@ -0,0 +1,26 @@ +# RBKJ13, RBKK69, RBKP69, RBKE69 - Boom Blox + +[Core] +# Values set here will override the main Dolphin settings. + +[OnLoad] +# Add memory patches to be loaded once on boot here. + +[OnFrame] +# Add memory patches to be applied every frame here. + +[ActionReplay] +# Add action replay cheats here. + +[Video_Settings] +# Add any video settings here + +[Wiimote.Shake] +Soft = 3.0 +Medium = 4.0 +Hard = 4.8 + +[Wiimote.Swing] +Slow = 3.5 +Medium = 4.8 +Fast = 6 \ No newline at end of file diff --git a/Data/Sys/GameSettings/RYB.ini b/Data/Sys/GameSettings/RYB.ini new file mode 100644 index 0000000000..ad69b55675 --- /dev/null +++ b/Data/Sys/GameSettings/RYB.ini @@ -0,0 +1,26 @@ +# RYBP69, RYBE69 - Boom Blox Bash Party + +[Core] +# Values set here will override the main Dolphin settings. + +[OnLoad] +# Add memory patches to be loaded once on boot here. + +[OnFrame] +# Add memory patches to be applied every frame here. + +[ActionReplay] +# Add action replay cheats here. + +[Video_Settings] +# Add any video settings here + +[Wiimote.Shake] +Soft = 3.0 +Medium = 4.0 +Hard = 4.8 + +[Wiimote.Swing] +Slow = 3.5 +Medium = 4.8 +Fast = 6 \ No newline at end of file diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 0fecda7e8b..f5d65c0588 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(core Config/NetplaySettings.cpp Config/SYSCONFSettings.cpp Config/UISettings.cpp + Config/WiimoteInputSettings.cpp ConfigLoaders/BaseConfigLoader.cpp ConfigLoaders/GameConfigLoader.cpp ConfigLoaders/IsSettingSaveable.cpp diff --git a/Source/Core/Core/Config/WiimoteInputSettings.cpp b/Source/Core/Core/Config/WiimoteInputSettings.cpp new file mode 100644 index 0000000000..db8ff55597 --- /dev/null +++ b/Source/Core/Core/Config/WiimoteInputSettings.cpp @@ -0,0 +1,53 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/Config/WiimoteInputSettings.h" + +namespace Config +{ +// Configuration Information + +// WiimoteInput.Settings + +const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_FAST{{System::WiiPad, "Swing", "Fast"}, 4.5}; +const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM{{System::WiiPad, "Swing", "Medium"}, + 2.5}; +const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_SLOW{{System::WiiPad, "Swing", "Slow"}, 1.5}; + +const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_HARD{{System::WiiPad, "Shake", "Hard"}, 5.0}; +const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM{{System::WiiPad, "Shake", "Medium"}, + 3.0}; +const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT{{System::WiiPad, "Shake", "Soft"}, 2.0}; + +// Dynamic settings +const ConfigInfo WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST{ + {System::WiiPad, "Dynamic_Swing", "FramesHeldFast"}, 100}; +const ConfigInfo WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW{ + {System::WiiPad, "Dynamic_Swing", "FramesHeldSlow"}, 30}; +const ConfigInfo WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH{ + {System::WiiPad, "Dynamic_Swing", "FrameCount"}, 30}; + +const ConfigInfo WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD{ + {System::WiiPad, "Dynamic_Shake", "FramesHeldHard"}, 45}; +const ConfigInfo WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT{ + {System::WiiPad, "Dynamic_Shake", "FramesHeldSoft"}, 15}; +const ConfigInfo WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH{ + {System::WiiPad, "Dynamic_Shake", "FrameCount"}, 30}; + +// NunchuckInput.Settings + +const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_FAST{ + {System::WiiPad, "Nunchuk_Swing", "Fast"}, 4.5}; +const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_MEDIUM{ + {System::WiiPad, "Nunchuk_Swing", "Medium"}, 2.5}; +const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_SLOW{ + {System::WiiPad, "Nunchuk_Swing", "Slow"}, 1.5}; + +const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_HARD{ + {System::WiiPad, "Nunchuk_Shake", "Hard"}, 5.0}; +const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM{ + {System::WiiPad, "Nunchuk_Shake", "Medium"}, 3.0}; +const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT{ + {System::WiiPad, "Nunchuk_Shake", "Soft"}, 2.0}; +} diff --git a/Source/Core/Core/Config/WiimoteInputSettings.h b/Source/Core/Core/Config/WiimoteInputSettings.h new file mode 100644 index 0000000000..de7ffc0241 --- /dev/null +++ b/Source/Core/Core/Config/WiimoteInputSettings.h @@ -0,0 +1,49 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Config/Config.h" + +namespace Config +{ +// Configuration Information + +// WiimoteInput.Settings + +extern const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_FAST; +extern const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM; +extern const ConfigInfo WIIMOTE_INPUT_SWING_INTENSITY_SLOW; + +extern const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_HARD; +extern const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM; +extern const ConfigInfo WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT; + +// Below settings are for dynamic input only (based on how long the user holds a button) + +extern const ConfigInfo + WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST; // How long button held constitutes a fast swing +extern const ConfigInfo + WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW; // How long button held constitutes a slow swing +extern const ConfigInfo + WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH; // How long to execute the swing + +extern const ConfigInfo + WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD; // How long button held constitutes a hard shake +extern const ConfigInfo + WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT; // How long button held constitutes a soft shake +extern const ConfigInfo + WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH; // How long to execute a shake + +// NunchuckInput.Settings + +extern const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_FAST; +extern const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_MEDIUM; +extern const ConfigInfo NUNCHUK_INPUT_SWING_INTENSITY_SLOW; + +extern const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_HARD; +extern const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM; +extern const ConfigInfo NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT; + +} // namespace Config diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 9039da4443..cb9d82584b 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -56,6 +56,7 @@ + @@ -321,6 +322,7 @@ + @@ -600,4 +602,4 @@ - + \ No newline at end of file diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index b9a979df78..b60f775010 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -910,6 +910,9 @@ Config + + Config + @@ -1604,6 +1607,9 @@ Config + + Config + diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp b/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp index 95d61417d2..b2a6f04ccb 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.cpp @@ -11,6 +11,7 @@ #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Common/MathUtil.h" +#include "Core/Config/WiimoteInputSettings.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "InputCommon/ControllerEmu/Control/Input.h" @@ -42,6 +43,8 @@ Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg) // swing groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); + groups.emplace_back(m_swing_slow = new ControllerEmu::Force("SwingSlow")); + groups.emplace_back(m_swing_fast = new ControllerEmu::Force("SwingFast")); // tilt groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); @@ -55,6 +58,16 @@ Nunchuk::Nunchuk(ExtensionReg& reg) : Attachment(_trans("Nunchuk"), reg) // i18n: Refers to a 3D axis (used when mapping motion controls) m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z"))); + groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); + + groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); + m_id = nunchuk_id; } @@ -91,9 +104,18 @@ void Nunchuk::GetState(u8* const data) EmulateTilt(&accel, m_tilt); // swing - EmulateSwing(&accel, m_swing); + EmulateSwing(&accel, m_swing, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_MEDIUM)); + EmulateSwing(&accel, m_swing_slow, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_SLOW)); + EmulateSwing(&accel, m_swing_fast, Config::Get(Config::NUNCHUK_INPUT_SWING_INTENSITY_FAST)); + // shake - EmulateShake(&accel, m_shake, m_shake_step.data()); + EmulateShake(&accel, m_shake, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_MEDIUM), + m_shake_step.data()); + EmulateShake(&accel, m_shake_soft, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_SOFT), + m_shake_soft_step.data()); + EmulateShake(&accel, m_shake_hard, Config::Get(Config::NUNCHUK_INPUT_SHAKE_INTENSITY_HARD), + m_shake_hard_step.data()); + // buttons m_buttons->GetState(&nc_data.bt.hex, nunchuk_button_bitmasks.data()); diff --git a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h b/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h index 48ec942672..8a8f5d5d08 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h +++ b/Source/Core/Core/HW/WiimoteEmu/Attachment/Nunchuk.h @@ -55,12 +55,18 @@ public: private: ControllerEmu::Tilt* m_tilt; ControllerEmu::Force* m_swing; + ControllerEmu::Force* m_swing_slow; + ControllerEmu::Force* m_swing_fast; ControllerEmu::Buttons* m_shake; + ControllerEmu::Buttons* m_shake_soft; + ControllerEmu::Buttons* m_shake_hard; ControllerEmu::Buttons* m_buttons; ControllerEmu::AnalogStick* m_stick; std::array m_shake_step{}; + std::array m_shake_soft_step{}; + std::array m_shake_hard_step{}; }; } diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 5cfd4ed083..3c2db51204 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -17,6 +17,7 @@ #include "Common/MsgHandler.h" #include "Core/Config/SYSCONFSettings.h" +#include "Core/Config/WiimoteInputSettings.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HW/Wiimote.h" @@ -99,15 +100,12 @@ static const ReportFeatures reporting_mode_features[] = { }; void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_group, - u8* const shake_step) + const double intensity, u8* const shake_step) { // frame count of one up/down shake // < 9 no shake detection in "Wario Land: Shake It" auto const shake_step_max = 15; - // peak G-force - auto const shake_intensity = 3.0; - // shake is a bitfield of X,Y,Z shake button states static const unsigned int btns[] = {0x01, 0x02, 0x04}; unsigned int shake = 0; @@ -117,7 +115,7 @@ void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_ { if (shake & (1 << i)) { - (&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * shake_intensity; + (&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * intensity; shake_step[i] = (shake_step[i] + 1) % shake_step_max; } else @@ -125,6 +123,55 @@ void EmulateShake(AccelData* const accel, ControllerEmu::Buttons* const buttons_ } } +void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Buttons* const buttons_group, + const DynamicConfiguration& config, u8* const shake_step) +{ + // frame count of one up/down shake + // < 9 no shake detection in "Wario Land: Shake It" + auto const shake_step_max = 15; + + // shake is a bitfield of X,Y,Z shake button states + static const unsigned int btns[] = {0x01, 0x02, 0x04}; + unsigned int shake = 0; + buttons_group->GetState(&shake, btns); + + for (int i = 0; i != 3; ++i) + { + if ((shake & (1 << i)) && dynamic_data.executing_frames_left[i] == 0) + { + dynamic_data.timing[i]++; + } + else if (dynamic_data.executing_frames_left[i] > 0) + { + (&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * dynamic_data.intensity[i]; + shake_step[i] = (shake_step[i] + 1) % shake_step_max; + dynamic_data.executing_frames_left[i]--; + } + else if (shake == 0 && dynamic_data.timing[i] > 0) + { + if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) + { + dynamic_data.intensity[i] = config.high_intensity; + } + else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) + { + dynamic_data.intensity[i] = config.low_intensity; + } + else + { + dynamic_data.intensity[i] = config.med_intensity; + } + dynamic_data.timing[i] = 0; + dynamic_data.executing_frames_left[i] = config.frames_to_execute; + } + else + { + shake_step[i] = 0; + } + } +} + void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, const bool sideways, const bool upright) { @@ -160,10 +207,8 @@ void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, (&accel->x)[fb] = sin(pitch) * sgn[fb]; } -#define SWING_INTENSITY 2.5 //-uncalibrated(aprox) 0x40-calibrated - void EmulateSwing(AccelData* const accel, ControllerEmu::Force* const swing_group, - const bool sideways, const bool upright) + const double intensity, const bool sideways, const bool upright) { ControlState swing[3]; swing_group->GetState(swing); @@ -184,7 +229,61 @@ void EmulateSwing(AccelData* const accel, ControllerEmu::Force* const swing_grou g_dir[axis_map[0]] *= -1; for (unsigned int i = 0; i < 3; ++i) - (&accel->x)[axis_map[i]] += swing[i] * g_dir[i] * SWING_INTENSITY; + (&accel->x)[axis_map[i]] += swing[i] * g_dir[i] * intensity; +} + +void EmulateDynamicSwing(AccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Force* const swing_group, + const DynamicConfiguration& config, const bool sideways, + const bool upright) +{ + ControlState swing[3]; + swing_group->GetState(swing); + + s8 g_dir[3] = {-1, -1, -1}; + u8 axis_map[3]; + + // determine which axis is which direction + axis_map[0] = upright ? (sideways ? 0 : 1) : 2; // up/down + axis_map[1] = sideways; // left|right + axis_map[2] = upright ? 2 : (sideways ? 0 : 1); // forward/backward + + // some orientations have up as positive, some as negative + // same with forward + if (sideways && !upright) + g_dir[axis_map[2]] *= -1; + if (!sideways && upright) + g_dir[axis_map[0]] *= -1; + + for (unsigned int i = 0; i < 3; ++i) + { + if (swing[i] > 0 && dynamic_data.executing_frames_left[i] == 0) + { + dynamic_data.timing[i]++; + } + else if (dynamic_data.executing_frames_left[i] > 0) + { + (&accel->x)[axis_map[i]] += g_dir[i] * dynamic_data.intensity[i]; + dynamic_data.executing_frames_left[i]--; + } + else if (swing[i] == 0 && dynamic_data.timing[i] > 0) + { + if (dynamic_data.timing[i] > config.frames_needed_for_high_intensity) + { + dynamic_data.intensity[i] = config.high_intensity; + } + else if (dynamic_data.timing[i] < config.frames_needed_for_low_intensity) + { + dynamic_data.intensity[i] = config.low_intensity; + } + else + { + dynamic_data.intensity[i] = config.med_intensity; + } + dynamic_data.timing[i] = 0; + dynamic_data.executing_frames_left[i] = config.frames_to_execute; + } + } } static const u16 button_bitmasks[] = { @@ -238,7 +337,11 @@ void Wiimote::Reset() // 0x55 - 0xff: level 4 m_status.battery = (u8)(m_battery_setting->GetValue() * 100); - memset(m_shake_step, 0, sizeof(m_shake_step)); + m_shake_step = {}; + m_shake_soft_step = {}; + m_shake_hard_step = {}; + m_swing_dynamic_data = {}; + m_shake_dynamic_data = {}; // clear read request queue while (!m_read_requests.empty()) @@ -271,6 +374,9 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 // swing groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); + groups.emplace_back(m_swing_slow = new ControllerEmu::Force("SwingSlow")); + groups.emplace_back(m_swing_fast = new ControllerEmu::Force("SwingFast")); + groups.emplace_back(m_swing_dynamic = new ControllerEmu::Force("Swing Dynamic")); // tilt groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); @@ -284,6 +390,24 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1 // i18n: Refers to a 3D axis (used when mapping motion controls) m_shake->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::Translate, _trans("Z"))); + groups.emplace_back(m_shake_soft = new ControllerEmu::Buttons("ShakeSoft")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); + m_shake_soft->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); + + groups.emplace_back(m_shake_hard = new ControllerEmu::Buttons("ShakeHard")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); + m_shake_hard->controls.emplace_back(new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); + + groups.emplace_back(m_shake_dynamic = new ControllerEmu::Buttons("Shake Dynamic")); + m_shake_dynamic->controls.emplace_back( + new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "X")); + m_shake_dynamic->controls.emplace_back( + new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Y")); + m_shake_dynamic->controls.emplace_back( + new ControllerEmu::Input(ControllerEmu::DoNotTranslate, "Z")); + // extension groups.emplace_back(m_extension = new ControllerEmu::Extension(_trans("Extension"))); m_extension->attachments.emplace_back(new WiimoteEmu::None(m_reg_ext)); @@ -484,8 +608,44 @@ void Wiimote::GetAccelData(u8* const data, const ReportFeatures& rptf) m_upright_setting->GetValue() ^ upright_modifier_toggle ^ upright_modifier_switch; EmulateTilt(&m_accel, m_tilt, is_sideways, is_upright); - EmulateSwing(&m_accel, m_swing, is_sideways, is_upright); - EmulateShake(&m_accel, m_shake, m_shake_step); + + DynamicConfiguration swing_config; + swing_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW); + swing_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM); + swing_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST); + swing_config.frames_needed_for_high_intensity = + Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_FAST); + swing_config.frames_needed_for_low_intensity = + Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_HELD_SLOW); + swing_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SWING_DYNAMIC_FRAMES_LENGTH); + + EmulateSwing(&m_accel, m_swing, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_MEDIUM), + is_sideways, is_upright); + EmulateSwing(&m_accel, m_swing_slow, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_SLOW), + is_sideways, is_upright); + EmulateSwing(&m_accel, m_swing_fast, Config::Get(Config::WIIMOTE_INPUT_SWING_INTENSITY_FAST), + is_sideways, is_upright); + EmulateDynamicSwing(&m_accel, m_swing_dynamic_data, m_swing_dynamic, swing_config, is_sideways, + is_upright); + + DynamicConfiguration shake_config; + shake_config.low_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT); + shake_config.med_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM); + shake_config.high_intensity = Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD); + shake_config.frames_needed_for_high_intensity = + Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_HARD); + shake_config.frames_needed_for_low_intensity = + Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_HELD_SOFT); + shake_config.frames_to_execute = Config::Get(Config::WIIMOTE_INPUT_SHAKE_DYNAMIC_FRAMES_LENGTH); + + EmulateShake(&m_accel, m_shake, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_MEDIUM), + m_shake_step.data()); + EmulateShake(&m_accel, m_shake_soft, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_SOFT), + m_shake_soft_step.data()); + EmulateShake(&m_accel, m_shake_hard, Config::Get(Config::WIIMOTE_INPUT_SHAKE_INTENSITY_HARD), + m_shake_hard_step.data()); + EmulateDynamicShake(&m_accel, m_shake_dynamic_data, m_shake_dynamic, shake_config, + m_shake_dynamic_step.data()); wm_accel& accel = *reinterpret_cast(data + rptf.accel); wm_buttons& core = *reinterpret_cast(data + rptf.core); @@ -562,8 +722,8 @@ void Wiimote::GetIRData(u8* const data, bool use_accel) static const int camHeight = 768; static const double bndup = -0.315447; static const double bnddown = 0.85; - static const double bndleft = 0.443364; - static const double bndright = -0.443364; + static const double bndleft = 0.78820266; + static const double bndright = -0.78820266; static const double dist1 = 100.0 / camWidth; // this seems the optimal distance for zelda static const double dist2 = 1.2 * dist1; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 812b7988de..f190220502 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -123,6 +124,33 @@ struct AccelData double x, y, z; }; +// Used for a dynamic swing or +// shake +struct DynamicData +{ + std::array timing; // Hold length in frames for each axis + std::array intensity; // Swing or shake intensity + std::array executing_frames_left; // Number of frames to execute the intensity operation +}; + +// Used for a dynamic swing or +// shake. This is used to pass +// in data that defines the dynamic +// action +struct DynamicConfiguration +{ + double low_intensity; + int frames_needed_for_low_intensity; + + double med_intensity; + // Frames needed for med intensity can be calculated between high & low + + double high_intensity; + int frames_needed_for_high_intensity; + + int frames_to_execute; // How many frames should we execute the action for? +}; + struct ADPCMState { s32 predictor, step; @@ -154,13 +182,22 @@ struct ExtensionReg #pragma pack(pop) void EmulateShake(AccelData* const accel_data, ControllerEmu::Buttons* const buttons_group, - u8* const shake_step); + const double intensity, u8* const shake_step); + +void EmulateDynamicShake(AccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Buttons* const buttons_group, + const DynamicConfiguration& config, u8* const shake_step); void EmulateTilt(AccelData* const accel, ControllerEmu::Tilt* const tilt_group, const bool sideways = false, const bool upright = false); void EmulateSwing(AccelData* const accel, ControllerEmu::Force* const tilt_group, - const bool sideways = false, const bool upright = false); + const double intensity, const bool sideways = false, const bool upright = false); + +void EmulateDynamicSwing(AccelData* const accel, DynamicData& dynamic_data, + ControllerEmu::Force* const swing_group, + const DynamicConfiguration& config, const bool sideways = false, + const bool upright = false); enum { @@ -247,9 +284,15 @@ private: ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_dpad; ControllerEmu::Buttons* m_shake; + ControllerEmu::Buttons* m_shake_soft; + ControllerEmu::Buttons* m_shake_hard; + ControllerEmu::Buttons* m_shake_dynamic; ControllerEmu::Cursor* m_ir; ControllerEmu::Tilt* m_tilt; ControllerEmu::Force* m_swing; + ControllerEmu::Force* m_swing_slow; + ControllerEmu::Force* m_swing_fast; + ControllerEmu::Force* m_swing_dynamic; ControllerEmu::ControlGroup* m_rumble; ControllerEmu::Output* m_motor; ControllerEmu::Extension* m_extension; @@ -259,6 +302,9 @@ private: ControllerEmu::NumericSetting* m_battery_setting; ControllerEmu::ModifySettingsButton* m_hotkeys; + DynamicData m_swing_dynamic_data; + DynamicData m_shake_dynamic_data; + // Wiimote accel data AccelData m_accel; @@ -274,7 +320,10 @@ private: u8 m_reporting_mode; u16 m_reporting_channel; - u8 m_shake_step[3]; + std::array m_shake_step{}; + std::array m_shake_soft_step{}; + std::array m_shake_hard_step{}; + std::array m_shake_dynamic_step{}; bool m_sensor_bar_on_top; diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp index 80b293f940..5905ea0ad1 100644 --- a/Source/Core/Core/HotkeyManager.cpp +++ b/Source/Core/Core/HotkeyManager.cpp @@ -20,7 +20,7 @@ #include "InputCommon/GCPadStatus.h" // clang-format off -constexpr std::array s_hotkey_labels{{ +constexpr std::array s_hotkey_labels{{ _trans("Open"), _trans("Change Disc"), _trans("Eject Disc"), @@ -69,6 +69,23 @@ constexpr std::array s_hotkey_labels{{ _trans("Connect Wii Remote 4"), _trans("Connect Balance Board"), + _trans("Next Profile for Wii Remote 1"), + _trans("Previous Profile for Wii Remote 1"), + _trans("Next Game Profile for Wii Remote 1"), + _trans("Previous Game Profile for Wii Remote 1"), + _trans("Next Profile for Wii Remote 2"), + _trans("Previous Profile for Wii Remote 2"), + _trans("Next Game Profile for Wii Remote 2"), + _trans("Previous Game Profile for Wii Remote 2"), + _trans("Next Profile for Wii Remote 3"), + _trans("Previous Profile for Wii Remote 3"), + _trans("Next Game Profile for Wii Remote 3"), + _trans("Previous Game Profile for Wii Remote 3"), + _trans("Next Profile for Wii Remote 4"), + _trans("Previous Profile for Wii Remote 4"), + _trans("Next Game Profile for Wii Remote 4"), + _trans("Previous Game Profile for Wii Remote 4"), + _trans("Toggle Crop"), _trans("Toggle Aspect Ratio"), _trans("Toggle EFB Copies"), @@ -255,6 +272,7 @@ constexpr std::array s_groups_info = { {_trans("Program Counter"), HK_SHOW_PC, HK_SET_PC}, {_trans("Breakpoint"), HK_BP_TOGGLE, HK_MBP_ADD}, {_trans("Wii"), HK_TRIGGER_SYNC_BUTTON, HK_BALANCEBOARD_CONNECT}, + {_trans("Controller Profile"), HK_NEXT_WIIMOTE_PROFILE_1, HK_PREV_GAME_WIIMOTE_PROFILE_4}, {_trans("Graphics Toggles"), HK_TOGGLE_CROP, HK_TOGGLE_TEXTURES}, {_trans("Internal Resolution"), HK_INCREASE_IR, HK_DECREASE_IR}, {_trans("Freelook"), HK_FREELOOK_DECREASE_SPEED, HK_FREELOOK_RESET}, diff --git a/Source/Core/Core/HotkeyManager.h b/Source/Core/Core/HotkeyManager.h index 8505412d16..7b72478a4d 100644 --- a/Source/Core/Core/HotkeyManager.h +++ b/Source/Core/Core/HotkeyManager.h @@ -67,6 +67,23 @@ enum Hotkey HK_WIIMOTE4_CONNECT, HK_BALANCEBOARD_CONNECT, + HK_NEXT_WIIMOTE_PROFILE_1, + HK_PREV_WIIMOTE_PROFILE_1, + HK_NEXT_GAME_WIIMOTE_PROFILE_1, + HK_PREV_GAME_WIIMOTE_PROFILE_1, + HK_NEXT_WIIMOTE_PROFILE_2, + HK_PREV_WIIMOTE_PROFILE_2, + HK_NEXT_GAME_WIIMOTE_PROFILE_2, + HK_PREV_GAME_WIIMOTE_PROFILE_2, + HK_NEXT_WIIMOTE_PROFILE_3, + HK_PREV_WIIMOTE_PROFILE_3, + HK_NEXT_GAME_WIIMOTE_PROFILE_3, + HK_PREV_GAME_WIIMOTE_PROFILE_3, + HK_NEXT_WIIMOTE_PROFILE_4, + HK_PREV_WIIMOTE_PROFILE_4, + HK_NEXT_GAME_WIIMOTE_PROFILE_4, + HK_PREV_GAME_WIIMOTE_PROFILE_4, + HK_TOGGLE_CROP, HK_TOGGLE_AR, HK_TOGGLE_EFBCOPIES, @@ -166,6 +183,7 @@ enum HotkeyGroup : int HKGP_PC, HKGP_BREAKPOINT, HKGP_WII, + HKGP_CONTROLLER_PROFILE, HKGP_GRAPHICS_TOGGLES, HKGP_IR, HKGP_FREELOOK, diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 46f0880092..19a2320ebe 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -50,6 +50,7 @@ add_executable(dolphin-emu Config/Mapping/GCPadEmu.cpp Config/Mapping/GCPadWiiUConfigDialog.cpp Config/Mapping/Hotkey3D.cpp + Config/Mapping/HotkeyControllerProfile.cpp Config/Mapping/HotkeyDebugging.cpp Config/Mapping/HotkeyGeneral.cpp Config/Mapping/HotkeyGraphics.cpp diff --git a/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.cpp b/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.cpp new file mode 100644 index 0000000000..044b10485d --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.cpp @@ -0,0 +1,40 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/Mapping/HotkeyControllerProfile.h" + +#include +#include + +#include "Core/HotkeyManager.h" + +HotkeyControllerProfile::HotkeyControllerProfile(MappingWindow* window) : MappingWidget(window) +{ + CreateMainLayout(); +} + +void HotkeyControllerProfile::CreateMainLayout() +{ + m_main_layout = new QHBoxLayout(); + + m_main_layout->addWidget(CreateGroupBox( + tr("Controller Profile"), HotkeyManagerEmu::GetHotkeyGroup(HKGP_CONTROLLER_PROFILE))); + + setLayout(m_main_layout); +} + +InputConfig* HotkeyControllerProfile::GetConfig() +{ + return HotkeyManagerEmu::GetConfig(); +} + +void HotkeyControllerProfile::LoadSettings() +{ + HotkeyManagerEmu::LoadConfig(); +} + +void HotkeyControllerProfile::SaveSettings() +{ + HotkeyManagerEmu::GetConfig()->SaveConfig(); +} diff --git a/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.h b/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.h new file mode 100644 index 0000000000..6884275b63 --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/HotkeyControllerProfile.h @@ -0,0 +1,26 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "DolphinQt/Config/Mapping/MappingWidget.h" + +class QHBoxLayout; + +class HotkeyControllerProfile final : public MappingWidget +{ + Q_OBJECT +public: + explicit HotkeyControllerProfile(MappingWindow* window); + + InputConfig* GetConfig() override; + +private: + void LoadSettings() override; + void SaveSettings() override; + void CreateMainLayout(); + + // Main + QHBoxLayout* m_main_layout; +}; diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index 1b6f629c1c..f6de39be6c 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -25,6 +25,7 @@ #include "DolphinQt/Config/Mapping/GCMicrophone.h" #include "DolphinQt/Config/Mapping/GCPadEmu.h" #include "DolphinQt/Config/Mapping/Hotkey3D.h" +#include "DolphinQt/Config/Mapping/HotkeyControllerProfile.h" #include "DolphinQt/Config/Mapping/HotkeyDebugging.h" #include "DolphinQt/Config/Mapping/HotkeyGeneral.h" #include "DolphinQt/Config/Mapping/HotkeyGraphics.h" @@ -316,6 +317,7 @@ void MappingWindow::SetMappingType(MappingWindow::Type type) AddWidget(tr("Debugging"), new HotkeyDebugging(this)); AddWidget(tr("Wii and Wii Remote"), new HotkeyWii(this)); + AddWidget(tr("Controller Profile"), new HotkeyControllerProfile(this)); AddWidget(tr("Graphics"), new HotkeyGraphics(this)); AddWidget(tr("3D"), new Hotkey3D(this)); AddWidget(tr("Save and Load State"), new HotkeyStates(this)); diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index a1ddc418e9..b2aa6ae409 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -70,6 +70,7 @@ + @@ -204,6 +205,7 @@ + @@ -285,6 +287,7 @@ + diff --git a/Source/Core/DolphinQt/HotkeyScheduler.cpp b/Source/Core/DolphinQt/HotkeyScheduler.cpp index 4853fd4517..8def220bad 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.cpp +++ b/Source/Core/DolphinQt/HotkeyScheduler.cpp @@ -240,6 +240,46 @@ void HotkeyScheduler::Run() emit ConnectWiiRemote(wiimote_id); } + if (IsHotkey(HK_PREV_WIIMOTE_PROFILE_1)) + m_profile_cycler.PreviousWiimoteProfile(0); + else if (IsHotkey(HK_NEXT_WIIMOTE_PROFILE_1)) + m_profile_cycler.NextWiimoteProfile(0); + + if (IsHotkey(HK_PREV_WIIMOTE_PROFILE_2)) + m_profile_cycler.PreviousWiimoteProfile(1); + else if (IsHotkey(HK_NEXT_WIIMOTE_PROFILE_2)) + m_profile_cycler.NextWiimoteProfile(1); + + if (IsHotkey(HK_PREV_WIIMOTE_PROFILE_3)) + m_profile_cycler.PreviousWiimoteProfile(2); + else if (IsHotkey(HK_NEXT_WIIMOTE_PROFILE_3)) + m_profile_cycler.NextWiimoteProfile(2); + + if (IsHotkey(HK_PREV_WIIMOTE_PROFILE_4)) + m_profile_cycler.PreviousWiimoteProfile(3); + else if (IsHotkey(HK_NEXT_WIIMOTE_PROFILE_4)) + m_profile_cycler.NextWiimoteProfile(3); + + if (IsHotkey(HK_PREV_GAME_WIIMOTE_PROFILE_1)) + m_profile_cycler.PreviousWiimoteProfileForGame(0); + else if (IsHotkey(HK_NEXT_GAME_WIIMOTE_PROFILE_1)) + m_profile_cycler.NextWiimoteProfileForGame(0); + + if (IsHotkey(HK_PREV_GAME_WIIMOTE_PROFILE_2)) + m_profile_cycler.PreviousWiimoteProfileForGame(1); + else if (IsHotkey(HK_NEXT_GAME_WIIMOTE_PROFILE_2)) + m_profile_cycler.NextWiimoteProfileForGame(1); + + if (IsHotkey(HK_PREV_GAME_WIIMOTE_PROFILE_3)) + m_profile_cycler.PreviousWiimoteProfileForGame(2); + else if (IsHotkey(HK_NEXT_GAME_WIIMOTE_PROFILE_3)) + m_profile_cycler.NextWiimoteProfileForGame(2); + + if (IsHotkey(HK_PREV_GAME_WIIMOTE_PROFILE_4)) + m_profile_cycler.PreviousWiimoteProfileForGame(3); + else if (IsHotkey(HK_NEXT_GAME_WIIMOTE_PROFILE_4)) + m_profile_cycler.NextWiimoteProfileForGame(3); + const auto show_msg = [](OSDMessage message) { if (g_renderer) g_renderer->ShowOSDMessage(message); diff --git a/Source/Core/DolphinQt/HotkeyScheduler.h b/Source/Core/DolphinQt/HotkeyScheduler.h index 673f6fcab7..998bda3a02 100644 --- a/Source/Core/DolphinQt/HotkeyScheduler.h +++ b/Source/Core/DolphinQt/HotkeyScheduler.h @@ -9,6 +9,7 @@ #include #include "Common/Flag.h" +#include "InputCommon/InputProfile.h" class HotkeyScheduler : public QObject { @@ -64,4 +65,6 @@ private: Common::Flag m_stop_requested; std::thread m_thread; + + InputProfile::ProfileCycler m_profile_cycler; }; diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 99cf2f9679..a3ef2722d8 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(inputcommon InputConfig.cpp + InputProfile.cpp ControllerEmu/ControllerEmu.cpp ControllerEmu/Control/Control.cpp ControllerEmu/Control/Input.cpp diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.cpp index 46dbd63c49..0c27e30298 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Force.cpp @@ -40,10 +40,7 @@ void Force::GetState(ControlState* axis) controls[i + 1]->control_ref->State() - controls[i]->control_ref->State(); if (fabs(state) > deadzone) tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone)); - - ControlState& ax = m_swing[i >> 1]; - *axis++ = (tmpf - ax); - ax = tmpf; + *axis++ = tmpf; } } } // namespace ControllerEmu diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index 84ad406de1..6d0efb2c91 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -71,6 +71,7 @@ 4200;%(DisableSpecificWarnings) + @@ -105,6 +106,7 @@ + diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index 600a77727d..c67333497c 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -110,6 +110,7 @@ ControllerInterface + @@ -202,6 +203,7 @@ ControllerInterface + diff --git a/Source/Core/InputCommon/InputConfig.cpp b/Source/Core/InputCommon/InputConfig.cpp index 2a5766fbbe..ac74c0ea7e 100644 --- a/Source/Core/InputCommon/InputConfig.cpp +++ b/Source/Core/InputCommon/InputConfig.cpp @@ -7,12 +7,15 @@ #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/MsgHandler.h" +#include "Common/StringUtil.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "Core/HW/Wiimote.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/InputConfig.h" +#include "InputCommon/InputProfile.h" InputConfig::InputConfig(const std::string& ini_name, const std::string& gui_name, const std::string& profile_name) @@ -51,17 +54,24 @@ bool InputConfig::LoadConfig(bool isGC) { if (control_section->Exists(type + "Profile" + num[i])) { - if (control_section->Get(type + "Profile" + num[i], &profile[i])) + std::string profile_setting; + if (control_section->Get(type + "Profile" + num[i], &profile_setting)) { - if (File::Exists(File::GetUserPath(D_CONFIG_IDX) + path + profile[i] + ".ini")) - { - useProfile[i] = true; - } - else + auto profiles = InputProfile::GetProfilesFromSetting( + profile_setting, File::GetUserPath(D_CONFIG_IDX) + path); + + if (profiles.empty()) { + const std::string error = + "No profiles found for game setting '" + profile_setting + "'"; // TODO: PanicAlert shouldn't be used for this. - PanicAlertT("Selected controller profile does not exist"); + PanicAlertT("%s", error.c_str()); + continue; } + + // Use the first profile by default + profile[i] = profiles[0]; + useProfile[i] = true; } } } @@ -75,8 +85,14 @@ bool InputConfig::LoadConfig(bool isGC) // Load settings from ini if (useProfile[n]) { + std::string base; + SplitPath(profile[n], nullptr, &base, nullptr); + Core::DisplayMessage("Loading game specific input profile '" + base + "' for device '" + + controller->GetName() + "'", + 6000); + IniFile profile_ini; - profile_ini.Load(File::GetUserPath(D_CONFIG_IDX) + path + profile[n] + ".ini"); + profile_ini.Load(profile[n]); controller->LoadConfig(profile_ini.GetOrCreateSection("Profile")); } else @@ -128,6 +144,11 @@ bool InputConfig::ControllersNeedToBeCreated() const return m_controllers.empty(); } +std::size_t InputConfig::GetControllerCount() const +{ + return m_controllers.size(); +} + bool InputConfig::IsControllerControlledByGamepadDevice(int index) const { if (static_cast(index) >= m_controllers.size()) diff --git a/Source/Core/InputCommon/InputConfig.h b/Source/Core/InputCommon/InputConfig.h index a993a412b1..004e4be8bb 100644 --- a/Source/Core/InputCommon/InputConfig.h +++ b/Source/Core/InputCommon/InputConfig.h @@ -38,6 +38,7 @@ public: std::string GetGUIName() const { return m_gui_name; } std::string GetProfileName() const { return m_profile_name; } + std::size_t GetControllerCount() const; private: std::vector> m_controllers; diff --git a/Source/Core/InputCommon/InputProfile.cpp b/Source/Core/InputCommon/InputProfile.cpp new file mode 100644 index 0000000000..c78c1fbfd6 --- /dev/null +++ b/Source/Core/InputCommon/InputProfile.cpp @@ -0,0 +1,210 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/FileSearch.h" +#include "Common/FileUtil.h" +#include "Common/StringUtil.h" + +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/HW/Wiimote.h" +#include "Core/HotkeyManager.h" + +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/InputConfig.h" +#include "InputCommon/InputProfile.h" + +#include +#include + +namespace InputProfile +{ +namespace +{ +constexpr int display_message_ms = 3000; +} + +std::vector GetProfilesFromSetting(const std::string& setting, const std::string& root) +{ + const auto& setting_choices = SplitString(setting, ','); + + std::vector result; + for (const std::string& setting_choice : setting_choices) + { + const std::string path = root + StripSpaces(setting_choice); + if (File::IsDirectory(path)) + { + const auto files_under_directory = Common::DoFileSearch({path}, {".ini"}, true); + result.insert(result.end(), files_under_directory.begin(), files_under_directory.end()); + } + else + { + const std::string file_path = path + ".ini"; + if (File::Exists(file_path)) + { + result.push_back(file_path); + } + } + } + + return result; +} + +std::vector ProfileCycler::GetProfilesForDevice(InputConfig* device_configuration) +{ + const std::string device_profile_root_location(File::GetUserPath(D_CONFIG_IDX) + "Profiles/" + + device_configuration->GetProfileName()); + return Common::DoFileSearch({device_profile_root_location}, {".ini"}, true); +} + +std::string ProfileCycler::GetProfile(CycleDirection cycle_direction, int& profile_index, + const std::vector& profiles) +{ + // update the index and bind it to the number of available strings + auto positive_modulo = [](int& i, int n) { i = (i % n + n) % n; }; + profile_index += static_cast(cycle_direction); + positive_modulo(profile_index, static_cast(profiles.size())); + + return profiles[profile_index]; +} + +void ProfileCycler::UpdateToProfile(const std::string& profile_filename, + ControllerEmu::EmulatedController* controller) +{ + std::string base; + SplitPath(profile_filename, nullptr, &base, nullptr); + + IniFile ini_file; + if (ini_file.Load(profile_filename)) + { + Core::DisplayMessage("Loading input profile '" + base + "' for device '" + + controller->GetName() + "'", + display_message_ms); + controller->LoadConfig(ini_file.GetOrCreateSection("Profile")); + controller->UpdateReferences(g_controller_interface); + } + else + { + Core::DisplayMessage("Unable to load input profile '" + base + "' for device '" + + controller->GetName() + "'", + display_message_ms); + } +} + +std::vector +ProfileCycler::GetMatchingProfilesFromSetting(const std::string& setting, + const std::vector& profiles, + InputConfig* device_configuration) +{ + const std::string device_profile_root_location(File::GetUserPath(D_CONFIG_IDX) + "Profiles/" + + device_configuration->GetProfileName() + "/"); + + const auto& profiles_from_setting = GetProfilesFromSetting(setting, device_profile_root_location); + if (profiles_from_setting.empty()) + { + return {}; + } + + std::vector result; + std::set_intersection(profiles.begin(), profiles.end(), profiles_from_setting.begin(), + profiles_from_setting.end(), std::back_inserter(result)); + return result; +} + +void ProfileCycler::CycleProfile(CycleDirection cycle_direction, InputConfig* device_configuration, + int& profile_index, int controller_index) +{ + const auto& profiles = GetProfilesForDevice(device_configuration); + if (profiles.empty()) + { + Core::DisplayMessage("No input profiles found", display_message_ms); + return; + } + const std::string profile = GetProfile(cycle_direction, profile_index, profiles); + + auto* controller = device_configuration->GetController(controller_index); + if (controller) + { + UpdateToProfile(profile, controller); + } + else + { + Core::DisplayMessage("No controller found for index: " + std::to_string(controller_index), + display_message_ms); + } +} + +void ProfileCycler::CycleProfileForGame(CycleDirection cycle_direction, + InputConfig* device_configuration, int& profile_index, + const std::string& setting, int controller_index) +{ + const auto& profiles = GetProfilesForDevice(device_configuration); + if (profiles.empty()) + { + Core::DisplayMessage("No input profiles found", display_message_ms); + return; + } + + if (setting.empty()) + { + Core::DisplayMessage("No setting found for game", display_message_ms); + return; + } + + const auto& profiles_for_game = + GetMatchingProfilesFromSetting(setting, profiles, device_configuration); + if (profiles_for_game.empty()) + { + Core::DisplayMessage("No input profiles found for game", display_message_ms); + return; + } + + const std::string profile = GetProfile(cycle_direction, profile_index, profiles_for_game); + + auto* controller = device_configuration->GetController(controller_index); + if (controller) + { + UpdateToProfile(profile, controller); + } + else + { + Core::DisplayMessage("No controller found for index: " + std::to_string(controller_index), + display_message_ms); + } +} + +std::string ProfileCycler::GetWiimoteInputProfilesForGame(int controller_index) +{ + IniFile game_ini = SConfig::GetInstance().LoadGameIni(); + const IniFile::Section* const control_section = game_ini.GetOrCreateSection("Controls"); + + std::string result; + control_section->Get(StringFromFormat("WiimoteProfile%d", controller_index + 1), &result); + return result; +} + +void ProfileCycler::NextWiimoteProfile(int controller_index) +{ + CycleProfile(CycleDirection::Forward, Wiimote::GetConfig(), m_wiimote_profile_index, + controller_index); +} + +void ProfileCycler::PreviousWiimoteProfile(int controller_index) +{ + CycleProfile(CycleDirection::Backward, Wiimote::GetConfig(), m_wiimote_profile_index, + controller_index); +} + +void ProfileCycler::NextWiimoteProfileForGame(int controller_index) +{ + CycleProfileForGame(CycleDirection::Forward, Wiimote::GetConfig(), m_wiimote_profile_index, + GetWiimoteInputProfilesForGame(controller_index), controller_index); +} + +void ProfileCycler::PreviousWiimoteProfileForGame(int controller_index) +{ + CycleProfileForGame(CycleDirection::Backward, Wiimote::GetConfig(), m_wiimote_profile_index, + GetWiimoteInputProfilesForGame(controller_index), controller_index); +} +} diff --git a/Source/Core/InputCommon/InputProfile.h b/Source/Core/InputCommon/InputProfile.h new file mode 100644 index 0000000000..ccb4fbf4db --- /dev/null +++ b/Source/Core/InputCommon/InputProfile.h @@ -0,0 +1,53 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +class InputConfig; + +namespace ControllerEmu +{ +class EmulatedController; +} + +#include +#include + +namespace InputProfile +{ +std::vector GetProfilesFromSetting(const std::string& setting, + const std::string& root); + +enum class CycleDirection : int +{ + Forward = 1, + Backward = -1 +}; + +class ProfileCycler +{ +public: + void NextWiimoteProfile(int controller_index); + void PreviousWiimoteProfile(int controller_index); + void NextWiimoteProfileForGame(int controller_index); + void PreviousWiimoteProfileForGame(int controller_index); + +private: + void CycleProfile(CycleDirection cycle_direction, InputConfig* device_configuration, + int& profile_index, int controller_index); + void CycleProfileForGame(CycleDirection cycle_direction, InputConfig* device_configuration, + int& profile_index, const std::string& setting, int controller_index); + std::vector GetProfilesForDevice(InputConfig* device_configuration); + std::string GetProfile(CycleDirection cycle_direction, int& profile_index, + const std::vector& profiles); + std::vector GetMatchingProfilesFromSetting(const std::string& setting, + const std::vector& profiles, + InputConfig* device_configuration); + void UpdateToProfile(const std::string& profile_filename, + ControllerEmu::EmulatedController* controller); + std::string GetWiimoteInputProfilesForGame(int controller_index); + + int m_wiimote_profile_index = 0; +}; +}