From 9cedce737f04ec614a19a22bb2c8e412e9ab43f8 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sun, 20 Mar 2022 00:45:45 -0700 Subject: [PATCH] Initial virtual PAD API --- Runtime/Input/CDolphinController.cpp | 23 +---- Runtime/Input/CDolphinController.hpp | 13 +-- Runtime/Input/CInputGenerator.cpp | 1 + Runtime/Input/CMakeLists.txt | 1 + Runtime/Input/InputTypes.hpp | 2 + Runtime/Input/PAD.hpp | 6 ++ aurora/include/aurora/pad.hpp | 80 +++++++++++++++++ aurora/lib/input.cpp | 129 +++++++++++++++++++++++++++ 8 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 Runtime/Input/PAD.hpp create mode 100644 aurora/include/aurora/pad.hpp diff --git a/Runtime/Input/CDolphinController.cpp b/Runtime/Input/CDolphinController.cpp index a0f2b4203..5357e6acf 100644 --- a/Runtime/Input/CDolphinController.cpp +++ b/Runtime/Input/CDolphinController.cpp @@ -1,24 +1,5 @@ #include "Runtime/Input/CDolphinController.hpp" -namespace PAD { -// clang-format off -enum BUTTON : u16 { - LEFT = 0x0001, - RIGHT = 0x0002, - DOWN = 0x0004, - UP = 0x0008, - TRIGGER_Z = 0x0010, - TRIGGER_R = 0x0020, - TRIGGER_L = 0x0040, - BUTTON_A = 0x0100, - BUTTON_B = 0x0200, - BUTTON_X = 0x0400, - BUTTON_Y = 0x0800, - BUTTON_START = 0x1000, -}; -// clang-format on -} // namespace PAD - namespace metaforce { CDolphinController::CDolphinController() { static bool sIsInitialized = false; @@ -77,8 +58,8 @@ void CDolphinController::ProcessAxis(u32 controller, EJoyAxis axis) { } static constexpr std::array mButtonMapping{ - PAD::BUTTON_A, PAD::BUTTON_B, PAD::BUTTON_X, PAD::BUTTON_Y, PAD::BUTTON_START, PAD::TRIGGER_Z, - PAD::UP, PAD::RIGHT, PAD::DOWN, PAD::LEFT, PAD::TRIGGER_L, PAD::TRIGGER_R, + PAD::BUTTON_A, PAD::BUTTON_B, PAD::BUTTON_X, PAD::BUTTON_Y, PAD::BUTTON_START, PAD::TRIGGER_Z, + PAD::BUTTON_UP, PAD::BUTTON_RIGHT, PAD::BUTTON_DOWN, PAD::BUTTON_LEFT, PAD::TRIGGER_L, PAD::TRIGGER_R, }; void CDolphinController::ProcessButtons(u32 controller) { diff --git a/Runtime/Input/CDolphinController.hpp b/Runtime/Input/CDolphinController.hpp index 05f68bcf7..712dbcab0 100644 --- a/Runtime/Input/CDolphinController.hpp +++ b/Runtime/Input/CDolphinController.hpp @@ -2,19 +2,8 @@ #include "Runtime/Input/IController.hpp" + namespace metaforce { -struct PADStatus { - u16 x0_buttons; - s8 x2_stickX; - s8 x3_stickY; - s8 x4_substickX; - s8 x5_substickY; - u8 x6_triggerL; - u8 x7_triggerR; - u8 x8_analogA; - u8 x9_analogB; - s8 xa_err; -}; class CDolphinController : public IController { diff --git a/Runtime/Input/CInputGenerator.cpp b/Runtime/Input/CInputGenerator.cpp index 1f0e379dc..237ee04a2 100644 --- a/Runtime/Input/CInputGenerator.cpp +++ b/Runtime/Input/CInputGenerator.cpp @@ -6,6 +6,7 @@ #include namespace metaforce { +static logvisor::Module Log("CInputGenerator"); void CInputGenerator::Update(float dt, CArchitectureQueue& queue) { if (m_firstFrame) { diff --git a/Runtime/Input/CMakeLists.txt b/Runtime/Input/CMakeLists.txt index d5c325173..fbe376aab 100644 --- a/Runtime/Input/CMakeLists.txt +++ b/Runtime/Input/CMakeLists.txt @@ -1,4 +1,5 @@ set(INPUT_SOURCES + PAD.hpp IController.hpp DolphinIController.cpp CControllerAxis.hpp CControllerButton.hpp diff --git a/Runtime/Input/InputTypes.hpp b/Runtime/Input/InputTypes.hpp index 37f3ae9a0..f21cecb54 100644 --- a/Runtime/Input/InputTypes.hpp +++ b/Runtime/Input/InputTypes.hpp @@ -1,4 +1,6 @@ #pragma once +#include "Runtime/Input/PAD.hpp" + namespace metaforce { enum class EIOPort { diff --git a/Runtime/Input/PAD.hpp b/Runtime/Input/PAD.hpp new file mode 100644 index 000000000..aaa52518e --- /dev/null +++ b/Runtime/Input/PAD.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "aurora/pad.hpp" + +using PADStatus = PAD::Status; +using PADButton = PAD::BUTTON; \ No newline at end of file diff --git a/aurora/include/aurora/pad.hpp b/aurora/include/aurora/pad.hpp new file mode 100644 index 000000000..2249fd314 --- /dev/null +++ b/aurora/include/aurora/pad.hpp @@ -0,0 +1,80 @@ +#pragma once +#include "aurora/common.hpp" + +namespace PAD { +struct Status { + u16 x0_buttons; + s8 x2_stickX; + s8 x3_stickY; + s8 x4_substickX; + s8 x5_substickY; + u8 x6_triggerL; + u8 x7_triggerR; + u8 x8_analogA; + u8 x9_analogB; + s8 xa_err; +}; + +enum BUTTON : u16 { + BUTTON_LEFT = 0x0001, + BUTTON_RIGHT = 0x0002, + BUTTON_DOWN = 0x0004, + BUTTON_UP = 0x0008, + TRIGGER_Z = 0x0010, + TRIGGER_R = 0x0020, + TRIGGER_L = 0x0040, + BUTTON_A = 0x0100, + BUTTON_B = 0x0200, + BUTTON_X = 0x0400, + BUTTON_Y = 0x0800, + BUTTON_START = 0x1000, +}; + +enum ERROR : s8 { + ERR_NONE = 0, + ERR_NO_CONTROLLER = -1, + ERR_NOT_READY = -2, + ERR_TRANSFER = -3, +}; + +enum MOTOR : u32 { + MOTOR_STOP = 0, + MOTOR_RUMBLE = 1, + MOTOR_STOP_HARD = 2, +}; +enum CHAN : u32 { + CHAN0_BIT = 0x80000000, + CHAN1_BIT = 0x40000000, + CHAN2_BIT = 0x20000000, + CHAN3_BIT = 0x10000000, +}; +} // namespace PAD + +namespace SI { +constexpr u32 ERROR_NO_RESPONSE = 0x0008; +constexpr u32 TYPE_GC = 0x08000000; +constexpr u32 GC_STANDARD = 0x01000000; +constexpr u32 GC_WIRELESS = 0x80000000; +constexpr u32 WIRELESS_STATE = 0x02000000; +constexpr u32 WIRELESS_FIX_ID = 0x00100000; +constexpr u32 GC_CONTROLLER = (TYPE_GC | GC_STANDARD); +constexpr u32 GC_RECEIVER = (TYPE_GC | GC_WIRELESS); +constexpr u32 GC_WAVEBIRD = (TYPE_GC | GC_WIRELESS | GC_STANDARD | WIRELESS_STATE | WIRELESS_FIX_ID); +} // namespace SI + +using PADSamplingCallback = void (*)(void); +constexpr bool PADButtonDown(u16 lastButton, u16 button) { return ((lastButton ^ button) & button) != 0; } +constexpr bool PADButtonUp(u16 lastButton, u16 button) { return ((lastButton ^ button) & lastButton) != 0; } +void PADClamp(PAD::Status* status); +void PADClampCircle(PAD::Status* status); +void PADInit(); +bool PADIsBarrel(s32 chan); +u32 PADRead(PAD::Status* status); +bool PADRecalibrate(u32 mask); +bool PADReset(u32 mask); +void PADSetAnalog(u32 mode); +void PADSetSpec(s32 spec); +void PADSetSamplingCallback(PADSamplingCallback callback); +void PADControlAllMotors(const u32* commands); + +u32 SIProbe(s32 chan); diff --git a/aurora/lib/input.cpp b/aurora/lib/input.cpp index 106264e92..4f68df0a5 100644 --- a/aurora/lib/input.cpp +++ b/aurora/lib/input.cpp @@ -1,4 +1,6 @@ #include "input.hpp" +#include "aurora/pad.hpp" + #include #include @@ -13,9 +15,30 @@ struct GameController { bool m_isGameCube = false; Sint32 m_index = -1; bool m_hasRumble = false; + constexpr bool operator==(const GameController&) const = default; }; absl::flat_hash_map g_GameControllers; +GameController get_controller_for_player(u32 player) { + for (const auto& [which, controller] : g_GameControllers) { + if (player_index(which) == player) { + return controller; + } + } + + return {}; +} + +Sint32 get_instance_for_player(u32 player) { + for (const auto& [which, controller] : g_GameControllers) { + if (player_index(which) == player) { + return which; + } + } + + return {}; +} + static std::optional remap_controller_layout(std::string_view mapping) { std::string newMapping; newMapping.reserve(mapping.size()); @@ -319,3 +342,109 @@ MouseButton translate_mouse_button_state(Uint8 state) noexcept { } } // namespace aurora::input + +void PADSetSpec(u32 spec) {} +void PADInit() {} + +static const std::array, 12> mMapping{{ + {SDL_CONTROLLER_BUTTON_A, PAD::BUTTON_A}, + {SDL_CONTROLLER_BUTTON_B, PAD::BUTTON_B}, + {SDL_CONTROLLER_BUTTON_X, PAD::BUTTON_X}, + {SDL_CONTROLLER_BUTTON_Y, PAD::BUTTON_Y}, + {SDL_CONTROLLER_BUTTON_START, PAD::BUTTON_START}, + {SDL_CONTROLLER_BUTTON_BACK, PAD::TRIGGER_Z}, + {SDL_CONTROLLER_BUTTON_LEFTSHOULDER, PAD::TRIGGER_L}, + {SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, PAD::TRIGGER_R}, + {SDL_CONTROLLER_BUTTON_DPAD_UP, PAD::BUTTON_UP}, + {SDL_CONTROLLER_BUTTON_DPAD_DOWN, PAD::BUTTON_DOWN}, + {SDL_CONTROLLER_BUTTON_DPAD_LEFT, PAD::BUTTON_LEFT}, + {SDL_CONTROLLER_BUTTON_DPAD_RIGHT, PAD::BUTTON_RIGHT}, +}}; + +u32 PADRead(PAD::Status* status) { + u32 rumbleSupport = 0; + for (u32 i = 0; i < 4; ++i) { + memset(&status[i], 0, sizeof(PAD::Status)); + auto controller = aurora::input::get_controller_for_player(i); + if (controller == aurora::input::GameController{}) { + status[i].xa_err = PAD::ERR_NO_CONTROLLER; + continue; + } + status[i].xa_err = PAD::ERR_NONE; + std::for_each(mMapping.begin(), mMapping.end(), [&controller, &i, &status](const auto& pair) { + if (SDL_GameControllerGetButton(controller.m_controller, pair.first)) { + status[i].x0_buttons |= pair.second; + } + }); + + Sint16 x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_LEFTX); + Sint16 y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_LEFTY); + x /= 256; + y = (-(y + 1u)) / 256u; + + status[i].x2_stickX = static_cast(x); + status[i].x3_stickY = static_cast(y); + + x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_RIGHTX); + y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_RIGHTY); + x /= 256; + y = (-(y + 1u)) / 256u; + + status[i].x4_substickX = static_cast(x); + status[i].x5_substickY = static_cast(y); + + x = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); + y = SDL_GameControllerGetAxis(controller.m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + x /= 128; + y = (-(y + 1u)) / 128u; + + status[i].x6_triggerL = static_cast(x); + status[i].x7_triggerR = static_cast(y); + + + if (controller.m_hasRumble) { + // Nintendo... why are these bits backwards? >.> + rumbleSupport |= PAD::CHAN3_BIT << (3 - i); + } + } + return rumbleSupport; +} + +void PADControlAllMotors(const u32* commands) { + for (u32 i = 0; i < 4; ++i) { + auto controller = aurora::input::get_controller_for_player(i); + auto instance = aurora::input::get_instance_for_player(i); + if (controller == aurora::input::GameController{}) { + continue; + } + + if (controller.m_isGameCube) { + if (commands[i] == PAD::MOTOR_STOP) { + aurora::input::controller_rumble(instance, 0, 1, 0); + } else if (commands[i] == PAD::MOTOR_RUMBLE) { + aurora::input::controller_rumble(instance, 1, 1, 0); + } else if (commands[i] == PAD::MOTOR_STOP_HARD) { + aurora::input::controller_rumble(instance, 0, 0, 0); + } + } else { + // TODO: Figure out sane values for generic controllers + } + } +} + + +u32 SIProbe(s32 chan) { + const auto controller = aurora::input::get_controller_for_player(chan); + if (controller == aurora::input::GameController{}){ + return SI::ERROR_NO_RESPONSE; + } + + if (controller.m_isGameCube) { + auto level = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(controller.m_controller)); + if (level == SDL_JOYSTICK_POWER_UNKNOWN) { + return SI::GC_WAVEBIRD; + } + } + + return SI::GC_CONTROLLER; +} \ No newline at end of file