From ddd5f16b87579770aa6332c94304cc92136c0b0e Mon Sep 17 00:00:00 2001 From: jugeeya <jugeeya@live.com> Date: Mon, 14 Aug 2023 11:16:15 -0700 Subject: [PATCH] Initial --- ryujinx_build.ps1 | 3 + src/common/button_config.rs | 107 +++++++ src/common/mod.rs | 13 +- src/static/layout.arc | Bin 2474768 -> 2478864 bytes src/training/input_log.rs | 23 ++ src/training/input_record.rs | 13 +- src/training/input_recording/mod.rs | 2 - src/training/input_recording/structures.rs | 307 --------------------- src/training/mod.rs | 22 +- src/training/ui/input_log.rs | 69 +++++ src/training/ui/mod.rs | 2 + 11 files changed, 248 insertions(+), 313 deletions(-) create mode 100644 src/training/input_log.rs delete mode 100644 src/training/input_recording/mod.rs delete mode 100644 src/training/input_recording/structures.rs create mode 100644 src/training/ui/input_log.rs diff --git a/ryujinx_build.ps1 b/ryujinx_build.ps1 index 8d602a1..ba78ce7 100644 --- a/ryujinx_build.ps1 +++ b/ryujinx_build.ps1 @@ -1,5 +1,8 @@ $IP=(Test-Connection -ComputerName (hostname) -Count 1 | Select -ExpandProperty IPV4Address).IPAddressToString cargo skyline build --release --features layout_arc_from_file +if (($lastexitcode -ne 0)) { + exit $lastexitcode +} # Set up symlinks $RYUJINX_LAYOUT_ARC_PATH="C:\Users\Josh\AppData\Roaming\Ryujinx\sdcard\ultimate\TrainingModpack\layout.arc" diff --git a/src/common/button_config.rs b/src/common/button_config.rs index 8c6508d..2da2745 100644 --- a/src/common/button_config.rs +++ b/src/common/button_config.rs @@ -46,6 +46,113 @@ pub fn button_mapping( } } +pub fn name_to_font_glyph(button: ButtonConfig, style: ControllerStyle) -> Option<u16> { + let is_gcc = style == ControllerStyle::GCController; + Some(match button { + ButtonConfig::A => 0xE0E0, + ButtonConfig::B => 0xE0E1, + ButtonConfig::X => { + if is_gcc { + 0xE206 + } else { + 0xE0E2 + } + } + ButtonConfig::Y => { + if is_gcc { + 0xE207 + } else { + 0xE0E3 + } + } + ButtonConfig::L => { + if is_gcc { + return None; + } else { + 0xE0E4 + } + } + ButtonConfig::R => { + if is_gcc { + 0xE205 + } else { + 0xE0E5 + } + } + ButtonConfig::ZL => { + if is_gcc { + 0xE204 + } else { + 0xE0E6 + } + } + ButtonConfig::ZR => { + if is_gcc { + 0xE208 + } else { + 0xE0E7 + } + } + ButtonConfig::DPAD_UP => { + if is_gcc { + 0xE209 + } else { + 0xE0EB + } + } + ButtonConfig::DPAD_DOWN => { + if is_gcc { + 0xE20A + } else { + 0xE0EC + } + } + ButtonConfig::DPAD_LEFT => { + if is_gcc { + 0xE20B + } else { + 0xE0ED + } + } + ButtonConfig::DPAD_RIGHT => { + if is_gcc { + 0xE20C + } else { + 0xE0EE + } + } + ButtonConfig::PLUS => { + if is_gcc { + 0xE20D + } else { + 0xE0EF + } + } + ButtonConfig::MINUS => { + if is_gcc { + return None; + } else { + 0xE0F0 + } + } + ButtonConfig::LSTICK => { + if is_gcc { + return None; + } else { + 0xE104 + } + } + ButtonConfig::RSTICK => { + if is_gcc { + return None; + } else { + 0xE105 + } + } + _ => return None, + }) +} + #[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)] pub enum ButtonCombo { OpenMenu, diff --git a/src/common/mod.rs b/src/common/mod.rs index e916512..17b8a51 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -41,14 +41,25 @@ pub fn is_emulator() -> bool { } pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor { + try_get_module_accessor(fighter_id).unwrap() +} + +pub fn try_get_module_accessor( + fighter_id: FighterId, +) -> Option<*mut app::BattleObjectModuleAccessor> { let entry_id_int = fighter_id as i32; let entry_id = app::FighterEntryID(entry_id_int); unsafe { let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager); let fighter_entry = FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry; + if fighter_entry.is_null() { + return None; + } let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry); - app::sv_battle_object::module_accessor(current_fighter_id as u32) + Some(app::sv_battle_object::module_accessor( + current_fighter_id as u32, + )) } } diff --git a/src/static/layout.arc b/src/static/layout.arc index a3c97e13d50b9b73bea81b1fec0a484c31f504e5..130ff5d9143dda4e1c007825c18b19678e4d44ef 100644 GIT binary patch delta 2716 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 PQ&4VGP}!!SDzF#;Wz+=t delta 2292 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 RK&j0@xy?Xjn}MppVgUFihnxTa diff --git a/src/training/input_log.rs b/src/training/input_log.rs new file mode 100644 index 0000000..6d030ee --- /dev/null +++ b/src/training/input_log.rs @@ -0,0 +1,23 @@ +use std::collections::VecDeque; + +use crate::common::input::*; +use lazy_static::lazy_static; +use parking_lot::Mutex; + +lazy_static! { + pub static ref P1_INPUT_MAPPINGS: Mutex<VecDeque<MappedInputs>> = Mutex::new(VecDeque::new()); +} + +// TODO: how many +const NUM_INPUTS: usize = 120; + +pub fn handle_final_input_mapping(player_idx: i32, out: *mut MappedInputs) { + unsafe { + if player_idx == 0 { + let mut mappings = P1_INPUT_MAPPINGS.lock(); + + mappings.push_front(*out); + mappings.truncate(NUM_INPUTS); + } + } +} diff --git a/src/training/input_record.rs b/src/training/input_record.rs index b59b9e2..9bba2a2 100644 --- a/src/training/input_record.rs +++ b/src/training/input_record.rs @@ -1,7 +1,9 @@ use crate::common::button_config; use crate::common::consts::{FighterId, HitstunPlayback, OnOff, RecordTrigger}; use crate::common::input::*; -use crate::common::{get_module_accessor, is_in_hitstun, is_in_shieldstun, MENU}; +use crate::common::{ + get_module_accessor, is_in_hitstun, is_in_shieldstun, try_get_module_accessor, MENU, +}; use crate::training::mash; use crate::training::ui::notifications::{clear_notifications, color_notification}; use lazy_static::lazy_static; @@ -434,7 +436,14 @@ unsafe fn set_cpu_controls(p_data: *mut *mut u8) { should_mash_playback(); } - let cpu_module_accessor = get_module_accessor(FighterId::CPU); + let cpu_module_accessor = try_get_module_accessor(FighterId::CPU); + + // Sometimes we can try to grab their module accessor before they are valid? + if cpu_module_accessor.is_none() { + return; + } + let cpu_module_accessor = cpu_module_accessor.unwrap(); + if INPUT_RECORD == Pause { match LOCKOUT_FRAME.cmp(&0) { Ordering::Greater => LOCKOUT_FRAME -= 1, diff --git a/src/training/input_recording/mod.rs b/src/training/input_recording/mod.rs deleted file mode 100644 index dd3a638..0000000 --- a/src/training/input_recording/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[macro_use] -pub mod structures; diff --git a/src/training/input_recording/structures.rs b/src/training/input_recording/structures.rs deleted file mode 100644 index ed21a04..0000000 --- a/src/training/input_recording/structures.rs +++ /dev/null @@ -1,307 +0,0 @@ -#![allow(dead_code)] // TODO: Yeah don't do this -use crate::common::events::smash_version; -use crate::common::release::CURRENT_VERSION; -use crate::training::save_states::SavedState; -use bitflags::bitflags; -use training_mod_consts::TrainingModpackMenu; - -use crate::default_save_state; -use crate::training::character_specific::steve; -use crate::training::charge::ChargeState; -use crate::training::save_states::SaveState::NoAction; - -// 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 - TODO: Is this a problem? What's the original order? -pub type ButtonBitfield = i32; // may need to actually implement? Not for now though - -/// Controller style declaring what kind of controller is being used -#[derive(PartialEq, Eq, Debug, Copy, Clone)] -#[repr(u32)] -pub enum ControllerStyle { - Handheld = 0x1, - DualJoycon = 0x2, - LeftJoycon = 0x3, - RightJoycon = 0x4, - ProController = 0x5, - DebugPad = 0x6, // probably - GCController = 0x7, -} - -#[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)] -#[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 -#[repr(C)] -pub struct Controller { - pub vtable: *const 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], - 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, - } - } -} - -// Final Structure containing all input recording slots, menu options, and save states. -// 5 Input Recording Slots should be fine for now for most mix up scenarios -// When loading a "scenario", we want to load all menu options (with maybe overrides in the config?), load savestate(s), and load input recording slots. -// If we have submenus for input recording slots, we need to get that info as well, and we want to apply saved damage from save states to the menu. -// Damage range seems to be saved in menu for range of damage, so that's taken care of with menu. - -#[derive(Clone)] -#[repr(C)] -pub struct Scenario { - pub record_slots: Vec<Vec<MappedInputs>>, - pub starting_statuses: Vec<i32>, - pub menu: TrainingModpackMenu, - pub save_states: Vec<SavedState>, - pub player_char: i32, // fighter_kind - pub cpu_char: i32, // fighter_kind - pub stage: i32, // index of stage, but -1 = random - pub title: String, - pub description: String, - pub mod_version: String, - pub smash_version: String, - // depending on version, we need to modify newly added menu options, so that regardless of their defaults they reflect the previous version to minimize breakage of old scenarios - // we may also add more scenario parts to the struct in the future etc. - // pub screenshot: image???? - // datetime? - // author? - // mirroring? -} - -impl Scenario { - pub fn default() -> Scenario { - Scenario { - record_slots: vec![vec![MappedInputs::default(); 600]; 5], - starting_statuses: vec![0], - menu: crate::common::consts::DEFAULTS_MENU, - save_states: vec![default_save_state!(); 5], - player_char: 0, - cpu_char: 0, - stage: -1, // index of stage, but -1 = random/any stage - title: "Scenario Title".to_string(), - description: "Description...".to_string(), - mod_version: CURRENT_VERSION.to_string(), - smash_version: smash_version(), - } - } -} diff --git a/src/training/mod.rs b/src/training/mod.rs index 7f9eb24..f1a6a0b 100644 --- a/src/training/mod.rs +++ b/src/training/mod.rs @@ -34,6 +34,7 @@ mod character_specific; mod fast_fall; mod full_hop; pub mod input_delay; +mod input_log; mod input_record; mod mash; mod reset; @@ -712,15 +713,34 @@ unsafe fn handle_final_input_mapping( controller_struct: &mut SomeControllerStruct, arg: bool, ) { - // go through the original mapping function first + // Order of hooks here REALLY matters. Tread lightly + + // Go through the original mapping function first original!()(mappings, player_idx, out, controller_struct, arg); if !is_training_mode() { return; } + + // Grab button input requests from player + // Non-mutable pull button_config::handle_final_input_mapping(player_idx, controller_struct); + + // Grab menu inputs from player + // Non-mutable pull menu::handle_final_input_mapping(player_idx, controller_struct, out); + + // Check if we should apply hot reload configs + // Non-mutable pull dev_config::handle_final_input_mapping(player_idx, controller_struct); + + // Potentially apply input delay + // MUTATES controller state input_delay::handle_final_input_mapping(player_idx, out); + + input_log::handle_final_input_mapping(player_idx, out); + + // Potentially apply input recording, thus with delay + // MUTATES controller state input_record::handle_final_input_mapping(player_idx, out); } diff --git a/src/training/ui/input_log.rs b/src/training/ui/input_log.rs new file mode 100644 index 0000000..2e80935 --- /dev/null +++ b/src/training/ui/input_log.rs @@ -0,0 +1,69 @@ +use skyline::nn::ui2d::*; +use smash::ui2d::{SmashPane, SmashTextBox}; +use training_mod_consts::ButtonConfig; + +use crate::{ + common::{ + button_config::name_to_font_glyph, + input::Buttons, + menu::{P1_CONTROLLER_STYLE, QUICK_MENU_ACTIVE}, + }, + training::input_log::P1_INPUT_MAPPINGS, +}; + +macro_rules! log_parent_fmt { + ($x:ident) => { + format!("TrModInputLog{}", $x).as_str() + }; +} + +pub unsafe fn draw(root_pane: &Pane) { + let log_number = 0; + let log_pane = root_pane + .find_pane_by_name_recursive(log_parent_fmt!(log_number)) + .unwrap(); + + // TODO: And menu option for input log is on + log_pane.set_visible(!QUICK_MENU_ACTIVE); + + let logs_ptr = P1_INPUT_MAPPINGS.data_ptr(); + if logs_ptr.is_null() { + return; + } + let logs = &*logs_ptr; + let first_log = logs.front(); + if first_log.is_none() { + return; + } + let first_log = first_log.unwrap(); + + let p1_style_ptr = P1_CONTROLLER_STYLE.data_ptr(); + if p1_style_ptr.is_null() { + return; + } + + let input_pane = log_pane + .find_pane_by_name_recursive("InputTxt") + .unwrap() + .as_textbox(); + + input_pane.set_text_string("NONE"); + + if first_log.buttons.contains(Buttons::ATTACK) { + let potential_font_glyph = name_to_font_glyph(ButtonConfig::A, *p1_style_ptr); + if let Some(font_glyph) = potential_font_glyph { + input_pane.set_text_string(""); + + let it = input_pane.text_buf as *mut u16; + input_pane.text_len = 1; + *it = font_glyph; + *(it.add(1)) = 0x0; + } + } + + log_pane + .find_pane_by_name_recursive("FrameTxt") + .unwrap() + .as_textbox() + .set_text_string(format!("{}", logs.len()).as_str()); +} diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs index e906910..50136a4 100644 --- a/src/training/ui/mod.rs +++ b/src/training/ui/mod.rs @@ -10,6 +10,7 @@ use crate::consts::LAYOUT_ARC_PATH; mod damage; mod display; +mod input_log; mod menu; pub mod notifications; @@ -38,6 +39,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) damage::draw(root_pane, &layout_name); if layout_name == "info_training" { + input_log::draw(root_pane); display::draw(root_pane); menu::draw(root_pane); }