From ec23ef02fde098021f49f2e0dc4fdbaa9ee77ab1 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Fri, 24 Nov 2023 17:10:21 -0500 Subject: [PATCH] Added some more patch functionality, added recomp namespace --- CMakeLists.txt | 6 +- include/recomp_helpers.h | 6 +- include/recomp_input.h | 63 +++++++++++ include/recomp_ui.h | 23 ++-- patches/input.c | 117 ++++++++++++++++++++- patches/input.h | 11 +- patches/patches.h | 2 + patches/print.c | 18 ++++ patches/syms.ld | 6 +- src/game/controls.cpp | 163 +++++++++++++++++++++++++++++ src/game/input.cpp | 197 +++++++++++++++++++++++++++++++++++ src/main/main.cpp | 179 +------------------------------ src/recomp/cont.cpp | 13 --- src/recomp/overlays.cpp | 11 ++ src/recomp/patch_loading.cpp | 11 ++ src/ui/ui_events.cpp | 2 +- src/ui/ui_renderer.cpp | 28 ++--- ultramodern/events.cpp | 2 +- 18 files changed, 628 insertions(+), 230 deletions(-) create mode 100644 include/recomp_input.h create mode 100644 patches/print.c create mode 100644 src/game/controls.cpp create mode 100644 src/game/input.cpp create mode 100644 src/recomp/patch_loading.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c5e370..82450a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,7 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/recomp/flash.cpp ${CMAKE_SOURCE_DIR}/src/recomp/math_routines.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/pi.cpp ${CMAKE_SOURCE_DIR}/src/recomp/ultra_stubs.cpp @@ -102,8 +103,11 @@ set (SOURCES ${CMAKE_SOURCE_DIR}/src/recomp/rt64_layer.cpp ${CMAKE_SOURCE_DIR}/src/recomp/sp.cpp ${CMAKE_SOURCE_DIR}/src/recomp/vi.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_events.cpp diff --git a/include/recomp_helpers.h b/include/recomp_helpers.h index ef9221e..cfadf81 100644 --- a/include/recomp_helpers.h +++ b/include/recomp_helpers.h @@ -2,6 +2,7 @@ #define __RECOMP_HELPERS__ #include "recomp.h" +#include "../ultramodern/ultra64.h" template T _arg(uint8_t* rdram, recomp_context* ctx) { @@ -13,7 +14,10 @@ T _arg(uint8_t* rdram, recomp_context* ctx) { return ctx->f12.fl; } else { - return std::bit_cast(raw_arg); + // static_assert in else workaround + [] () { + static_assert(flag, "Floats in a2/a3 not supported"); + }(); } } else if constexpr (std::is_pointer_v) { diff --git a/include/recomp_input.h b/include/recomp_input.h new file mode 100644 index 0000000..ed116c8 --- /dev/null +++ b/include/recomp_input.h @@ -0,0 +1,63 @@ +#ifndef __RECOMP_INPUT_H__ +#define __RECOMP_INPUT_H__ + +#include +#include +#include +#include + +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; + + 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 get_input_states(); + void handle_events(); +} + +#endif diff --git a/include/recomp_ui.h b/include/recomp_ui.h index c678418..d01c1fc 100644 --- a/include/recomp_ui.h +++ b/include/recomp_ui.h @@ -5,22 +5,25 @@ #include "SDL.h" -void queue_event(const SDL_Event& event); -bool try_deque_event(SDL_Event& out); - namespace Rml { class ElementDocument; class EventListenerInstancer; } -std::unique_ptr make_event_listener_instancer(); +namespace recomp { -enum class Menu { - Launcher, - None -}; + void queue_event(const SDL_Event& event); + bool try_deque_event(SDL_Event& out); -void set_current_menu(Menu menu); -void destroy_ui(); + std::unique_ptr make_event_listener_instancer(); + + enum class Menu { + Launcher, + None + }; + + void set_current_menu(Menu menu); + void destroy_ui(); +} #endif diff --git a/patches/input.c b/patches/input.c index addb681..f269427 100644 --- a/patches/input.c +++ b/patches/input.c @@ -8,17 +8,126 @@ u32 sPlayerItemButtons[] = { 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 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++) { - if (CHECK_BTN_ALL(cur_inputs.buttons, sPlayerItemButtons[i])) { + if (CHECK_BTN_ALL(pressed_item_buttons, sPlayerItemButtons[i])) { break; } } 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); +} +*/ diff --git a/patches/input.h b/patches/input.h index 6ef1c1a..4753ad0 100644 --- a/patches/input.h +++ b/patches/input.h @@ -11,13 +11,6 @@ extern "C" { #endif -typedef struct RecompInputs { - u32 buttons; - float x; - float y; -} RecompInputs; - - #ifdef MIPS # define DECLARE_FUNC(type, name, ...) \ type name(__VA_ARGS__); @@ -26,7 +19,9 @@ typedef struct RecompInputs { void name(uint8_t* rdram, recomp_context* ctx); #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 } diff --git a/patches/patches.h b/patches/patches.h index 1e41569..a045534 100644 --- a/patches/patches.h +++ b/patches/patches.h @@ -3,4 +3,6 @@ #include "global.h" +int recomp_printf(const char* fmt, ...); + #endif diff --git a/patches/print.c b/patches/print.c new file mode 100644 index 0000000..f118d6a --- /dev/null +++ b/patches/print.c @@ -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; +} diff --git a/patches/syms.ld b/patches/syms.ld index 5152687..a4eceae 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -2,6 +2,7 @@ __start = 0x80000000; /* Dummy addresses that get recompiled into function calls */ recomp_get_item_inputs = 0x81000000; +recomp_puts = 0x81000004; /* TODO pull these symbols from the elf file directly */ Player_PostLimbDrawGameplay = 0x80128BD0; @@ -10,4 +11,7 @@ gRegEditor = 0x801f3f60; Audio_PlaySfx = 0x8019f0c8; gSaveContext = 0x801ef670; Interface_SetHudVisibility = 0x8010ef68; -Player_GetItemOnButton = 0x8012364C; +PadMgr_GetInput = 0x80175f98; +Inventory_GetBtnBItem = 0x8012ec80; +gItemSlots = 0x801c2078; +_Printf = 0x8008e050; diff --git a/src/game/controls.cpp b/src/game/controls.cpp new file mode 100644 index 0000000..136d77c --- /dev/null +++ b/src/game/controls.cpp @@ -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; + +struct GameControllerButtonMapping { + uint32_t button; + uint32_t output_mask; +}; +using button_map_t = std::vector; + +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 input_states = recomp::get_input_states(); + + for (const InputState& state : input_states) { + if (const auto* controller_state = std::get_if(&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(&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 input_states = recomp::get_input_states(); + + for (const recomp::InputState& state : input_states) { + if (const auto* controller_state = std::get_if(&state)) { + cur_buttons |= process_controller_mappings(*controller_state, item_axis_map, item_button_map); + } + else if (const auto* mouse_state = std::get_if(&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); + } +} diff --git a/src/game/input.cpp b/src/game/input.cpp new file mode 100644 index 0000000..fce85c7 --- /dev/null +++ b/src/game/input.cpp @@ -0,0 +1,197 @@ +#include + +#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 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::get_input_states() { + std::vector 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 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 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(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> 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]); +} diff --git a/src/main/main.cpp b/src/main/main.cpp index 3e68637..d8b1b82 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -15,6 +15,7 @@ #endif #include "recomp_ui.h" +#include "recomp_input.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -33,120 +34,6 @@ void exit_error(const char* str, Ts ...args) { std::quick_exit(EXIT_FAILURE); } - -std::vector> 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 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 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 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() { SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "system"); 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*) { - // 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; + recomp::handle_events(); } static SDL_AudioDeviceID audio_device = 0; @@ -357,7 +184,7 @@ int main(int argc, char** argv) { }; ultramodern::input_callbacks_t input_callbacks{ - .get_input = get_input, + .get_input = recomp::get_n64_input, }; ultramodern::start({}, audio_callbacks, input_callbacks, gfx_callbacks); diff --git a/src/recomp/cont.cpp b/src/recomp/cont.cpp index 6dbe26d..bac45ed 100644 --- a/src/recomp/cont.cpp +++ b/src/recomp/cont.cpp @@ -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) { ; } - -#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; - } -} diff --git a/src/recomp/overlays.cpp b/src/recomp/overlays.cpp index aaf7a08..7e343bb 100644 --- a/src/recomp/overlays.cpp +++ b/src/recomp/overlays.cpp @@ -35,6 +35,13 @@ void load_overlay(size_t section_table_index, int32_t 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" { 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() { 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; @@ -139,6 +148,8 @@ void init_overlays() { return a.rom_addr < b.rom_addr; } ); + + load_patch_functions(); } extern "C" recomp_func_t * get_function(int32_t addr) { diff --git a/src/recomp/patch_loading.cpp b/src/recomp/patch_loading.cpp new file mode 100644 index 0000000..541a18b --- /dev/null +++ b/src/recomp/patch_loading.cpp @@ -0,0 +1,11 @@ +#include +#include +#include +#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); +} diff --git a/src/ui/ui_events.cpp b/src/ui/ui_events.cpp index 51a4b0c..0152055 100644 --- a/src/ui/ui_events.cpp +++ b/src/ui/ui_events.cpp @@ -36,7 +36,7 @@ public: } }; -std::unique_ptr make_event_listener_instancer() { +std::unique_ptr recomp::make_event_listener_instancer() { std::unique_ptr ret = std::make_unique(); ret->register_event("start_game", diff --git a/src/ui/ui_renderer.cpp b/src/ui/ui_renderer.cpp index b4ae510..641bc56 100644 --- a/src/ui/ui_renderer.cpp +++ b/src/ui/ui_renderer.cpp @@ -579,7 +579,7 @@ public: struct { struct UIRenderContext render; class { - std::unordered_map documents; + std::unordered_map documents; Rml::ElementDocument* current_document; public: SystemInterface_SDL system_interface; @@ -591,7 +591,7 @@ struct { render_interface.reset(); } - void swap_document(Menu menu) { + void swap_document(recomp::Menu menu) { if (current_document != nullptr) { current_document->Hide(); } @@ -628,7 +628,7 @@ struct { 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; } UIContext; @@ -645,7 +645,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { // Setup RML UIContext.rml.system_interface.SetWindow(window); UIContext.rml.render_interface = std::make_unique(&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::SetRenderInterface(UIContext.rml.render_interface.get()); @@ -685,15 +685,15 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { moodycamel::ConcurrentQueue ui_event_queue{}; -void queue_event(const SDL_Event& event) { +void recomp::queue_event(const SDL_Event& 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); } -std::atomic open_menu = Menu::Launcher; +std::atomic open_menu = recomp::Menu::Launcher; void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) { 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; was_reload_held = is_reload_held; - static Menu prev_menu = Menu::None; - Menu cur_menu = open_menu.load(); + static recomp::Menu prev_menu = recomp::Menu::None; + recomp::Menu cur_menu = open_menu.load(); if (reload_sheets) { UIContext.rml.load_documents(); - prev_menu = Menu::None; + prev_menu = recomp::Menu::None; } if (cur_menu != prev_menu) { @@ -720,11 +720,11 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_ SDL_Event cur_event{}; - while (try_deque_event(cur_event)) { + while (recomp::try_deque_event(cur_event)) { RmlSDL::InputEventHandler(UIContext.rml.context, cur_event); } - if (cur_menu != Menu::None) { + if (cur_menu != recomp::Menu::None) { int width, height; SDL_GetWindowSizeInPixels(window, &width, &height); @@ -753,11 +753,11 @@ void set_rt64_hooks() { 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); } -void destroy_ui() { +void recomp::destroy_ui() { Rml::Shutdown(); UIContext.rml.unload(); } diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp index 0fe6342..8e79973 100644 --- a/ultramodern/events.cpp +++ b/ultramodern/events.cpp @@ -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. // RT64Shutdown(); }