Added some more patch functionality, added recomp namespace
This commit is contained in:
parent
3b06ad52f0
commit
ec23ef02fd
|
@ -93,6 +93,7 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/flash.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/flash.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/math_routines.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/math_routines.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/overlays.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/overlays.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/recomp/patch_loading.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/pak.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/pak.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/pi.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/pi.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/recomp/ultra_stubs.cpp
|
${CMAKE_SOURCE_DIR}/src/recomp/ultra_stubs.cpp
|
||||||
|
@ -105,6 +106,9 @@ set (SOURCES
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
||||||
|
|
||||||
|
${CMAKE_SOURCE_DIR}/src/game/input.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_events.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_events.cpp
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define __RECOMP_HELPERS__
|
#define __RECOMP_HELPERS__
|
||||||
|
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
#include "../ultramodern/ultra64.h"
|
||||||
|
|
||||||
template<int index, typename T>
|
template<int index, typename T>
|
||||||
T _arg(uint8_t* rdram, recomp_context* ctx) {
|
T _arg(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
@ -13,7 +14,10 @@ T _arg(uint8_t* rdram, recomp_context* ctx) {
|
||||||
return ctx->f12.fl;
|
return ctx->f12.fl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return std::bit_cast<T>(raw_arg);
|
// static_assert in else workaround
|
||||||
|
[] <bool flag = false>() {
|
||||||
|
static_assert(flag, "Floats in a2/a3 not supported");
|
||||||
|
}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_pointer_v<T>) {
|
else if constexpr (std::is_pointer_v<T>) {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef __RECOMP_INPUT_H__
|
||||||
|
#define __RECOMP_INPUT_H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace recomp {
|
||||||
|
struct ControllerState {
|
||||||
|
enum Button : uint32_t {
|
||||||
|
BUTTON_NORTH = 1 << 0,
|
||||||
|
BUTTON_SOUTH = 1 << 1,
|
||||||
|
BUTTON_EAST = 1 << 2,
|
||||||
|
BUTTON_WEST = 1 << 3,
|
||||||
|
BUTTON_L1 = 1 << 4, // Left Bumper
|
||||||
|
BUTTON_R1 = 1 << 5, // Right Bumper
|
||||||
|
BUTTON_L2 = 1 << 6, // Left Trigger Press
|
||||||
|
BUTTON_R2 = 1 << 7, // Right Trigger Press
|
||||||
|
BUTTON_L3 = 1 << 8, // Left Joystick Press
|
||||||
|
BUTTON_R3 = 1 << 9, // Right Joystick Press
|
||||||
|
BUTTON_DPAD_UP = 1 << 10,
|
||||||
|
BUTTON_DPAD_DOWN = 1 << 11,
|
||||||
|
BUTTON_DPAD_RIGHT = 1 << 12,
|
||||||
|
BUTTON_DPAD_LEFT = 1 << 13,
|
||||||
|
BUTTON_START = 1 << 14,
|
||||||
|
};
|
||||||
|
enum Axis : size_t {
|
||||||
|
AXIS_LEFT_X,
|
||||||
|
AXIS_LEFT_Y,
|
||||||
|
AXIS_RIGHT_X,
|
||||||
|
AXIS_RIGHT_Y,
|
||||||
|
AXIS_LEFT_TRIGGER,
|
||||||
|
AXIS_RIGHT_TRIGGER,
|
||||||
|
AXIS_MAX
|
||||||
|
};
|
||||||
|
uint32_t buttons;
|
||||||
|
float axes[AXIS_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MouseState {
|
||||||
|
enum Button : uint32_t {
|
||||||
|
LEFT = 1 << 0,
|
||||||
|
RIGHT = 1 << 1,
|
||||||
|
MIDDLE = 1 << 2,
|
||||||
|
BACK = 1 << 3,
|
||||||
|
FORWARD = 1 << 4,
|
||||||
|
};
|
||||||
|
int32_t wheel_pos;
|
||||||
|
int32_t position_x;
|
||||||
|
int32_t position_y;
|
||||||
|
uint32_t buttons;
|
||||||
|
};
|
||||||
|
|
||||||
|
using InputState = std::variant<ControllerState, MouseState>;
|
||||||
|
|
||||||
|
void get_keyboard_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
||||||
|
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
|
||||||
|
std::vector<InputState> get_input_states();
|
||||||
|
void handle_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,14 +5,16 @@
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
|
||||||
void queue_event(const SDL_Event& event);
|
|
||||||
bool try_deque_event(SDL_Event& out);
|
|
||||||
|
|
||||||
namespace Rml {
|
namespace Rml {
|
||||||
class ElementDocument;
|
class ElementDocument;
|
||||||
class EventListenerInstancer;
|
class EventListenerInstancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace recomp {
|
||||||
|
|
||||||
|
void queue_event(const SDL_Event& event);
|
||||||
|
bool try_deque_event(SDL_Event& out);
|
||||||
|
|
||||||
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer();
|
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer();
|
||||||
|
|
||||||
enum class Menu {
|
enum class Menu {
|
||||||
|
@ -22,5 +24,6 @@ enum class Menu {
|
||||||
|
|
||||||
void set_current_menu(Menu menu);
|
void set_current_menu(Menu menu);
|
||||||
void destroy_ui();
|
void destroy_ui();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
117
patches/input.c
117
patches/input.c
|
@ -8,17 +8,126 @@ u32 sPlayerItemButtons[] = {
|
||||||
BTN_CRIGHT,
|
BTN_CRIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return currently-pressed button, in order of priority B, CLEFT, CDOWN, CRIGHT.
|
u32 prev_item_buttons = 0;
|
||||||
|
u32 cur_item_buttons = 0;
|
||||||
|
u32 pressed_item_buttons = 0;
|
||||||
|
u32 released_item_buttons = 0;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EQUIP_SLOT_EX_DEKU = -2,
|
||||||
|
EQUIP_SLOT_EX_GORON = -3,
|
||||||
|
EQUIP_SLOT_EX_ZORA = -4,
|
||||||
|
EQUIP_SLOT_EX_OCARINA = -5
|
||||||
|
} EquipSlotEx;
|
||||||
|
|
||||||
|
void GameState_GetInput(GameState* gameState) {
|
||||||
|
PadMgr_GetInput(gameState->input, true);
|
||||||
|
|
||||||
|
|
||||||
|
prev_item_buttons = cur_item_buttons;
|
||||||
|
recomp_get_item_inputs(&cur_item_buttons);
|
||||||
|
u32 button_diff = prev_item_buttons ^ cur_item_buttons;
|
||||||
|
pressed_item_buttons = cur_item_buttons & button_diff;
|
||||||
|
released_item_buttons = prev_item_buttons & button_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SlotMap {
|
||||||
|
u32 button;
|
||||||
|
EquipSlotEx slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SlotMap exSlotMapping[] = {
|
||||||
|
{BTN_DLEFT, EQUIP_SLOT_EX_GORON},
|
||||||
|
{BTN_DRIGHT, EQUIP_SLOT_EX_ZORA},
|
||||||
|
{BTN_DUP, EQUIP_SLOT_EX_DEKU},
|
||||||
|
{BTN_DDOWN, EQUIP_SLOT_EX_OCARINA},
|
||||||
|
};
|
||||||
|
|
||||||
|
// D-Pad items
|
||||||
|
// TODO restore this once UI is made
|
||||||
|
// Return currently-pressed button, in order of priority D-Pad, B, CLEFT, CDOWN, CRIGHT.
|
||||||
|
/*
|
||||||
EquipSlot func_8082FDC4(void) {
|
EquipSlot func_8082FDC4(void) {
|
||||||
EquipSlot i;
|
EquipSlot i;
|
||||||
RecompInputs cur_inputs;
|
|
||||||
recomp_get_item_inputs(&cur_inputs);
|
for (int mapping_index = 0; mapping_index < ARRAY_COUNT(exSlotMapping); mapping_index++) {
|
||||||
|
if (pressed_item_buttons & exSlotMapping[mapping_index].button) {
|
||||||
|
return (EquipSlot)exSlotMapping[mapping_index].slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_COUNT(sPlayerItemButtons); i++) {
|
for (i = 0; i < ARRAY_COUNT(sPlayerItemButtons); i++) {
|
||||||
if (CHECK_BTN_ALL(cur_inputs.buttons, sPlayerItemButtons[i])) {
|
if (CHECK_BTN_ALL(pressed_item_buttons, sPlayerItemButtons[i])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemId Player_GetItemOnButton(PlayState* play, Player* player, EquipSlot slot) {
|
||||||
|
if (slot >= EQUIP_SLOT_A) {
|
||||||
|
return ITEM_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot <= EQUIP_SLOT_EX_DEKU) {
|
||||||
|
ItemId dpad_item = ITEM_NONE;
|
||||||
|
switch ((EquipSlotEx)slot) {
|
||||||
|
case EQUIP_SLOT_EX_DEKU:
|
||||||
|
dpad_item = ITEM_MASK_DEKU;
|
||||||
|
break;
|
||||||
|
case EQUIP_SLOT_EX_GORON:
|
||||||
|
dpad_item = ITEM_MASK_GORON;
|
||||||
|
break;
|
||||||
|
case EQUIP_SLOT_EX_ZORA:
|
||||||
|
dpad_item = ITEM_MASK_ZORA;
|
||||||
|
break;
|
||||||
|
case EQUIP_SLOT_EX_OCARINA:
|
||||||
|
dpad_item = ITEM_OCARINA_OF_TIME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dpad_item != ITEM_NONE) && (INV_CONTENT(dpad_item) == dpad_item)) {
|
||||||
|
recomp_printf("Used dpad item %d\n", dpad_item);
|
||||||
|
return dpad_item;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ITEM_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == EQUIP_SLOT_B) {
|
||||||
|
ItemId item = Inventory_GetBtnBItem(play);
|
||||||
|
|
||||||
|
if (item >= ITEM_FD) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((player->currentMask == PLAYER_MASK_BLAST) && (play->interfaceCtx.bButtonDoAction == DO_ACTION_EXPLODE)) {
|
||||||
|
return ITEM_F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((player->currentMask == PLAYER_MASK_BREMEN) && (play->interfaceCtx.bButtonDoAction == DO_ACTION_MARCH)) {
|
||||||
|
return ITEM_F1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((player->currentMask == PLAYER_MASK_KAMARO) && (play->interfaceCtx.bButtonDoAction == DO_ACTION_DANCE)) {
|
||||||
|
return ITEM_F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == EQUIP_SLOT_C_LEFT) {
|
||||||
|
return C_BTN_ITEM(EQUIP_SLOT_C_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == EQUIP_SLOT_C_DOWN) {
|
||||||
|
return C_BTN_ITEM(EQUIP_SLOT_C_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EQUIP_SLOT_C_RIGHT
|
||||||
|
|
||||||
|
return C_BTN_ITEM(EQUIP_SLOT_C_RIGHT);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -11,13 +11,6 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct RecompInputs {
|
|
||||||
u32 buttons;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
} RecompInputs;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef MIPS
|
#ifdef MIPS
|
||||||
# define DECLARE_FUNC(type, name, ...) \
|
# define DECLARE_FUNC(type, name, ...) \
|
||||||
type name(__VA_ARGS__);
|
type name(__VA_ARGS__);
|
||||||
|
@ -26,7 +19,9 @@ typedef struct RecompInputs {
|
||||||
void name(uint8_t* rdram, recomp_context* ctx);
|
void name(uint8_t* rdram, recomp_context* ctx);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DECLARE_FUNC(void, recomp_get_item_inputs, RecompInputs* inputs);
|
DECLARE_FUNC(void, recomp_get_item_inputs, u32* buttons);
|
||||||
|
// TODO move this
|
||||||
|
DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,6 @@
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
|
int recomp_printf(const char* fmt, ...);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "patches.h"
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
void* proutPrintf(void* dst, const char* fmt, size_t size) {
|
||||||
|
recomp_puts(fmt, size);
|
||||||
|
return (void*)1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recomp_printf(const char* fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
int ret = _Printf(&proutPrintf, NULL, fmt, args);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ __start = 0x80000000;
|
||||||
|
|
||||||
/* Dummy addresses that get recompiled into function calls */
|
/* Dummy addresses that get recompiled into function calls */
|
||||||
recomp_get_item_inputs = 0x81000000;
|
recomp_get_item_inputs = 0x81000000;
|
||||||
|
recomp_puts = 0x81000004;
|
||||||
|
|
||||||
/* TODO pull these symbols from the elf file directly */
|
/* TODO pull these symbols from the elf file directly */
|
||||||
Player_PostLimbDrawGameplay = 0x80128BD0;
|
Player_PostLimbDrawGameplay = 0x80128BD0;
|
||||||
|
@ -10,4 +11,7 @@ gRegEditor = 0x801f3f60;
|
||||||
Audio_PlaySfx = 0x8019f0c8;
|
Audio_PlaySfx = 0x8019f0c8;
|
||||||
gSaveContext = 0x801ef670;
|
gSaveContext = 0x801ef670;
|
||||||
Interface_SetHudVisibility = 0x8010ef68;
|
Interface_SetHudVisibility = 0x8010ef68;
|
||||||
Player_GetItemOnButton = 0x8012364C;
|
PadMgr_GetInput = 0x80175f98;
|
||||||
|
Inventory_GetBtnBItem = 0x8012ec80;
|
||||||
|
gItemSlots = 0x801c2078;
|
||||||
|
_Printf = 0x8008e050;
|
||||||
|
|
|
@ -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]);
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
|
#include "recomp_input.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
@ -33,120 +34,6 @@ void exit_error(const char* str, Ts ...args) {
|
||||||
std::quick_exit(EXIT_FAILURE);
|
std::quick_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GameControllerAxisMapping {
|
|
||||||
SDL_GameControllerAxis axis;
|
|
||||||
int threshold; // Positive or negative to indicate direction
|
|
||||||
uint16_t output_mask;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr int controller_default_threshold = 20000;
|
|
||||||
|
|
||||||
std::vector<GameControllerAxisMapping> controller_axis_map{
|
|
||||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX, -controller_default_threshold, 0x0002 }, // c left
|
|
||||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX, controller_default_threshold, 0x0001 }, // c right
|
|
||||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY, -controller_default_threshold, 0x0008 }, // c up
|
|
||||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY, controller_default_threshold, 0x0004 }, // c down
|
|
||||||
{ SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT, 10000, 0x2000 }, // z
|
|
||||||
//{ 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
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GameControllerButtonMapping {
|
|
||||||
SDL_GameControllerButton button;
|
|
||||||
uint16_t output_mask;
|
|
||||||
};
|
|
||||||
std::vector<GameControllerButtonMapping> controller_button_map{
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START, 0x1000 }, // start
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A, 0x8000 }, // a
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B, 0x4000 }, // b
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X, 0x4000 }, // b
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 0x0020 }, // l
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 0x0010 }, // r
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0x0200 }, // dpad left
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 0x0100 }, // dpad right
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP, 0x0800 }, // dpad up
|
|
||||||
{ SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN, 0x0400 }, // dpad down
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
queue_event(*event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "system");
|
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "system");
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
|
||||||
|
@ -181,67 +68,7 @@ ultramodern::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_gfx(void*) {
|
void update_gfx(void*) {
|
||||||
// Handle events
|
recomp::handle_events();
|
||||||
constexpr int max_events_per_frame = 16;
|
|
||||||
SDL_Event cur_event;
|
|
||||||
int i = 0;
|
|
||||||
static bool exited = false;
|
|
||||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event) && !exited) {
|
|
||||||
exited = sdl_event_filter(nullptr, &cur_event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_input(uint16_t* buttons_out, float* x_out, float* y_out) {
|
|
||||||
uint16_t cur_buttons = 0;
|
|
||||||
float cur_x = 0.0f;
|
|
||||||
float cur_y = 0.0f;
|
|
||||||
|
|
||||||
const Uint8* key_states = SDL_GetKeyboardState(nullptr);
|
|
||||||
int new_button = 0;
|
|
||||||
|
|
||||||
for (const auto& mapping : keyboard_button_map) {
|
|
||||||
if (key_states[mapping.first]) {
|
|
||||||
cur_buttons |= mapping.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_x += (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
|
|
||||||
cur_y += (100.0f / 100.0f) * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
|
|
||||||
|
|
||||||
for (SDL_JoystickID controller_id : controllers) {
|
|
||||||
SDL_GameController* controller = SDL_GameControllerFromInstanceID(controller_id);
|
|
||||||
if (controller != nullptr) {
|
|
||||||
cur_x += SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) * (1/32768.0f);
|
|
||||||
cur_y -= SDL_GameControllerGetAxis(controller, SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY) * (1/32768.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& mapping : controller_axis_map) {
|
|
||||||
int input_value = SDL_GameControllerGetAxis(controller, 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 : controller_button_map) {
|
|
||||||
int input_value = SDL_GameControllerGetButton(controller, mapping.button);
|
|
||||||
if (input_value) {
|
|
||||||
cur_buttons |= mapping.output_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_AudioDeviceID audio_device = 0;
|
static SDL_AudioDeviceID audio_device = 0;
|
||||||
|
@ -357,7 +184,7 @@ int main(int argc, char** argv) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ultramodern::input_callbacks_t input_callbacks{
|
ultramodern::input_callbacks_t input_callbacks{
|
||||||
.get_input = get_input,
|
.get_input = recomp::get_n64_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
ultramodern::start({}, audio_callbacks, input_callbacks, gfx_callbacks);
|
ultramodern::start({}, audio_callbacks, input_callbacks, gfx_callbacks);
|
||||||
|
|
|
@ -101,16 +101,3 @@ extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "../patches/input.h"
|
|
||||||
|
|
||||||
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
|
||||||
RecompInputs* inputs = _arg<0, RecompInputs*>(rdram, ctx);
|
|
||||||
|
|
||||||
if (input_callbacks.get_input) {
|
|
||||||
u16 buttons;
|
|
||||||
input_callbacks.get_input(&buttons, &inputs->x, &inputs->y);
|
|
||||||
// TODO remap the inputs for items here
|
|
||||||
inputs->buttons = buttons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -35,6 +35,13 @@ void load_overlay(size_t section_table_index, int32_t ram) {
|
||||||
section_addresses[section.index] = ram;
|
section_addresses[section.index] = ram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_special_overlay(const SectionTableEntry& section, int32_t ram) {
|
||||||
|
for (size_t function_index = 0; function_index < section.num_funcs; function_index++) {
|
||||||
|
const FuncEntry& func = section.funcs[function_index];
|
||||||
|
func_map[ram + func.offset] = func.func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int32_t section_addresses[num_sections];
|
int32_t section_addresses[num_sections];
|
||||||
|
@ -128,6 +135,8 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_patch_functions();
|
||||||
|
|
||||||
void init_overlays() {
|
void init_overlays() {
|
||||||
for (size_t section_index = 0; section_index < num_code_sections; section_index++) {
|
for (size_t section_index = 0; section_index < num_code_sections; section_index++) {
|
||||||
section_addresses[section_table[section_index].index] = section_table[section_index].ram_addr;
|
section_addresses[section_table[section_index].index] = section_table[section_index].ram_addr;
|
||||||
|
@ -139,6 +148,8 @@ void init_overlays() {
|
||||||
return a.rom_addr < b.rom_addr;
|
return a.rom_addr < b.rom_addr;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
load_patch_functions();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" recomp_func_t * get_function(int32_t addr) {
|
extern "C" recomp_func_t * get_function(int32_t addr) {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include "recomp.h"
|
||||||
|
#include "../RecompiledPatches/recomp_overlays.inl"
|
||||||
|
|
||||||
|
void load_special_overlay(const SectionTableEntry& section, int32_t ram);
|
||||||
|
|
||||||
|
void load_patch_functions() {
|
||||||
|
load_special_overlay(section_table[0], section_table[0].ram_addr);
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer() {
|
std::unique_ptr<Rml::EventListenerInstancer> recomp::make_event_listener_instancer() {
|
||||||
std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>();
|
std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>();
|
||||||
|
|
||||||
ret->register_event("start_game",
|
ret->register_event("start_game",
|
||||||
|
|
|
@ -579,7 +579,7 @@ public:
|
||||||
struct {
|
struct {
|
||||||
struct UIRenderContext render;
|
struct UIRenderContext render;
|
||||||
class {
|
class {
|
||||||
std::unordered_map<Menu, Rml::ElementDocument*> documents;
|
std::unordered_map<recomp::Menu, Rml::ElementDocument*> documents;
|
||||||
Rml::ElementDocument* current_document;
|
Rml::ElementDocument* current_document;
|
||||||
public:
|
public:
|
||||||
SystemInterface_SDL system_interface;
|
SystemInterface_SDL system_interface;
|
||||||
|
@ -591,7 +591,7 @@ struct {
|
||||||
render_interface.reset();
|
render_interface.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void swap_document(Menu menu) {
|
void swap_document(recomp::Menu menu) {
|
||||||
if (current_document != nullptr) {
|
if (current_document != nullptr) {
|
||||||
current_document->Hide();
|
current_document->Hide();
|
||||||
}
|
}
|
||||||
|
@ -628,7 +628,7 @@ struct {
|
||||||
Rml::Factory::RegisterEventListenerInstancer(event_listener_instancer.get());
|
Rml::Factory::RegisterEventListenerInstancer(event_listener_instancer.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
documents.emplace(Menu::Launcher, context->LoadDocument("assets/launcher.rml"));
|
documents.emplace(recomp::Menu::Launcher, context->LoadDocument("assets/launcher.rml"));
|
||||||
}
|
}
|
||||||
} rml;
|
} rml;
|
||||||
} UIContext;
|
} UIContext;
|
||||||
|
@ -645,7 +645,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||||
// Setup RML
|
// Setup RML
|
||||||
UIContext.rml.system_interface.SetWindow(window);
|
UIContext.rml.system_interface.SetWindow(window);
|
||||||
UIContext.rml.render_interface = std::make_unique<RmlRenderInterface_RT64>(&UIContext.render);
|
UIContext.rml.render_interface = std::make_unique<RmlRenderInterface_RT64>(&UIContext.render);
|
||||||
UIContext.rml.event_listener_instancer = make_event_listener_instancer();
|
UIContext.rml.event_listener_instancer = recomp::make_event_listener_instancer();
|
||||||
|
|
||||||
Rml::SetSystemInterface(&UIContext.rml.system_interface);
|
Rml::SetSystemInterface(&UIContext.rml.system_interface);
|
||||||
Rml::SetRenderInterface(UIContext.rml.render_interface.get());
|
Rml::SetRenderInterface(UIContext.rml.render_interface.get());
|
||||||
|
@ -685,15 +685,15 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||||
|
|
||||||
moodycamel::ConcurrentQueue<SDL_Event> ui_event_queue{};
|
moodycamel::ConcurrentQueue<SDL_Event> ui_event_queue{};
|
||||||
|
|
||||||
void queue_event(const SDL_Event& event) {
|
void recomp::queue_event(const SDL_Event& event) {
|
||||||
ui_event_queue.enqueue(event);
|
ui_event_queue.enqueue(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_deque_event(SDL_Event& out) {
|
bool recomp::try_deque_event(SDL_Event& out) {
|
||||||
return ui_event_queue.try_dequeue(out);
|
return ui_event_queue.try_dequeue(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<Menu> open_menu = Menu::Launcher;
|
std::atomic<recomp::Menu> open_menu = recomp::Menu::Launcher;
|
||||||
|
|
||||||
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) {
|
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) {
|
||||||
int num_keys;
|
int num_keys;
|
||||||
|
@ -704,12 +704,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||||
bool reload_sheets = is_reload_held && !was_reload_held;
|
bool reload_sheets = is_reload_held && !was_reload_held;
|
||||||
was_reload_held = is_reload_held;
|
was_reload_held = is_reload_held;
|
||||||
|
|
||||||
static Menu prev_menu = Menu::None;
|
static recomp::Menu prev_menu = recomp::Menu::None;
|
||||||
Menu cur_menu = open_menu.load();
|
recomp::Menu cur_menu = open_menu.load();
|
||||||
|
|
||||||
if (reload_sheets) {
|
if (reload_sheets) {
|
||||||
UIContext.rml.load_documents();
|
UIContext.rml.load_documents();
|
||||||
prev_menu = Menu::None;
|
prev_menu = recomp::Menu::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur_menu != prev_menu) {
|
if (cur_menu != prev_menu) {
|
||||||
|
@ -720,11 +720,11 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||||
|
|
||||||
SDL_Event cur_event{};
|
SDL_Event cur_event{};
|
||||||
|
|
||||||
while (try_deque_event(cur_event)) {
|
while (recomp::try_deque_event(cur_event)) {
|
||||||
RmlSDL::InputEventHandler(UIContext.rml.context, cur_event);
|
RmlSDL::InputEventHandler(UIContext.rml.context, cur_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur_menu != Menu::None) {
|
if (cur_menu != recomp::Menu::None) {
|
||||||
int width, height;
|
int width, height;
|
||||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||||
|
|
||||||
|
@ -753,11 +753,11 @@ void set_rt64_hooks() {
|
||||||
RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook);
|
RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_current_menu(Menu menu) {
|
void recomp::set_current_menu(Menu menu) {
|
||||||
open_menu.store(menu);
|
open_menu.store(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_ui() {
|
void recomp::destroy_ui() {
|
||||||
Rml::Shutdown();
|
Rml::Shutdown();
|
||||||
UIContext.rml.unload();
|
UIContext.rml.unload();
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,7 +292,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
destroy_ui();
|
recomp::destroy_ui();
|
||||||
// TODO restore this call once the RT64 shutdown issue is fixed.
|
// TODO restore this call once the RT64 shutdown issue is fixed.
|
||||||
// RT64Shutdown();
|
// RT64Shutdown();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue