diff --git a/Cargo.toml b/Cargo.toml index 64f050c..c73956a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ bitflags = "1.2.1" parking_lot = { version = "0.12.0", features = ["nightly"] } include-flate = "0.1.4" lazy_static = "1.4.0" +modular-bitfield = "0.11.2" owo-colors = "2.1.0" once_cell = "1.12.0" paste = "1.0" diff --git a/src/common/dev_config.rs b/src/common/dev_config.rs index f65c72f..93bf98e 100644 --- a/src/common/dev_config.rs +++ b/src/common/dev_config.rs @@ -3,9 +3,9 @@ use std::fs; use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Deserialize; -use skyline::nn::hid::NpadGcState; use toml; +use crate::common::input::*; use crate::consts::DEV_TOML_PATH; use crate::logging::info; @@ -55,17 +55,9 @@ impl DevConfig { } } -pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32) { - let a_press = 1 << 0; - let l_press = 1 << 6; - let r_press = 1 << 7; - let buttons; - unsafe { - buttons = (*state).Buttons; - } - - // Occurs on L+R+A - if (buttons & a_press > 0) && (buttons & l_press > 0) && (buttons & r_press > 0) { +pub fn handle_final_input_mapping(player_idx: i32, controller_struct: &mut SomeControllerStruct) { + let current_buttons = controller_struct.controller.current_buttons; + if player_idx == 0 && current_buttons.l() && current_buttons.r() && current_buttons.a() { let mut dev_config = DEV_CONFIG.lock(); *dev_config = DevConfig::load_from_toml(); } diff --git a/src/common/input.rs b/src/common/input.rs new file mode 100644 index 0000000..6ca7dd4 --- /dev/null +++ b/src/common/input.rs @@ -0,0 +1,291 @@ +#![allow(dead_code)] // TODO: Yeah don't do this +use bitflags::bitflags; +use modular_bitfield::{bitfield, specifiers::*}; + +// Need to define necesary structures here. Probably should move to consts or something. Realistically, should be in skyline smash prob tho. + +// Final final controls used for controlmodule +// can I actually derive these? +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct ControlModuleInternal { + pub vtable: *mut u8, + pub controller_index: i32, + pub buttons: Buttons, + pub stick_x: f32, + pub stick_y: f32, + pub padding: [f32; 2], + pub unk: [u32; 8], + pub clamped_lstick_x: f32, + pub clamped_lstick_y: f32, + pub padding2: [f32; 2], + pub clamped_rstick_x: f32, + pub clamped_rstick_y: f32, +} + +impl ControlModuleInternal { + pub fn _clear(&mut self) { + // Try to nullify controls so we can't control player 1 during recording + self.stick_x = 0.0; + self.stick_y = 0.0; + self.buttons = Buttons::empty(); + self.clamped_lstick_x = 0.0; + self.clamped_lstick_y = 0.0; + self.clamped_rstick_x = 0.0; + self.clamped_rstick_y = 0.0; + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct ControlModuleStored { + // Custom type for saving only necessary controls/not saving vtable + pub buttons: Buttons, + pub stick_x: f32, + pub stick_y: f32, + pub padding: [f32; 2], + pub unk: [u32; 8], + pub clamped_lstick_x: f32, + pub clamped_lstick_y: f32, + pub padding2: [f32; 2], + pub clamped_rstick_x: f32, + pub clamped_rstick_y: f32, +} + +/// Re-ordered bitfield the game uses for buttons +#[bitfield] +#[derive(Debug, Default, Copy, Clone)] +#[repr(C)] +pub struct ButtonBitfield { + pub dpad_up: bool, + pub dpad_right: bool, + pub dpad_down: bool, + pub dpad_left: bool, + pub x: bool, + pub a: bool, + pub b: bool, + pub y: bool, + pub l: bool, + pub r: bool, + pub zl: bool, + pub zr: bool, + pub left_sl: bool, + pub left_sr: bool, + pub right_sl: bool, + pub right_sr: bool, + pub stick_l: bool, + pub stick_r: bool, + pub plus: bool, + pub minus: bool, + pub l_up: bool, + pub l_right: bool, + pub l_down: bool, + pub l_left: bool, + pub r_up: bool, + pub r_right: bool, + pub r_down: bool, + pub r_left: bool, + pub real_digital_l: bool, + pub real_digital_r: bool, + pub unused: B2, +} + +/// Controller style declaring what kind of controller is being used +#[derive(PartialEq, Eq, Default, Debug, Copy, Clone)] +#[repr(u32)] +pub enum ControllerStyle { + #[default] + Handheld = 0x1, + DualJoycon = 0x2, + LeftJoycon = 0x3, + RightJoycon = 0x4, + ProController = 0x5, + DebugPad = 0x6, // probably + GCController = 0x7, +} + +#[derive(Debug, Default, Copy, Clone)] +#[repr(C)] +pub struct AutorepeatInfo { + field: [u8; 0x18], +} + +// Can map any of these over any button - what does this mean? +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InputKind { + Attack = 0x0, + Special = 0x1, + Jump = 0x2, + Guard = 0x3, + Grab = 0x4, + SmashAttack = 0x5, + AppealHi = 0xA, + AppealS = 0xB, + AppealLw = 0xC, + Unset = 0xD, +} + +// 0x50 Byte struct containing the information for controller mappings +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct ControllerMapping { + pub gc_l: InputKind, + pub gc_r: InputKind, + pub gc_z: InputKind, + pub gc_dup: InputKind, + pub gc_dlr: InputKind, + pub gc_ddown: InputKind, + pub gc_a: InputKind, + pub gc_b: InputKind, + pub gc_cstick: InputKind, + pub gc_y: InputKind, + pub gc_x: InputKind, + pub gc_rumble: bool, + pub gc_absmash: bool, + pub gc_tapjump: bool, + pub gc_sensitivity: u8, + // 0xF + pub pro_l: InputKind, + pub pro_r: InputKind, + pub pro_zl: InputKind, + pub pro_zr: InputKind, + pub pro_dup: InputKind, + pub pro_dlr: InputKind, + pub pro_ddown: InputKind, + pub pro_a: InputKind, + pub pro_b: InputKind, + pub pro_cstick: InputKind, + pub pro_x: InputKind, + pub pro_y: InputKind, + pub pro_rumble: bool, + pub pro_absmash: bool, + pub pro_tapjump: bool, + pub pro_sensitivity: u8, + // 0x1F + pub joy_shoulder: InputKind, + pub joy_zshoulder: InputKind, + pub joy_sl: InputKind, + pub joy_sr: InputKind, + pub joy_up: InputKind, + pub joy_right: InputKind, + pub joy_left: InputKind, + pub joy_down: InputKind, + pub joy_rumble: bool, + pub joy_absmash: bool, + pub joy_tapjump: bool, + pub joy_sensitivity: u8, + // 0x2B + pub _2b: u8, + pub _2c: u8, + pub _2d: u8, + pub _2e: u8, + pub _2f: u8, + pub _30: u8, + pub _31: u8, + pub _32: u8, + pub is_absmash: bool, + pub _34: [u8; 0x1C], +} + +//type Buttons = u32; // may need to actually implement (like label and such)? Not for now though +bitflags! { + pub struct Buttons: u32 { + const ATTACK = 0x1; + const SPECIAL = 0x2; + const JUMP = 0x4; + const GUARD = 0x8; + const CATCH = 0x10; + const SMASH = 0x20; + const JUMP_MINI = 0x40; + const CSTICK_ON = 0x80; + const STOCK_SHARE = 0x100; + const ATTACK_RAW = 0x200; + const APPEAL_HI = 0x400; + const SPECIAL_RAW = 0x800; + const APPEAL_LW = 0x1000; + const APPEAL_SL = 0x2000; + const APPEAL_SR = 0x4000; + const FLICK_JUMP = 0x8000; + const GUARD_HOLD = 0x10000; + const SPECIAL_RAW2 = 0x20000; + } +} + +// Controller class used internally by the game +#[derive(Debug, Default, Copy, Clone)] +#[repr(C)] +pub struct Controller { + pub vtable: u64, + pub current_buttons: ButtonBitfield, + pub previous_buttons: ButtonBitfield, + pub left_stick_x: f32, + pub left_stick_y: f32, + pub left_trigger: f32, + pub _left_padding: u32, + pub right_stick_x: f32, + pub right_stick_y: f32, + pub right_trigger: f32, + pub _right_padding: u32, + pub gyro: [f32; 4], + pub button_timespan: AutorepeatInfo, + pub lstick_timespan: AutorepeatInfo, + pub rstick_timespan: AutorepeatInfo, + pub just_down: ButtonBitfield, + pub just_release: ButtonBitfield, + pub autorepeat_keys: u32, + pub autorepeat_threshold: u32, + pub autorepeat_initial_press_threshold: u32, + pub style: ControllerStyle, + pub controller_id: u32, + pub primary_controller_color1: u32, + pub primary_controller_color2: u32, + pub secondary_controller_color1: u32, + pub secondary_controller_color2: u32, + pub led_pattern: u8, + pub button_autorepeat_initial_press: bool, + pub lstick_autorepeat_initial_press: bool, + pub rstick_autorepeat_initial_press: bool, + pub is_valid_controller: bool, + pub _x_b9: [u8; 2], + pub is_connected: bool, + pub is_left_connected: bool, + pub is_right_connected: bool, + pub is_wired: bool, + pub is_left_wired: bool, + pub is_right_wired: bool, + pub _x_c1: [u8; 3], + pub npad_number: u32, + pub _x_c8: [u8; 8], +} + +// SomeControllerStruct used in hooked function - need to ask blujay what this is again +#[repr(C)] +pub struct SomeControllerStruct { + padding: [u8; 0x10], + pub controller: &'static mut Controller, +} + +// Define struct used for final controller inputs +#[derive(Copy, Clone)] +#[repr(C)] +pub struct MappedInputs { + pub buttons: Buttons, + pub lstick_x: i8, + pub lstick_y: i8, + pub rstick_x: i8, + pub rstick_y: i8, +} + +impl MappedInputs { + // pub needed? + pub fn default() -> MappedInputs { + MappedInputs { + buttons: Buttons::empty(), + lstick_x: 0, + lstick_y: 0, + rstick_x: 0, + rstick_y: 0, + } + } +} diff --git a/src/common/menu.rs b/src/common/menu.rs index ebfdcd8..35ae78c 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -2,7 +2,7 @@ use std::fs; use lazy_static::lazy_static; use parking_lot::Mutex; -use skyline::nn::hid::{GetNpadStyleSet, NpadGcState}; +use skyline::nn::hid::GetNpadStyleSet; use training_mod_consts::MenuJsonStruct; use training_mod_tui::AppPage; @@ -10,12 +10,12 @@ use training_mod_tui::AppPage; use crate::common::*; use crate::consts::MENU_OPTIONS_PATH; use crate::events::{Event, EVENT_QUEUE}; +use crate::input::*; use crate::logging::*; // This is a special frame counter that will tick on draw() // We'll count how long the menu has been open pub static mut FRAME_COUNTER: u32 = 0; -const MENU_INPUT_WAIT_FRAMES: u32 = 30; const MENU_CLOSE_WAIT_FRAMES: u32 = 60; pub static mut QUICK_MENU_ACTIVE: bool = false; @@ -76,177 +76,6 @@ pub fn spawn_menu() { } } -pub struct ButtonPresses { - pub a: ButtonPress, - pub b: ButtonPress, - pub x: ButtonPress, - pub y: ButtonPress, - pub r: ButtonPress, - pub l: ButtonPress, - pub zr: ButtonPress, - pub zl: ButtonPress, - pub left: ButtonPress, - pub right: ButtonPress, - pub up: ButtonPress, - pub down: ButtonPress, -} - -pub struct ButtonPress { - pub prev_frame_is_pressed: bool, - pub is_pressed: bool, - pub lockout_frames: usize, -} - -impl ButtonPress { - pub fn read_press(&mut self) -> bool { - let is_pressed = self.is_pressed; - if self.is_pressed { - self.is_pressed = false; - if self.lockout_frames == 0 { - self.prev_frame_is_pressed = true; - self.lockout_frames = 10; - return true; - } - } - - if self.lockout_frames > 0 { - self.lockout_frames -= 1; - } - - self.prev_frame_is_pressed = is_pressed; - false - } -} - -pub static mut BUTTON_PRESSES: ButtonPresses = ButtonPresses { - a: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - b: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - x: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - y: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - r: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - l: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - zr: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - zl: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - left: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - right: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - up: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, - down: ButtonPress { - prev_frame_is_pressed: false, - is_pressed: false, - lockout_frames: 0, - }, -}; - -pub fn handle_get_npad_state(state: *mut NpadGcState, controller_id: *const u32) { - unsafe { - let update_count = (*state).updateCount; - let flags = (*state).Flags; - if QUICK_MENU_ACTIVE { - if (*state).Buttons & (1 << 0) > 0 { - BUTTON_PRESSES.a.is_pressed = true; - } - if (*state).Buttons & (1 << 1) > 0 { - BUTTON_PRESSES.b.is_pressed = true; - } - if (*state).Buttons & (1 << 2) > 0 { - BUTTON_PRESSES.x.is_pressed = true; - } - if (*state).Buttons & (1 << 3) > 0 { - BUTTON_PRESSES.y.is_pressed = true; - } - if (*state).Buttons & (1 << 6) > 0 { - BUTTON_PRESSES.l.is_pressed = true; - } - if (*state).Buttons & (1 << 7) > 0 { - BUTTON_PRESSES.r.is_pressed = true; - } - // Special case for frame-by-frame - if FRAME_COUNTER > MENU_INPUT_WAIT_FRAMES && (*state).Buttons & (1 << 8) > 0 { - BUTTON_PRESSES.zl.is_pressed = true; - } - if (*state).Buttons & (1 << 9) > 0 { - BUTTON_PRESSES.zr.is_pressed = true; - } - if (*state).Buttons & ((1 << 12) | (1 << 16)) > 0 { - BUTTON_PRESSES.left.is_pressed = true; - } - if (*state).Buttons & ((1 << 14) | (1 << 18)) > 0 { - BUTTON_PRESSES.right.is_pressed = true; - } - if (*state).Buttons & ((1 << 15) | (1 << 19)) > 0 { - BUTTON_PRESSES.down.is_pressed = true; - } - // Special case for "UP" in menu open button combo - if FRAME_COUNTER > MENU_INPUT_WAIT_FRAMES - && (*state).Buttons & ((1 << 13) | (1 << 17)) > 0 - { - BUTTON_PRESSES.up.is_pressed = true; - } - - // For digital triggers: these pressed 1/3 of the way mean we should consider a press - if controller_is_gcc(*controller_id) { - if (*state).LTrigger >= 0x2AAA { - BUTTON_PRESSES.l.is_pressed = true; - } - - if (*state).RTrigger >= 0x2AAA { - BUTTON_PRESSES.r.is_pressed = true; - } - } - - // If we're here, remove all other Npad presses... - // Should we exclude the home button? - (*state) = NpadGcState::default(); - (*state).updateCount = update_count; - (*state).Flags = flags; - } - } -} - lazy_static! { pub static ref QUICK_MENU_APP: Mutex<training_mod_tui::App<'static>> = Mutex::new( training_mod_tui::App::new(unsafe { ui_menu(MENU) }, unsafe { @@ -256,22 +85,26 @@ lazy_static! { ) }) ); + pub static ref P1_CONTROLLER_STATE: Mutex<Controller> = Mutex::new(Controller::default()); } -pub unsafe fn controller_is_gcc(controller_id: u32) -> bool { - let style_set = GetNpadStyleSet(&controller_id as *const _); - (style_set.flags & (1 << 5)) > 0 -} - -pub unsafe fn p1_controller_is_gcc() -> bool { - let p1_controller_id = crate::training::input_delay::p1_controller_id(); - controller_is_gcc(p1_controller_id) +pub fn handle_final_input_mapping( + player_idx: i32, + controller_struct: &mut SomeControllerStruct, + out: *mut MappedInputs, +) { + unsafe { + if QUICK_MENU_ACTIVE && player_idx == 0 { + *P1_CONTROLLER_STATE.lock() = *controller_struct.controller; + // If we're here, remove all other presses + *out = MappedInputs::default(); + } + } } pub unsafe fn quick_menu_loop() { loop { std::thread::sleep(std::time::Duration::from_secs(10)); - let button_presses = &mut BUTTON_PRESSES; let mut received_input = true; loop { std::thread::sleep(std::time::Duration::from_millis(16)); @@ -291,15 +124,16 @@ pub unsafe fn quick_menu_loop() { continue; } - let is_gcc = p1_controller_is_gcc(); + let p1_controller_state = *P1_CONTROLLER_STATE.data_ptr(); + let is_gcc = p1_controller_state.style == ControllerStyle::GCController; + let button_presses = p1_controller_state.just_down; let app = &mut *QUICK_MENU_APP.data_ptr(); - button_presses.a.read_press().then(|| { + button_presses.a().then(|| { app.on_a(); received_input = true; }); - let b_press = &mut button_presses.b; - b_press.read_press().then(|| { + button_presses.b().then(|| { received_input = true; if app.page != AppPage::SUBMENU { app.on_b() @@ -312,21 +146,21 @@ pub unsafe fn quick_menu_loop() { EVENT_QUEUE.push(Event::menu_open(menu_json)); } }); - button_presses.x.read_press().then(|| { + button_presses.x().then(|| { app.save_defaults(); received_input = true; }); - button_presses.y.read_press().then(|| { + button_presses.y().then(|| { app.reset_all_submenus(); received_input = true; }); - button_presses.l.read_press().then(|| { + button_presses.l().then(|| { if is_gcc { app.previous_tab(); } received_input = true; }); - button_presses.r.read_press().then(|| { + button_presses.r().then(|| { if is_gcc { app.next_tab(); } else { @@ -334,13 +168,13 @@ pub unsafe fn quick_menu_loop() { } received_input = true; }); - button_presses.zl.read_press().then(|| { + button_presses.zl().then(|| { if !is_gcc { app.previous_tab(); } received_input = true; }); - button_presses.zr.read_press().then(|| { + button_presses.zr().then(|| { if !is_gcc { app.next_tab(); } else { @@ -348,19 +182,19 @@ pub unsafe fn quick_menu_loop() { } received_input = true; }); - button_presses.left.read_press().then(|| { + button_presses.l_left().then(|| { app.on_left(); received_input = true; }); - button_presses.right.read_press().then(|| { + button_presses.l_right().then(|| { app.on_right(); received_input = true; }); - button_presses.up.read_press().then(|| { + button_presses.l_up().then(|| { app.on_up(); received_input = true; }); - button_presses.down.read_press().then(|| { + button_presses.l_down().then(|| { app.on_down(); received_input = true; }); diff --git a/src/common/mod.rs b/src/common/mod.rs index b38f8e5..ce7e947 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -10,6 +10,7 @@ pub mod button_config; pub mod consts; pub mod dev_config; pub mod events; +pub mod input; pub mod menu; pub mod raygun_printer; pub mod release; diff --git a/src/training/input_delay.rs b/src/training/input_delay.rs index 2a4fcac..5277d59 100644 --- a/src/training/input_delay.rs +++ b/src/training/input_delay.rs @@ -1,51 +1,30 @@ use std::collections::VecDeque; +use crate::common::input::*; use lazy_static::lazy_static; use parking_lot::Mutex; -use skyline::nn::hid::{GetNpadStyleSet, NpadGcState}; use crate::common::MENU; lazy_static! { - static ref P1_DELAYED_NPAD_STATES: Mutex<VecDeque<NpadGcState>> = Mutex::new(VecDeque::new()); + static ref P1_DELAYED_INPUT_MAPPINGS: Mutex<VecDeque<MappedInputs>> = + Mutex::new(VecDeque::new()); } -pub unsafe fn p1_controller_id() -> u32 { - let min_controller_id = (0..8) - .filter(|i| GetNpadStyleSet(i as *const _).flags != 0) - .min() - .unwrap_or(0); - - let handheld_id = 0x20; - if GetNpadStyleSet(&handheld_id as *const _).flags != 0 { - handheld_id - } else { - min_controller_id - } -} - -pub fn handle_get_npad_state(state: *mut NpadGcState, controller_id: *const u32) { +pub fn handle_final_input_mapping(player_idx: i32, out: *mut MappedInputs) { unsafe { - if crate::common::is_training_mode() && *controller_id == p1_controller_id() { - let mut delayed_states = P1_DELAYED_NPAD_STATES.lock(); - let actual_state = *state; + if player_idx == 0 { + let mut delayed_mappings = P1_DELAYED_INPUT_MAPPINGS.lock(); + let actual_mapping = *out; - if delayed_states.len() < MENU.input_delay.into_delay() as usize { - let update_count = (*state).updateCount; - let attributes = (*state).Flags; - *state = NpadGcState::default(); - (*state).updateCount = update_count; - (*state).Flags = attributes; - } else if let Some(delayed_state) = delayed_states.back() { - let update_count = (*state).updateCount; - let attributes = (*state).Flags; - *state = *delayed_state; - (*state).updateCount = update_count; - (*state).Flags = attributes; + if delayed_mappings.len() < MENU.input_delay.into_delay() as usize { + *out = MappedInputs::default(); + } else if let Some(delayed_mapping) = delayed_mappings.back() { + *out = *delayed_mapping; } - delayed_states.push_front(actual_state); - delayed_states.truncate(MENU.input_delay.into_delay() as usize); + delayed_mappings.push_front(actual_mapping); + delayed_mappings.truncate(MENU.input_delay.into_delay() as usize); } } } diff --git a/src/training/input_record.rs b/src/training/input_record.rs index 44558a9..a9cced4 100644 --- a/src/training/input_record.rs +++ b/src/training/input_record.rs @@ -1,7 +1,7 @@ use crate::common::button_config; use crate::common::consts::{FighterId, HitstunPlayback, OnOff}; +use crate::common::input::*; use crate::common::{get_module_accessor, is_in_hitstun, is_in_shieldstun, MENU}; -use crate::training::input_recording::structures::*; use crate::training::mash; use crate::training::ui::notifications::{clear_notifications, color_notification}; use lazy_static::lazy_static; @@ -372,18 +372,7 @@ pub unsafe fn is_end_standby() -> bool { lstick_movement || rstick_movement || buttons_pressed } -static FIM_OFFSET: usize = 0x17504a0; -// TODO: Should we define all of our offsets in one file? Should at least be a good start for changing to be based on ASM instructions -#[skyline::hook(offset = FIM_OFFSET)] -unsafe fn handle_final_input_mapping( - mappings: *mut ControllerMapping, - player_idx: i32, // Is this the player index, or plugged in controller index? Need to check, assuming player for now - is this 0 indexed or 1? - out: *mut MappedInputs, - controller_struct: &mut SomeControllerStruct, - arg: bool, -) { - // go through the original mapping function first - original!()(mappings, player_idx, out, controller_struct, arg); +pub unsafe fn handle_final_input_mapping(player_idx: i32, out: *mut MappedInputs) { if player_idx == 0 { // if player 1 if INPUT_RECORD == Record { @@ -524,5 +513,5 @@ extern "C" { } pub fn init() { - skyline::install_hooks!(set_cpu_controls, handle_final_input_mapping,); + skyline::install_hooks!(set_cpu_controls); } diff --git a/src/training/mod.rs b/src/training/mod.rs index d9d702e..e6bd3fc 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -1,5 +1,4 @@ use skyline::hooks::{getRegionAddress, InlineCtx, Region}; -use skyline::nn::hid::*; use skyline::nn::ro::LookupSymbol; use smash::app::{self, enSEType, lua_bind::*, utility}; use smash::lib::lua_const::*; @@ -11,6 +10,7 @@ use crate::common::{ is_training_mode, menu, FIGHTER_MANAGER_ADDR, ITEM_MANAGER_ADDR, STAGE_MANAGER_ADDR, }; use crate::hitbox_visualizer; +use crate::input::*; use crate::logging::*; use crate::training::character_specific::items; @@ -35,7 +35,6 @@ mod fast_fall; mod full_hop; pub mod input_delay; mod input_record; -mod input_recording; mod mash; mod reset; pub mod save_states; @@ -615,25 +614,30 @@ pub unsafe fn handle_reused_ui( original!()(fighter_data, param_2) } -#[allow(improper_ctypes)] -extern "C" { - fn add_nn_hid_hook(callback: fn(*mut NpadGcState, *const u32)); +static FIM_OFFSET: usize = 0x17504a0; +// TODO: Should we define all of our offsets in one file? Should at least be a good start for changing to be based on ASM instructions +#[skyline::hook(offset = FIM_OFFSET)] +unsafe fn handle_final_input_mapping( + mappings: *mut ControllerMapping, + player_idx: i32, // Is this the player index, or plugged in controller index? Need to check, assuming player for now - is this 0 indexed or 1? + out: *mut MappedInputs, + controller_struct: &mut SomeControllerStruct, + arg: bool, +) { + // go through the original mapping function first + original!()(mappings, player_idx, out, controller_struct, arg); + if !is_training_mode() { + return; + } + menu::handle_final_input_mapping(player_idx, controller_struct, out); + dev_config::handle_final_input_mapping(player_idx, controller_struct); + input_delay::handle_final_input_mapping(player_idx, out); + input_record::handle_final_input_mapping(player_idx, out); } pub fn training_mods() { info!("Applying training mods."); - // Input Mods - unsafe { - if let Some(_f) = (add_nn_hid_hook as *const ()).as_ref() { - add_nn_hid_hook(input_delay::handle_get_npad_state); - add_nn_hid_hook(menu::handle_get_npad_state); - add_nn_hid_hook(dev_config::handle_get_npad_state); - } else { - panic!("The NN-HID hook plugin could not be found and is required to add NRO hooks. Make sure libnn_hid_hook.nro is installed."); - } - } - unsafe { LookupSymbol( &mut FIGHTER_MANAGER_ADDR, @@ -705,6 +709,8 @@ pub fn training_mods() { handle_star_ko, // Clatter clatter::hook_start_clatter, + // Input + handle_final_input_mapping ); combo::init(); diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index 16df712..c9bc76d 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -6,7 +6,7 @@ use smash::ui2d::{SmashPane, SmashTextBox}; use training_mod_tui::gauge::GaugeState; use training_mod_tui::{App, AppPage}; -use crate::{common, common::menu::QUICK_MENU_ACTIVE}; +use crate::{common, common::menu::QUICK_MENU_ACTIVE, input::*}; pub static NUM_MENU_TEXT_OPTIONS: usize = 33; pub static _NUM_MENU_TABS: usize = 3; @@ -405,7 +405,8 @@ pub unsafe fn draw(root_pane: &Pane) { }; let tab_titles = [prev_tab, tab_selected, next_tab].map(|idx| app_tabs[idx]); - let is_gcc = common::menu::p1_controller_is_gcc(); + let is_gcc = + (*common::menu::P1_CONTROLLER_STATE.data_ptr()).style == ControllerStyle::GCController; let button_mapping = if is_gcc { GCC_BUTTON_MAPPING.clone() } else {