mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2026-08-30 01:40:07 -04:00
Added some more patch functionality, added recomp namespace
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
#include "recomp_helpers.h"
|
||||
#include "recomp_input.h"
|
||||
#include "../ultramodern/ultramodern.hpp"
|
||||
#include "../patches/input.h"
|
||||
|
||||
namespace N64Inputs {
|
||||
enum Input : uint16_t {
|
||||
A = 0x8000,
|
||||
B = 0x4000,
|
||||
Z = 0x2000,
|
||||
START = 0x1000,
|
||||
DPAD_UP = 0x0800,
|
||||
DPAD_DOWN = 0x0400,
|
||||
DPAD_LEFT = 0x0200,
|
||||
DPAD_RIGHT = 0x0100,
|
||||
L = 0x0020,
|
||||
R = 0x0010,
|
||||
C_UP = 0x0008,
|
||||
C_DOWN = 0x0004,
|
||||
C_LEFT = 0x0002,
|
||||
C_RIGHT = 0x0001,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr float controller_default_threshold = 0.7f;
|
||||
struct GameControllerAxisMapping {
|
||||
int32_t axis;
|
||||
float threshold; // Positive or negative to indicate direction
|
||||
uint32_t output_mask;
|
||||
};
|
||||
using axis_map_t = std::vector<GameControllerAxisMapping>;
|
||||
|
||||
struct GameControllerButtonMapping {
|
||||
uint32_t button;
|
||||
uint32_t output_mask;
|
||||
};
|
||||
using button_map_t = std::vector<GameControllerButtonMapping>;
|
||||
|
||||
uint32_t process_controller_mappings(const recomp::ControllerState& controller_state, const axis_map_t& axis_map, const button_map_t& button_map) {
|
||||
uint32_t cur_buttons = 0;
|
||||
|
||||
for (const auto& mapping : axis_map) {
|
||||
float input_value = controller_state.axes[mapping.axis];
|
||||
if (mapping.threshold > 0) {
|
||||
if (input_value > mapping.threshold) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (input_value < mapping.threshold) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& mapping : button_map) {
|
||||
int input_value = controller_state.buttons & mapping.button;
|
||||
if (input_value) {
|
||||
cur_buttons |= mapping.output_mask;
|
||||
}
|
||||
}
|
||||
|
||||
return cur_buttons;
|
||||
}
|
||||
|
||||
void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||
static const axis_map_t general_axis_map{
|
||||
{ recomp::ControllerState::AXIS_RIGHT_X, -controller_default_threshold, N64Inputs::C_LEFT },
|
||||
{ recomp::ControllerState::AXIS_RIGHT_X, controller_default_threshold, N64Inputs::C_RIGHT },
|
||||
{ recomp::ControllerState::AXIS_RIGHT_Y, -controller_default_threshold, N64Inputs::C_UP },
|
||||
{ recomp::ControllerState::AXIS_RIGHT_Y, controller_default_threshold, N64Inputs::C_DOWN },
|
||||
{ recomp::ControllerState::AXIS_LEFT_TRIGGER, 0.30f, N64Inputs::Z },
|
||||
};
|
||||
static const button_map_t general_button_map{
|
||||
{ recomp::ControllerState::BUTTON_START, N64Inputs::START },
|
||||
{ recomp::ControllerState::BUTTON_SOUTH, N64Inputs::A },
|
||||
{ recomp::ControllerState::BUTTON_EAST, N64Inputs::B },
|
||||
{ recomp::ControllerState::BUTTON_WEST, N64Inputs::B },
|
||||
{ recomp::ControllerState::BUTTON_L1, N64Inputs::L },
|
||||
{ recomp::ControllerState::BUTTON_R1, N64Inputs::R },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_LEFT, N64Inputs::DPAD_LEFT },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_RIGHT, N64Inputs::DPAD_RIGHT },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_UP, N64Inputs::DPAD_UP },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_DOWN, N64Inputs::DPAD_DOWN },
|
||||
};
|
||||
|
||||
uint16_t cur_buttons = 0;
|
||||
float cur_x = 0.0f;
|
||||
float cur_y = 0.0f;
|
||||
|
||||
recomp::get_keyboard_input(&cur_buttons, &cur_x, &cur_y);
|
||||
|
||||
std::vector<InputState> input_states = recomp::get_input_states();
|
||||
|
||||
for (const InputState& state : input_states) {
|
||||
if (const auto* controller_state = std::get_if<ControllerState>(&state)) {
|
||||
cur_x += controller_state->axes[ControllerState::AXIS_LEFT_X];
|
||||
cur_y -= controller_state->axes[ControllerState::AXIS_LEFT_Y];
|
||||
|
||||
cur_buttons |= (uint16_t)process_controller_mappings(*controller_state, general_axis_map, general_button_map);
|
||||
}
|
||||
else if (const auto* mouse_state = std::get_if<MouseState>(&state)) {
|
||||
// Mouse currently unused
|
||||
}
|
||||
}
|
||||
|
||||
*buttons_out = cur_buttons;
|
||||
cur_x = std::clamp(cur_x, -1.0f, 1.0f);
|
||||
cur_y = std::clamp(cur_y, -1.0f, 1.0f);
|
||||
*x_out = cur_x;
|
||||
*y_out = cur_y;
|
||||
}
|
||||
|
||||
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
static const axis_map_t item_axis_map{
|
||||
{ recomp::ControllerState::AXIS_RIGHT_X, -controller_default_threshold, N64Inputs::C_LEFT },
|
||||
{ recomp::ControllerState::AXIS_RIGHT_X, controller_default_threshold, N64Inputs::C_RIGHT },
|
||||
{ recomp::ControllerState::AXIS_RIGHT_Y, controller_default_threshold, N64Inputs::C_DOWN },
|
||||
};
|
||||
|
||||
static const button_map_t item_button_map {
|
||||
{ recomp::ControllerState::BUTTON_EAST, N64Inputs::B },
|
||||
{ recomp::ControllerState::BUTTON_WEST, N64Inputs::B },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_LEFT, N64Inputs::DPAD_LEFT },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_RIGHT, N64Inputs::DPAD_RIGHT },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_UP, N64Inputs::DPAD_UP },
|
||||
{ recomp::ControllerState::BUTTON_DPAD_DOWN, N64Inputs::DPAD_DOWN },
|
||||
};
|
||||
|
||||
u32* buttons_out = _arg<0, u32*>(rdram, ctx);
|
||||
|
||||
uint32_t cur_buttons = 0;
|
||||
|
||||
// TODO do this in a way that will allow for remapping keyboard inputs
|
||||
uint16_t keyboard_buttons;
|
||||
float dummy_x, dummy_y;
|
||||
recomp::get_keyboard_input(&keyboard_buttons, &dummy_x, &dummy_y);
|
||||
cur_buttons |= keyboard_buttons;
|
||||
|
||||
// Process controller inputs
|
||||
std::vector<recomp::InputState> input_states = recomp::get_input_states();
|
||||
|
||||
for (const recomp::InputState& state : input_states) {
|
||||
if (const auto* controller_state = std::get_if<recomp::ControllerState>(&state)) {
|
||||
cur_buttons |= process_controller_mappings(*controller_state, item_axis_map, item_button_map);
|
||||
}
|
||||
else if (const auto* mouse_state = std::get_if<recomp::MouseState>(&state)) {
|
||||
// Mouse currently unused
|
||||
}
|
||||
}
|
||||
|
||||
*buttons_out = cur_buttons;
|
||||
}
|
||||
|
||||
// TODO move this
|
||||
extern "C" void recomp_puts(uint8_t* rdram, recomp_context* ctx) {
|
||||
PTR(char) cur_str = _arg<0, PTR(char)>(rdram, ctx);
|
||||
u32 length = _arg<1, u32>(rdram, ctx);
|
||||
|
||||
for (u32 i = 0; i < length; i++) {
|
||||
fputc(MEM_B(i, (gpr)cur_str), stdout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "../ultramodern/ultramodern.hpp"
|
||||
#include "recomp.h"
|
||||
#include "recomp_input.h"
|
||||
#include "recomp_ui.h"
|
||||
#include "SDL.h"
|
||||
|
||||
std::atomic_int32_t mouse_wheel_pos = 0;
|
||||
std::vector<SDL_JoystickID> controllers{};
|
||||
|
||||
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
//case SDL_EventType::SDL_KEYUP:
|
||||
//case SDL_EventType::SDL_KEYDOWN:
|
||||
// {
|
||||
// const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
||||
// int new_button = 0;
|
||||
|
||||
// for (const auto& mapping : keyboard_button_map) {
|
||||
// if (key_states[mapping.first]) {
|
||||
// new_button |= mapping.second;
|
||||
// }
|
||||
// }
|
||||
|
||||
// button = new_button;
|
||||
|
||||
// stick_x = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
||||
// stick_y = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
||||
// }
|
||||
// break;
|
||||
case SDL_EventType::SDL_CONTROLLERDEVICEADDED:
|
||||
{
|
||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(controller_event->which);
|
||||
printf("Controller added: %d\n", controller_event->which);
|
||||
if (controller != nullptr) {
|
||||
printf(" Instance ID: %d\n", SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||
controllers.push_back(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_CONTROLLERDEVICEREMOVED:
|
||||
{
|
||||
SDL_ControllerDeviceEvent* controller_event = (SDL_ControllerDeviceEvent*)event;
|
||||
printf("Controller removed: %d\n", controller_event->which);
|
||||
std::erase(controllers, controller_event->which);
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_QUIT:
|
||||
ultramodern::quit();
|
||||
return true;
|
||||
case SDL_EventType::SDL_MOUSEWHEEL:
|
||||
{
|
||||
SDL_MouseWheelEvent* wheel_event = (SDL_MouseWheelEvent*)event;
|
||||
mouse_wheel_pos.fetch_add(wheel_event->y * (wheel_event->direction == SDL_MOUSEWHEEL_FLIPPED ? -1 : 1));
|
||||
}
|
||||
recomp::queue_event(*event);
|
||||
break;
|
||||
default:
|
||||
recomp::queue_event(*event);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void recomp::handle_events() {
|
||||
SDL_Event cur_event;
|
||||
static bool exited = false;
|
||||
while (SDL_PollEvent(&cur_event) && !exited) {
|
||||
exited = sdl_event_filter(nullptr, &cur_event);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<recomp::InputState> recomp::get_input_states() {
|
||||
std::vector<InputState> ret{};
|
||||
|
||||
int32_t mouse_x;
|
||||
int32_t mouse_y;
|
||||
|
||||
Uint32 sdl_mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||
|
||||
struct MouseButtonMapping {
|
||||
Sint32 input;
|
||||
decltype(MouseState::buttons) output;
|
||||
};
|
||||
static const std::vector<MouseButtonMapping> input_mouse_map{
|
||||
{ SDL_BUTTON_LMASK, MouseState::LEFT },
|
||||
{ SDL_BUTTON_RMASK, MouseState::RIGHT },
|
||||
{ SDL_BUTTON_MMASK, MouseState::MIDDLE },
|
||||
{ SDL_BUTTON_X1MASK, MouseState::BACK },
|
||||
{ SDL_BUTTON_X2MASK, MouseState::FORWARD },
|
||||
};
|
||||
|
||||
decltype(MouseState::buttons) mouse_buttons = 0;
|
||||
for (const MouseButtonMapping& mapping : input_mouse_map) {
|
||||
if (sdl_mouse_buttons & mapping.input) {
|
||||
mouse_buttons |= mapping.output;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouse_buttons & MouseState::FORWARD) {
|
||||
printf("forward\n");
|
||||
}
|
||||
|
||||
if (mouse_buttons & MouseState::BACK) {
|
||||
printf("back\n");
|
||||
}
|
||||
|
||||
ret.emplace_back(
|
||||
MouseState {
|
||||
.wheel_pos = mouse_wheel_pos,
|
||||
.position_x = mouse_x,
|
||||
.position_y = mouse_y,
|
||||
.buttons = mouse_buttons
|
||||
}
|
||||
);
|
||||
|
||||
for (SDL_JoystickID controller_id : controllers) {
|
||||
struct InputButtonMapping {
|
||||
SDL_GameControllerButton input;
|
||||
decltype(ControllerState::buttons) output;
|
||||
};
|
||||
static const std::vector<InputButtonMapping> input_button_map{
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START, ControllerState::BUTTON_START },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A, ControllerState::BUTTON_SOUTH },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B, ControllerState::BUTTON_EAST },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X, ControllerState::BUTTON_WEST },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y, ControllerState::BUTTON_NORTH },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER, ControllerState::BUTTON_L1 },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, ControllerState::BUTTON_R1 },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK, ControllerState::BUTTON_L3 },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK, ControllerState::BUTTON_R3 },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT, ControllerState::BUTTON_DPAD_LEFT },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT, ControllerState::BUTTON_DPAD_RIGHT },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP, ControllerState::BUTTON_DPAD_UP },
|
||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN, ControllerState::BUTTON_DPAD_DOWN },
|
||||
};
|
||||
SDL_GameController* controller = SDL_GameControllerFromInstanceID(controller_id);
|
||||
decltype(ControllerState::buttons) buttons = 0;
|
||||
|
||||
for (const InputButtonMapping& mapping : input_button_map) {
|
||||
if (SDL_GameControllerGetButton(controller, mapping.input)) {
|
||||
buttons |= mapping.output;
|
||||
}
|
||||
}
|
||||
|
||||
auto& new_input_state = ret.emplace_back(
|
||||
ControllerState {
|
||||
.buttons = buttons,
|
||||
.axes = {},
|
||||
}
|
||||
);
|
||||
auto& new_state = std::get<ControllerState>(new_input_state);
|
||||
new_state.axes[ControllerState::AXIS_LEFT_X] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) * (1/32768.0f);
|
||||
new_state.axes[ControllerState::AXIS_LEFT_Y] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY) * (1/32768.0f);
|
||||
new_state.axes[ControllerState::AXIS_RIGHT_X] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX) * (1/32768.0f);
|
||||
new_state.axes[ControllerState::AXIS_RIGHT_Y] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY) * (1/32768.0f);
|
||||
new_state.axes[ControllerState::AXIS_LEFT_TRIGGER] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT) * (1/32768.0f);
|
||||
new_state.axes[ControllerState::AXIS_LEFT_TRIGGER] = SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * (1/32768.0f);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO figure out a way to handle this more generally without exposing SDL to controls.cpp
|
||||
void recomp::get_keyboard_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||
static const std::vector<std::pair<SDL_Scancode, int>> keyboard_button_map{
|
||||
{ SDL_Scancode::SDL_SCANCODE_LEFT, 0x0002 }, // c left
|
||||
{ SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right
|
||||
{ SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up
|
||||
{ SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down
|
||||
{ SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start
|
||||
{ SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a
|
||||
{ SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b
|
||||
{ SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z
|
||||
{ SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l
|
||||
{ SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r
|
||||
{ SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left
|
||||
{ SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right
|
||||
{ SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up
|
||||
{ SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down
|
||||
};
|
||||
|
||||
const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
||||
|
||||
*buttons_out = 0;
|
||||
|
||||
for (const auto& mapping : keyboard_button_map) {
|
||||
if (key_states[mapping.first]) {
|
||||
*buttons_out |= mapping.second;
|
||||
}
|
||||
}
|
||||
|
||||
*x_out = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
||||
*y_out = (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
||||
}
|
||||
Reference in New Issue
Block a user