Initial virtual PAD API

This commit is contained in:
Phillip Stephens 2022-03-20 00:45:45 -07:00
parent a96fe24260
commit 9cedce737f
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
8 changed files with 222 additions and 33 deletions

View File

@ -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<u16, size_t(EButton::MAX)> 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) {

View File

@ -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 {

View File

@ -6,6 +6,7 @@
#include <magic_enum.hpp>
namespace metaforce {
static logvisor::Module Log("CInputGenerator");
void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
if (m_firstFrame) {

View File

@ -1,4 +1,5 @@
set(INPUT_SOURCES
PAD.hpp
IController.hpp DolphinIController.cpp
CControllerAxis.hpp
CControllerButton.hpp

View File

@ -1,4 +1,6 @@
#pragma once
#include "Runtime/Input/PAD.hpp"
namespace metaforce {
enum class EIOPort {

6
Runtime/Input/PAD.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "aurora/pad.hpp"
using PADStatus = PAD::Status;
using PADButton = PAD::BUTTON;

View File

@ -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);

View File

@ -1,4 +1,6 @@
#include "input.hpp"
#include "aurora/pad.hpp"
#include <SDL_haptic.h>
#include <absl/container/btree_map.h>
@ -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<Uint32, GameController> 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<std::string> 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<std::pair<SDL_GameControllerButton, PAD::BUTTON>, 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<s8>(x);
status[i].x3_stickY = static_cast<s8>(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<s8>(x);
status[i].x5_substickY = static_cast<s8>(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<s8>(x);
status[i].x7_triggerR = static_cast<s8>(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;
}