From 67ebe7ff64519320dc5b1022e8fbfd241575a7f8 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Sun, 2 Nov 2025 18:37:26 -0500 Subject: [PATCH 01/31] Add Dynamic Array Accessor --- src/dynamic_accessor.rs | 78 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 79 insertions(+) create mode 100644 src/dynamic_accessor.rs diff --git a/src/dynamic_accessor.rs b/src/dynamic_accessor.rs new file mode 100644 index 00000000..27fb59d9 --- /dev/null +++ b/src/dynamic_accessor.rs @@ -0,0 +1,78 @@ +use std::{ + ops::{Deref, DerefMut}, + ffi::{c_char, CStr}, + sync::OnceLock, +}; + +pub trait PushArray { + fn push(&mut self, item: T); +} + +pub struct DynamicArrayAccessor { + data: OnceLock>, + offset: usize, + count: usize, +} + +impl DynamicArrayAccessor { + pub const fn new(offset: usize, count: usize) -> Self { + Self { + data: OnceLock::new(), + offset, + count, + } + } + + fn init_data(&self) -> Vec { + unsafe { + let text = skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as *const u8; + let array = text.add(self.offset) as *const T; + let s: &[T]= std::slice::from_raw_parts(array, self.count); + s.to_vec() + } + } +} + +impl Deref for DynamicArrayAccessor { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + self.data.get_or_init(|| self.init_data()) + } +} + +impl DerefMut for DynamicArrayAccessor { + fn deref_mut(&mut self) -> &mut Self::Target { + self.data.get_or_init(|| self.init_data()); + self.data.get_mut().unwrap() + } +} + +impl PushArray<*const c_char> for DynamicArrayAccessor<*const c_char> { + fn push(&mut self, item: *const c_char) { + let data: &mut Vec<*const c_char> = self; + + let item = unsafe { + data.iter() + .find(|&&x| CStr::from_ptr(x) == CStr::from_ptr(item)) + .unwrap_or(&item) + }; + + data.push(*item); + } +} + +macro_rules! primitives { + ($($T:ty)*) => { + $( + impl PushArray<$T> for DynamicArrayAccessor<$T> { + fn push(&mut self, item: $T) { + let data: &mut Vec<$T> = self; + data.push(item); + } + } + )* + } +} + +primitives![i8 u8 i16 u16 i32 u32 i64 u64 f32 f64]; diff --git a/src/lib.rs b/src/lib.rs index d85aa01e..6048de60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,7 @@ mod params; mod runtime_reload; mod state_callback; mod static_accessor; +mod dynamic_accessor; mod unwind; mod utils; From 0b3fc2e2997b292178df2aa40299ae0a44b32c52 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Sun, 2 Nov 2025 22:11:38 -0500 Subject: [PATCH 02/31] Add some weapon related static vars --- src/cloning/weapons.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 7f7b8c5c..462c116e 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -1,12 +1,15 @@ use std::{ collections::BTreeMap, - sync::atomic::{AtomicBool, AtomicI32, Ordering}, + sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering}, + ffi::c_char, }; use locks::RwLock; use skyline::hooks::InlineCtx; use smashline::{skyline_smash::app::BattleObjectModuleAccessor, Hash40}; +use crate::dynamic_accessor::DynamicArrayAccessor; + pub struct NewAgent { pub old_owner_id: i32, pub owner_id: i32, @@ -27,6 +30,13 @@ pub static NEW_ARTICLES: RwLock>> = RwLock::new(BT pub static NEW_AGENTS: RwLock>> = RwLock::new(BTreeMap::new()); pub static IGNORE_NEW_AGENTS: AtomicBool = AtomicBool::new(false); +const ORIGINAL_ARTICLE_COUNT: usize = 0x267; +pub static ARTICLE_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_ARTICLE_COUNT); + +pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_ARTICLE_COUNT)); +pub static WEAPON_OWNER_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5188240, ORIGINAL_ARTICLE_COUNT)); +pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_ARTICLE_COUNT)); + pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); pub fn try_get_new_agent( From 855a8bab5e26829a6832d803e23bd922a777613f Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 4 Nov 2025 16:33:13 -0500 Subject: [PATCH 03/31] Added a clarifying comment --- src/dynamic_accessor.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dynamic_accessor.rs b/src/dynamic_accessor.rs index 27fb59d9..46a7cac2 100644 --- a/src/dynamic_accessor.rs +++ b/src/dynamic_accessor.rs @@ -48,6 +48,9 @@ impl DerefMut for DynamicArrayAccessor { } } +// 2+ weapons with the same name use the same pointer to that string. +// So, if someone is adding a new weapon with the same name, +// it'll also use the same pointer that the previous weapons use. impl PushArray<*const c_char> for DynamicArrayAccessor<*const c_char> { fn push(&mut self, item: *const c_char) { let data: &mut Vec<*const c_char> = self; From 3c2acc3889fa15d63d49477c3eeedd7486329474 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 4 Nov 2025 16:36:47 -0500 Subject: [PATCH 04/31] Changed get_file_weapon_name hook offset --- src/cloning/weapons.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 462c116e..b401af17 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -272,7 +272,7 @@ decl_hooks! { decl_hooks! { install_weapon_name_hooks => weapon_name_hook; - get_file_weapon_name(23, 22, 0x17e098c); + get_file_weapon_name(23, 22, 0x17e0890); normal_param_data(21, 27, 0x33b6830); map_collision_param_data(21, 2, 0x33b69f0); visibility_param_data(21, 2, 0x33b6d14); From 6cf3f7e726103147645457bea8924f68735e1946 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Wed, 5 Nov 2025 23:57:21 -0500 Subject: [PATCH 05/31] Update weapon hooks to use corresponding DynamicArrayAccessor --- src/cloning/weapons.rs | 45 ++++++------------------------------------ 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index b401af17..88e8c7d1 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -186,51 +186,18 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { } fn weapon_owner_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - if IGNORE_NEW_AGENTS.load(Ordering::Relaxed) { - return; - } - - let owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); - let Some(agent) = try_get_new_agent(&agents, unsafe { ctx.registers[source_register].x() as i32 }, owner) else { - return; - }; - - unsafe { - ctx.registers[dst_register].set_x(agent.owner_id as u64); - } + let index = ctx.registers[source_register].x(); + ctx.registers[dst_register].set_x(WEAPON_OWNER_KINDS.read()[index as usize] as u64); } fn weapon_owner_name_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - if IGNORE_NEW_AGENTS.load(Ordering::Relaxed) { - return; - } - - let owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); - let Some(agent) = try_get_new_agent(&agents, unsafe { ctx.registers[source_register].x() as i32 }, owner) else { - return; - }; - - unsafe { - ctx.registers[dst_register].set_x(agent.owner_name_ffi.as_ptr() as u64); - } + let index = ctx.registers[source_register].x(); + ctx.registers[dst_register].set_x(WEAPON_OWNER_NAMES.read()[index as usize] as u64); } fn weapon_name_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - if IGNORE_NEW_AGENTS.load(Ordering::Relaxed) { - return; - } - - let owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); - let Some(agent) = try_get_new_agent(&agents, unsafe { ctx.registers[source_register].x() as i32 }, owner) else { - return; - }; - - unsafe { - ctx.registers[dst_register].set_x(agent.new_name_ffi.as_ptr() as u64); - } + let index = ctx.registers[source_register].x(); + ctx.registers[dst_register].set_x(WEAPON_NAMES.read()[index as usize] as u64); } macro_rules! decl_hooks { From c78cfdcedeadb89fe669351fd03d55de6f22b6ec Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 6 Nov 2025 00:03:23 -0500 Subject: [PATCH 06/31] Added shift parameter to weapon hooks to fix incorrect index --- src/cloning/weapons.rs | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 88e8c7d1..2e222c23 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -185,27 +185,27 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { Box::leak(new_fighter_data) } -fn weapon_owner_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - let index = ctx.registers[source_register].x(); +fn weapon_owner_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { + let index = ctx.registers[source_register].x() >> shift; ctx.registers[dst_register].set_x(WEAPON_OWNER_KINDS.read()[index as usize] as u64); } -fn weapon_owner_name_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - let index = ctx.registers[source_register].x(); +fn weapon_owner_name_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { + let index = ctx.registers[source_register].x() >> shift; ctx.registers[dst_register].set_x(WEAPON_OWNER_NAMES.read()[index as usize] as u64); } -fn weapon_name_hook(ctx: &mut InlineCtx, source_register: usize, dst_register: usize) { - let index = ctx.registers[source_register].x(); +fn weapon_name_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { + let index = ctx.registers[source_register].x() >> shift; ctx.registers[dst_register].set_x(WEAPON_NAMES.read()[index as usize] as u64); } macro_rules! decl_hooks { - ($install_fn:ident => $func:expr; $($name:ident($src:expr, $dst:expr, $offset:expr));*) => { + ($install_fn:ident => $func:expr; $($name:ident($src:expr, $shift:expr, $dst:expr, $offset:expr));*) => { $( #[skyline::hook(offset = $offset, inline)] unsafe fn $name(ctx: &mut InlineCtx) { - $func(ctx, $src, $dst); + $func(ctx, $src, $shift, $dst); } )* @@ -221,32 +221,32 @@ macro_rules! decl_hooks { decl_hooks! { install_weapon_owner_hooks => weapon_owner_hook; - params(21, 26, 0x33b6628); - game_animcmd_owner(22, 8, 0x33acf78); - sound_animcmd_owner(22, 8, 0x33aee38); - effect_animcmd_owner(22, 8, 0x33aded8); - status_script_owner(22, 8, 0x33ac040) + params(21, 0, 26, 0x33b6628); + game_animcmd_owner(22, 0, 8, 0x33acf78); + sound_animcmd_owner(22, 0, 8, 0x33aee38); + effect_animcmd_owner(22, 0, 8, 0x33aded8); + status_script_owner(22, 0, 8, 0x33ac040) } decl_hooks! { install_weapon_owner_name_hooks => weapon_owner_name_hook; - get_file(26, 25, 0x17e0a4c); - game_animcmd_owner_name(8, 2, 0x33ace7c); - sound_animcmd_owner_name(8, 2, 0x33aed3c); - effect_animcmd_owner_name(8, 2, 0x33adddc); - status_script_owner_name(8, 2, 0x33abf54) + get_file(26, 0, 25, 0x17e0a4c); + game_animcmd_owner_name(8, 3, 2, 0x33ace7c); + sound_animcmd_owner_name(8, 3, 2, 0x33aed3c); + effect_animcmd_owner_name(8, 3, 2, 0x33adddc); + status_script_owner_name(8, 3, 2, 0x33abf54) } decl_hooks! { install_weapon_name_hooks => weapon_name_hook; - get_file_weapon_name(23, 22, 0x17e0890); - normal_param_data(21, 27, 0x33b6830); - map_collision_param_data(21, 2, 0x33b69f0); - visibility_param_data(21, 2, 0x33b6d14); - game_animcmd_weapon_name(8, 3, 0x33ace8c); - sound_animcmd_weapon_name(8, 3, 0x33aed4c); - effect_animcmd_weapon_name(8, 3, 0x33addec); - status_script_weapon_name(8, 3, 0x33abf64) + get_file_weapon_name(23, 0, 22, 0x17e0890); + normal_param_data(21, 0, 27, 0x33b6830); + map_collision_param_data(21, 0, 2, 0x33b69f0); + visibility_param_data(21, 0, 2, 0x33b6d14); + game_animcmd_weapon_name(8, 3, 3, 0x33ace8c); + sound_animcmd_weapon_name(8, 3, 3, 0x33aed4c); + effect_animcmd_weapon_name(8, 3, 3, 0x33addec); + status_script_weapon_name(8, 3, 3, 0x33abf64) } macro_rules! decl_hooks_kirby_get_kind { From bcb0ca23584817fddd3f52b8b00e52cb4cd07206 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 18 Nov 2025 01:30:48 -0500 Subject: [PATCH 07/31] Add get file category hook --- src/cloning/weapons.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 2e222c23..b1186ad2 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -351,13 +351,27 @@ unsafe fn kirby_get_copy_articles(ctx: &mut InlineCtx, store_reg: usize) { ctx.registers[store_reg].set_x(static_article_info as *const StaticArticleData as u64); } +// Just going to assume "fighter" when getting the file +// There's on;y 1 case where it's "enemy" instead +#[skyline::hook(offset = 0x17e09a8, inline)] +unsafe fn get_file_category(ctx: &mut InlineCtx) { + if ctx.registers[26].x() >= 0x267 { + use skyline::hooks; + + let text = hooks::getRegionAddress(hooks::Region::Text) as *const u8; + let fighter_string = text.add(0x4358c60) as *const c_char; + ctx.registers[25].set_x(fighter_string as u64); + } +} + pub fn install() { install_weapon_name_hooks(); install_weapon_owner_hooks(); install_weapon_owner_name_hooks(); skyline::install_hooks!( - get_static_fighter_data + get_static_fighter_data, + get_file_category, ); install_kirby_copy_kind_hooks(); From 6a0513451d88cfbb75ea73340b8d081e7b6dd1dd Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 18 Nov 2025 01:34:25 -0500 Subject: [PATCH 08/31] Add a vec for base weapon kinds --- src/cloning/weapons.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index b1186ad2..3f419ee7 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -36,6 +36,7 @@ pub static ARTICLE_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_ARTICLE_COUNT) pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_ARTICLE_COUNT)); pub static WEAPON_OWNER_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5188240, ORIGINAL_ARTICLE_COUNT)); pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_ARTICLE_COUNT)); +pub static BASE_WEAPON_KIND: RwLock> = RwLock::new(Vec::new()); pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); From 4c7b0970669a1e73e803283f1e7996a0108dd164 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 18 Nov 2025 01:41:31 -0500 Subject: [PATCH 09/31] Use const var --- src/cloning/weapons.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 3f419ee7..745f3dba 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -356,7 +356,7 @@ unsafe fn kirby_get_copy_articles(ctx: &mut InlineCtx, store_reg: usize) { // There's on;y 1 case where it's "enemy" instead #[skyline::hook(offset = 0x17e09a8, inline)] unsafe fn get_file_category(ctx: &mut InlineCtx) { - if ctx.registers[26].x() >= 0x267 { + if ctx.registers[26].x() >= ORIGINAL_ARTICLE_COUNT { use skyline::hooks; let text = hooks::getRegionAddress(hooks::Region::Text) as *const u8; From 08a457ab8d7a297871bf5924ae31b03a65ab7b36 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 18 Nov 2025 01:42:29 -0500 Subject: [PATCH 10/31] Add a hook for a weapon bone stuff thing --- src/cloning/weapons.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 745f3dba..b1d3ca03 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -1,7 +1,7 @@ use std::{ collections::BTreeMap, sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering}, - ffi::c_char, + ffi::{c_char, c_void}, }; use locks::RwLock; @@ -365,6 +365,20 @@ unsafe fn get_file_category(ctx: &mut InlineCtx) { } } +#[skyline::hook(offset = 0x33aa1e0)] +fn get_weapon_bone_stuff(kind: i32) -> *const c_void { + let k; + + if kind < ORIGINAL_ARTICLE_COUNT + || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { + kind + } else { + BASE_WEAPON_KIND.read()[(kind - 0x267) as usize] + } + + call_original!(k) +} + pub fn install() { install_weapon_name_hooks(); install_weapon_owner_hooks(); @@ -373,6 +387,7 @@ pub fn install() { skyline::install_hooks!( get_static_fighter_data, get_file_category, + get_weapon_bone_stuff, ); install_kirby_copy_kind_hooks(); From 2eba2c90a309c9d0ee4704f6991467ec55679bff Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 18 Nov 2025 01:44:02 -0500 Subject: [PATCH 11/31] Add weapon vtable hook --- src/cloning/weapons.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index b1d3ca03..23491840 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -379,6 +379,20 @@ fn get_weapon_bone_stuff(kind: i32) -> *const c_void { call_original!(k) } +#[skyline::hook(offset = 0x33be790)] +fn get_weapon_vtable(kind: i32) -> *const c_void { + let k; + + if kind < ORIGINAL_ARTICLE_COUNT + || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { + kind + } else { + BASE_WEAPON_KIND.read()[(kind - 0x267) as usize] + } + + call_original!(k) +} + pub fn install() { install_weapon_name_hooks(); install_weapon_owner_hooks(); @@ -388,6 +402,7 @@ pub fn install() { get_static_fighter_data, get_file_category, get_weapon_bone_stuff, + get_weapon_vtable, ); install_kirby_copy_kind_hooks(); From 4c4b7963794c5862472af34c8ae3866a213dd20d Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Tue, 25 Nov 2025 12:41:26 -0500 Subject: [PATCH 12/31] Update how to get name and owner name when creating agent --- src/create_agent.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/create_agent.rs b/src/create_agent.rs index 56a44ce1..e064cbb4 100644 --- a/src/create_agent.rs +++ b/src/create_agent.rs @@ -2,7 +2,9 @@ use std::{ borrow::BorrowMut, collections::{BTreeMap, HashMap}, ops::{Deref, DerefMut}, sync::{ atomic::{AtomicBool, Ordering}, Arc, - }, time::Duration + }, + time::Duration, + ffi::CStr, }; use acmd_engine::SmashlineScript; @@ -23,7 +25,7 @@ use smashline::{ use vtables::{CustomDataAccessError, VirtualClass}; use crate::{ - cloning::weapons::IGNORE_NEW_AGENTS, interpreter::LoadedScript, + cloning::weapons::{IGNORE_NEW_AGENTS, WEAPON_NAMES, WEAPON_OWNER_NAMES}, interpreter::LoadedScript, static_accessor::StaticArrayAccessor, callbacks::{CALLBACKS, StatusCallbackFunction} }; @@ -565,16 +567,6 @@ fn create_agent_hook( Some(agent) } BattleObjectCategory::Weapon => { - let Some(name) = crate::utils::get_weapon_name(object.kind) else { - // TODO: Warn - return original.call(object, boma, lua_state); - }; - - let Some(owner) = crate::utils::get_weapon_owner_name(object.kind) else { - // TODO: Warn - return original.call(object, boma, lua_state); - }; - let (agent, additional_module) = if let Some(agent) = original.call(object, boma, lua_state) { @@ -606,6 +598,9 @@ fn create_agent_hook( } }; + let name = unsafe { CStr::from_ptr(WEAPON_NAMES.read()[object.kind as usize]).to_str().unwrap() }; + let owner = unsafe { CStr::from_ptr(WEAPON_OWNER_NAMES.read()[object.kind as usize]).to_str().unwrap() }; + let qualified_name = format!("{owner}_{name}"); let hash = Hash40::new(&qualified_name); @@ -1065,14 +1060,6 @@ fn create_agent_status_weapon( boma: &mut BattleObjectModuleAccessor, lua_state: *mut lua_State, ) -> Option<&'static mut L2CFighterBase> { - let Some(name) = crate::utils::get_weapon_name(object.kind) else { - return call_original!(object, boma, lua_state); - }; - - let Some(owner_name) = crate::utils::get_weapon_owner_name(object.kind) else { - return call_original!(object, boma, lua_state); - }; - let (is_new, agent, additional_fighter) = if let Some(agent) = call_original!(object, boma, lua_state) { (false, agent, None) @@ -1123,6 +1110,9 @@ fn create_agent_status_weapon( .vtable_accessor_mut() .set_set_status_scripts(set_status_scripts); + let name = unsafe { CStr::from_ptr(WEAPON_NAMES.read()[object.kind as usize]).to_str().unwrap() }; + let owner_name = unsafe { CStr::from_ptr(WEAPON_OWNER_NAMES.read()[object.kind as usize]).to_str().unwrap() }; + let data = vtables::vtable_custom_data_mut::<_, L2CFighterWrapper>(wrapper.deref_mut()); data.hash = Hash40::new(&format!("{owner_name}_{name}")); data.kind = object.kind; From 52a9705080db3884deef502cea1505eb3accbeee Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Wed, 26 Nov 2025 14:34:03 -0500 Subject: [PATCH 13/31] Use const instead --- src/cloning/weapons.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 23491840..aa4bdd5e 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -373,7 +373,7 @@ fn get_weapon_bone_stuff(kind: i32) -> *const c_void { || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { kind } else { - BASE_WEAPON_KIND.read()[(kind - 0x267) as usize] + BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_ARTICLE_COUNT] } call_original!(k) @@ -387,7 +387,7 @@ fn get_weapon_vtable(kind: i32) -> *const c_void { || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { kind } else { - BASE_WEAPON_KIND.read()[(kind - 0x267) as usize] + BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_ARTICLE_COUNT] } call_original!(k) From 071fa6be923092f5d798c68653c7639ac385ce04 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Wed, 26 Nov 2025 18:30:48 -0500 Subject: [PATCH 14/31] Renamed weapon related vars --- src/cloning/weapons.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index aa4bdd5e..2e03dda8 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -30,12 +30,12 @@ pub static NEW_ARTICLES: RwLock>> = RwLock::new(BT pub static NEW_AGENTS: RwLock>> = RwLock::new(BTreeMap::new()); pub static IGNORE_NEW_AGENTS: AtomicBool = AtomicBool::new(false); -const ORIGINAL_ARTICLE_COUNT: usize = 0x267; -pub static ARTICLE_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_ARTICLE_COUNT); +const ORIGINAL_WEAPON_COUNT: usize = 0x267; +pub static WEAPON_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_WEAPON_COUNT); -pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_ARTICLE_COUNT)); -pub static WEAPON_OWNER_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5188240, ORIGINAL_ARTICLE_COUNT)); -pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_ARTICLE_COUNT)); +pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_WEAPON_COUNT)); +pub static WEAPON_OWNER_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5188240, ORIGINAL_WEAPON_COUNT)); +pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_WEAPON_COUNT)); pub static BASE_WEAPON_KIND: RwLock> = RwLock::new(Vec::new()); pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); @@ -356,7 +356,7 @@ unsafe fn kirby_get_copy_articles(ctx: &mut InlineCtx, store_reg: usize) { // There's on;y 1 case where it's "enemy" instead #[skyline::hook(offset = 0x17e09a8, inline)] unsafe fn get_file_category(ctx: &mut InlineCtx) { - if ctx.registers[26].x() >= ORIGINAL_ARTICLE_COUNT { + if ctx.registers[26].x() >= ORIGINAL_WEAPON_COUNT { use skyline::hooks; let text = hooks::getRegionAddress(hooks::Region::Text) as *const u8; @@ -369,11 +369,11 @@ unsafe fn get_file_category(ctx: &mut InlineCtx) { fn get_weapon_bone_stuff(kind: i32) -> *const c_void { let k; - if kind < ORIGINAL_ARTICLE_COUNT - || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { + if kind < ORIGINAL_WEAPON_COUNT + || kind >= WEAPON_COUNT.load(Ordering::Relaxed) { kind } else { - BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_ARTICLE_COUNT] + BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] } call_original!(k) @@ -383,11 +383,11 @@ fn get_weapon_bone_stuff(kind: i32) -> *const c_void { fn get_weapon_vtable(kind: i32) -> *const c_void { let k; - if kind < ORIGINAL_ARTICLE_COUNT - || kind >= ARTICLE_COUNT.load(Ordering::Relaxed) { + if kind < ORIGINAL_WEAPON_COUNT + || kind >= WEAPON_COUNT.load(Ordering::Relaxed) { kind } else { - BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_ARTICLE_COUNT] + BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] } call_original!(k) From 2447b52ada75cf436f0c858ae1861fb24ddf7a8e Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Wed, 26 Nov 2025 18:33:55 -0500 Subject: [PATCH 15/31] Mimic echo fighter weapons --- src/cloning/weapons.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 2e03dda8..beffe8f7 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -38,6 +38,8 @@ pub static WEAPON_OWNER_NAMES: RwLock> = RwL pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_WEAPON_COUNT)); pub static BASE_WEAPON_KIND: RwLock> = RwLock::new(Vec::new()); +pub static CURRENT_WEAPON_KIND: AtomicI32 = AtomicI32::new(-1); + pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); pub fn try_get_new_agent( @@ -393,6 +395,24 @@ fn get_weapon_vtable(kind: i32) -> *const c_void { call_original!(k) } +#[skyline::hook(offset = 0x33b5d10, inline)] +unsafe fn mimic_echo_weapon(ctx: &mut InlineCtx) { + let mut kind = ctx.registers[28].w(); + + CURRENT_WEAPON_KIND.store(kind as i32, Ordering::Relaxed); + + if kind >= ORIGINAL_WEAPON_COUNT { + kind = BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] as u32; + } + + ctx.registers[28].set_w(kind); +} + +#[skyline::hook(offset = 0x33b6528, inline)] +unsafe fn restore_weapon_kind(ctx: &mut InlineCtx) { + ctx.registers[28].set_w(CURRENT_WEAPON_KIND.load(Ordering::Relaxed) as u32); +} + pub fn install() { install_weapon_name_hooks(); install_weapon_owner_hooks(); @@ -403,6 +423,8 @@ pub fn install() { get_file_category, get_weapon_bone_stuff, get_weapon_vtable, + mimic_echo_weapon, + restore_weapon_kind, ); install_kirby_copy_kind_hooks(); From e3a0a7bc2d98e717028bd24dbfef64bd8d4dec02 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 27 Nov 2025 22:43:43 -0500 Subject: [PATCH 16/31] Removed ffi fields from NewAgent --- src/api.rs | 2 -- src/cloning/weapons.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/api.rs b/src/api.rs index b709ce30..1eff7cc4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -361,8 +361,6 @@ pub extern "C" fn smashline_clone_weapon( .push(NewAgent { old_owner_id: original_owner_id as i32, owner_id: new_owner_id as i32, - owner_name_ffi: format!("{new_owner}\0"), - new_name_ffi: format!("{new_name}\0"), owner_name: new_owner, new_name, old_name: original_name.to_string(), diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index beffe8f7..3a3d7b20 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -13,8 +13,6 @@ use crate::dynamic_accessor::DynamicArrayAccessor; pub struct NewAgent { pub old_owner_id: i32, pub owner_id: i32, - pub owner_name_ffi: String, - pub new_name_ffi: String, pub owner_name: String, pub new_name: String, pub old_name: String, From 0fba5a427085774c692021c1808078da7cb4f2e0 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 27 Nov 2025 23:16:46 -0500 Subject: [PATCH 17/31] Changed field names from id to kind --- src/api.rs | 26 +++++++++++++------------- src/cloning/weapons.rs | 6 +++--- src/utils.rs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/api.rs b/src/api.rs index 1eff7cc4..e14c93e8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -303,7 +303,7 @@ pub extern "C" fn smashline_reload_script( #[no_mangle] pub extern "C" fn smashline_clone_weapon( original_owner: StringFFI, - original_article_id: i32, + original_weapon_kind: i32, new_owner: StringFFI, new_name: StringFFI, use_original_code: bool, @@ -312,7 +312,7 @@ pub extern "C" fn smashline_clone_weapon( let new_owner = new_owner.as_str().unwrap().to_string(); let new_name = new_name.as_str().unwrap().to_string(); - let original_owner_id = LOWERCASE_FIGHTER_NAMES + let original_owner_kind = LOWERCASE_FIGHTER_NAMES .iter() .position(|name| name == original_owner) .unwrap(); @@ -322,9 +322,9 @@ pub extern "C" fn smashline_clone_weapon( // .position(|name| name == original_name) // .unwrap(); - let original_name = LOWERCASE_WEAPON_NAMES.get(original_article_id as usize).unwrap(); + let original_name = LOWERCASE_WEAPON_NAMES.get(original_weapon_kind as usize).unwrap(); - let new_owner_id = LOWERCASE_FIGHTER_NAMES + let new_owner_kind = LOWERCASE_FIGHTER_NAMES .iter() .position(|name| name == new_owner) .unwrap(); @@ -333,12 +333,12 @@ pub extern "C" fn smashline_clone_weapon( let mut new_articles = crate::cloning::weapons::NEW_ARTICLES.write(); let articles = new_articles - .entry(new_owner_id as i32) + .entry(new_owner_kind as i32) .or_default(); if let Some(id) = articles.iter().position(|article| - article.original_owner == original_owner_id as i32 && - article.weapon_id == original_article_id + article.original_owner == original_owner_kind as i32 && + article.weapon_id == original_weapon_kind ) { return id as i32; } @@ -347,7 +347,7 @@ pub extern "C" fn smashline_clone_weapon( if let Some(agent) = agents.iter().find(|agent| agent.owner_name == new_owner && agent.new_name == new_name ) { - let owner = LOWERCASE_FIGHTER_NAMES.get(agent.old_owner_id as usize).unwrap(); + let owner = LOWERCASE_FIGHTER_NAMES.get(agent.old_owner_kind as usize).unwrap(); panic!( "Weapon with the name '{}_{}' has already been cloned, but using '{}_{}' instead of '{}_{}'", new_owner, new_name, owner, agent.old_name, original_owner, original_name @@ -356,11 +356,11 @@ pub extern "C" fn smashline_clone_weapon( } new_agents - .entry(original_article_id as i32) + .entry(original_weapon_kind as i32) .or_default() .push(NewAgent { - old_owner_id: original_owner_id as i32, - owner_id: new_owner_id as i32, + old_owner_kind: original_owner_kind as i32, + owner_kind: new_owner_kind as i32, owner_name: new_owner, new_name, old_name: original_name.to_string(), @@ -369,8 +369,8 @@ pub extern "C" fn smashline_clone_weapon( let id = articles.len(); articles.push(NewArticle { - original_owner: original_owner_id as i32, - weapon_id: original_article_id, + original_owner: original_owner_kind as i32, + weapon_id: original_weapon_kind, }); id as i32 diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 3a3d7b20..5e4d9f57 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -11,8 +11,8 @@ use smashline::{skyline_smash::app::BattleObjectModuleAccessor, Hash40}; use crate::dynamic_accessor::DynamicArrayAccessor; pub struct NewAgent { - pub old_owner_id: i32, - pub owner_id: i32, + pub old_owner_kind: i32, + pub owner_kind: i32, pub owner_name: String, pub new_name: String, pub old_name: String, @@ -46,7 +46,7 @@ pub fn try_get_new_agent( owner: i32, ) -> Option<&NewAgent> { map.get(&weapon) - .and_then(|v| v.iter().find(|a| a.owner_id == owner)) + .and_then(|v| v.iter().find(|a| a.owner_kind == owner)) } pub static CURRENT_OWNER_KIND: AtomicI32 = AtomicI32::new(-1); diff --git a/src/utils.rs b/src/utils.rs index 670623cf..eed97ee7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -34,7 +34,7 @@ pub fn get_weapon_code_dependency(id: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); let agents = NEW_AGENTS.read(); - try_get_new_agent(&agents, id, current_owner).and_then(|x| x.use_original_code.then_some(x.old_owner_id)) + try_get_new_agent(&agents, id, current_owner).and_then(|x| x.use_original_code.then_some(x.old_owner_kind)) } pub fn get_costume_from_entry_id(entry_id: i32) -> Option { From 71d605cdbdfc159d7440af51934dced41cbeab87 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Wed, 24 Dec 2025 21:10:40 -0500 Subject: [PATCH 18/31] Some refactoring and updated API --- crates/smashline/src/lib.rs | 12 ++++++-- src/api.rs | 58 ++++++++++++++++++++++--------------- src/cloning/weapons.rs | 42 +++++++++++++-------------- src/dynamic_accessor.rs | 9 ++++-- src/params.rs | 23 +++++++-------- src/utils.rs | 8 ++--- 6 files changed, 83 insertions(+), 69 deletions(-) diff --git a/crates/smashline/src/lib.rs b/crates/smashline/src/lib.rs index 1a7fa4db..22996822 100644 --- a/crates/smashline/src/lib.rs +++ b/crates/smashline/src/lib.rs @@ -92,6 +92,12 @@ impl Costume { } } +#[repr(C)] +pub struct CloneWeaponInfo { + pub kind: i32, + pub table_id: i32, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub enum Acmd { @@ -415,11 +421,11 @@ decl_imports! { fn smashline_clone_weapon( original_owner: StringFFI, - original_article_id: i32, + original_weapon_kind: i32, new_owner: StringFFI, new_name: StringFFI, use_original_code: bool - ) -> i32; + ) -> CloneWeaponInfo; fn smashline_update_weapon_count( article_id: i32, @@ -462,7 +468,7 @@ pub fn clone_weapon( new_owner: impl Into, new_name: impl Into, use_original_code: bool, -) -> i32 { +) -> CloneWeaponInfo { smashline_clone_weapon( StringFFI::from_str(original_owner), original_article_id, diff --git a/src/api.rs b/src/api.rs index e14c93e8..730f305f 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,17 +1,19 @@ use std::{ + ffi::CString, num::{NonZeroU64, NonZeroUsize}, ptr::NonNull, + sync::atomic::Ordering, }; use acmd_engine::action::ActionRegistry; use rtld::Section; use smashline::{ - Acmd, AcmdFunction, AgentEntry, Costume, Hash40, L2CAgentBase, ObjectEvent, Priority, StatusLine, StringFFI, + Acmd, AcmdFunction, AgentEntry, CloneWeaponInfo, Costume, Hash40, L2CAgentBase, ObjectEvent, Priority, StatusLine, StringFFI, }; use crate::{ callbacks::{StatusCallback, StatusCallbackFunction}, - cloning::weapons::{NewAgent, NewArticle}, + cloning::weapons::{NewWeapon, BASE_WEAPON_KIND, WEAPON_COUNT, WEAPON_NAMES, WEAPON_OWNER_KINDS, WEAPON_OWNER_NAMES}, create_agent::{ AcmdScript, StatusScript, StatusScriptFunction, LOWERCASE_FIGHTER_NAMES, LOWERCASE_WEAPON_NAMES @@ -307,7 +309,7 @@ pub extern "C" fn smashline_clone_weapon( new_owner: StringFFI, new_name: StringFFI, use_original_code: bool, -) -> i32 { +) -> CloneWeaponInfo { let original_owner = original_owner.as_str().unwrap().to_string(); let new_owner = new_owner.as_str().unwrap().to_string(); let new_name = new_name.as_str().unwrap().to_string(); @@ -329,14 +331,14 @@ pub extern "C" fn smashline_clone_weapon( .position(|name| name == new_owner) .unwrap(); - let mut new_agents = crate::cloning::weapons::NEW_AGENTS.write(); + let mut new_weapons = crate::cloning::weapons::NEW_WEAPONS.write(); - let mut new_articles = crate::cloning::weapons::NEW_ARTICLES.write(); + /* let mut new_articles = crate::cloning::weapons::NEW_ARTICLES.write(); let articles = new_articles .entry(new_owner_kind as i32) - .or_default(); + .or_default(); */ - if let Some(id) = articles.iter().position(|article| + /* if let Some(id) = articles.iter().position(|article| article.original_owner == original_owner_kind as i32 && article.weapon_id == original_weapon_kind ) { @@ -353,27 +355,35 @@ pub extern "C" fn smashline_clone_weapon( new_owner, new_name, owner, agent.old_name, original_owner, original_name ); } - } + } */ - new_agents - .entry(original_weapon_kind as i32) - .or_default() - .push(NewAgent { - old_owner_kind: original_owner_kind as i32, - owner_kind: new_owner_kind as i32, - owner_name: new_owner, - new_name, - old_name: original_name.to_string(), - use_original_code, - }); + let weapons = new_weapons + .entry(new_owner_kind as i32) + .or_default(); - let id = articles.len(); - articles.push(NewArticle { - original_owner: original_owner_kind as i32, - weapon_id: original_weapon_kind, + let kind = WEAPON_COUNT.fetch_add(1, Ordering::Relaxed) as i32; + let table_id = weapons.len(); + + weapons.push(NewWeapon { + old_owner_kind: original_owner_kind as i32, + owner_kind: new_owner_kind as i32, + owner_name: new_owner.clone(), + new_name: new_name.clone(), + old_name: original_name.to_string(), + kind, + old_kind: original_weapon_kind as i32, + use_original_code, }); - id as i32 + WEAPON_NAMES.write().push(CString::new(new_name).unwrap().into_raw()); + WEAPON_OWNER_NAMES.write().push(CString::new(new_owner).unwrap().into_raw()); + WEAPON_OWNER_KINDS.write().push(new_owner_kind as i32); + BASE_WEAPON_KIND.write().push(original_owner_kind as i32); + + CloneWeaponInfo { + kind, + table_id: table_id as i32, + } } #[no_mangle] diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 5e4d9f57..12c731fa 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -10,22 +10,24 @@ use smashline::{skyline_smash::app::BattleObjectModuleAccessor, Hash40}; use crate::dynamic_accessor::DynamicArrayAccessor; -pub struct NewAgent { +pub struct NewWeapon { pub old_owner_kind: i32, pub owner_kind: i32, pub owner_name: String, pub new_name: String, pub old_name: String, + pub kind: i32, + pub old_kind: i32, pub use_original_code: bool, } -pub struct NewArticle { +/* pub struct NewArticle { pub original_owner: i32, pub weapon_id: i32, -} +} */ -pub static NEW_ARTICLES: RwLock>> = RwLock::new(BTreeMap::new()); -pub static NEW_AGENTS: RwLock>> = RwLock::new(BTreeMap::new()); +// pub static NEW_ARTICLES: RwLock>> = RwLock::new(BTreeMap::new()); +pub static NEW_WEAPONS: RwLock>> = RwLock::new(BTreeMap::new()); pub static IGNORE_NEW_AGENTS: AtomicBool = AtomicBool::new(false); const ORIGINAL_WEAPON_COUNT: usize = 0x267; @@ -41,10 +43,10 @@ pub static CURRENT_WEAPON_KIND: AtomicI32 = AtomicI32::new(-1); pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); pub fn try_get_new_agent( - map: &BTreeMap>, + map: &BTreeMap>, weapon: i32, owner: i32, -) -> Option<&NewAgent> { +) -> Option<&NewWeapon> { map.get(&weapon) .and_then(|v| v.iter().find(|a| a.owner_kind == owner)) } @@ -159,13 +161,13 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { } } - if let Some(new_articles) = NEW_ARTICLES.read().get(&kind) { + if let Some(new_articles) = NEW_WEAPONS.read().get(&kind) { for article in new_articles.iter() { - let source_data = call_original!(article.original_owner); + let source_data = call_original!(article.old_owner_kind); unsafe { - let Some(article) = (*source_data).get_article(article.weapon_id) else { + let Some(article) = (*source_data).get_article(article.old_kind) else { panic!("Failed to append article table"); }; @@ -356,7 +358,7 @@ unsafe fn kirby_get_copy_articles(ctx: &mut InlineCtx, store_reg: usize) { // There's on;y 1 case where it's "enemy" instead #[skyline::hook(offset = 0x17e09a8, inline)] unsafe fn get_file_category(ctx: &mut InlineCtx) { - if ctx.registers[26].x() >= ORIGINAL_WEAPON_COUNT { + if ctx.registers[26].x() >= ORIGINAL_WEAPON_COUNT as u64 { use skyline::hooks; let text = hooks::getRegionAddress(hooks::Region::Text) as *const u8; @@ -367,28 +369,24 @@ unsafe fn get_file_category(ctx: &mut InlineCtx) { #[skyline::hook(offset = 0x33aa1e0)] fn get_weapon_bone_stuff(kind: i32) -> *const c_void { - let k; - - if kind < ORIGINAL_WEAPON_COUNT - || kind >= WEAPON_COUNT.load(Ordering::Relaxed) { + let k = if kind < ORIGINAL_WEAPON_COUNT as i32 + || kind >= WEAPON_COUNT.load(Ordering::Relaxed) as i32 { kind } else { BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] - } + }; call_original!(k) } #[skyline::hook(offset = 0x33be790)] fn get_weapon_vtable(kind: i32) -> *const c_void { - let k; - - if kind < ORIGINAL_WEAPON_COUNT - || kind >= WEAPON_COUNT.load(Ordering::Relaxed) { + let k = if kind < ORIGINAL_WEAPON_COUNT as i32 + || kind >= WEAPON_COUNT.load(Ordering::Relaxed) as i32 { kind } else { BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] - } + }; call_original!(k) } @@ -399,7 +397,7 @@ unsafe fn mimic_echo_weapon(ctx: &mut InlineCtx) { CURRENT_WEAPON_KIND.store(kind as i32, Ordering::Relaxed); - if kind >= ORIGINAL_WEAPON_COUNT { + if kind >= ORIGINAL_WEAPON_COUNT as u32 { kind = BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] as u32; } diff --git a/src/dynamic_accessor.rs b/src/dynamic_accessor.rs index 46a7cac2..48c45136 100644 --- a/src/dynamic_accessor.rs +++ b/src/dynamic_accessor.rs @@ -1,6 +1,6 @@ use std::{ + ffi::{c_char, CStr, CString}, ops::{Deref, DerefMut}, - ffi::{c_char, CStr}, sync::OnceLock, }; @@ -55,13 +55,16 @@ impl PushArray<*const c_char> for DynamicArrayAccessor<*const c_char> { fn push(&mut self, item: *const c_char) { let data: &mut Vec<*const c_char> = self; - let item = unsafe { + let new_item = unsafe { data.iter() .find(|&&x| CStr::from_ptr(x) == CStr::from_ptr(item)) + .inspect(|x| { + let _ = CString::from_raw(item as *mut i8); + }) .unwrap_or(&item) }; - data.push(*item); + data.push(*new_item); } } diff --git a/src/params.rs b/src/params.rs index bbb0c72f..6f8e3f56 100644 --- a/src/params.rs +++ b/src/params.rs @@ -15,7 +15,7 @@ use vtables::{vtable, VirtualClass}; use std::ops::{Deref, DerefMut}; -use crate::cloning::weapons::{try_get_new_agent, NEW_AGENTS, NEW_ARTICLES}; +use crate::cloning::weapons::{try_get_new_agent, NEW_WEAPONS}; pub static WHITELISTED_PARAMS: RwLock>> = RwLock::new(BTreeMap::new()); @@ -323,10 +323,10 @@ unsafe fn init_fighter_p_object(ctx: &InlineCtx) { } let whitelisted_params = WHITELISTED_PARAMS.read(); - let new_articles = NEW_ARTICLES.read(); + let new_weapons = NEW_WEAPONS.read(); if whitelisted_params.get(&fighter_id).is_none() - && new_articles.get(&fighter_id).is_none() { + && new_weapons.get(&fighter_id).is_none() { return; } @@ -360,16 +360,13 @@ unsafe fn init_fighter_p_object(ctx: &InlineCtx) { let mut allowed_names = vec![]; let mut remap_names = HashMap::new(); - let new_agents = NEW_AGENTS.read(); - if let Some(articles) = new_articles.get(&fighter_id) { - for article in articles.iter() { - if let Some(agent) = try_get_new_agent(&new_agents, article.weapon_id, fighter_id) { - allowed_names.push(Hash40::new(&format!("param_{}", agent.new_name)).0); - remap_names.insert( - Hash40::new(&format!("param_{}", agent.old_name)).0, - Hash40::new(&format!("param_{}", agent.new_name)).0, - ); - } + if let Some(weapons) = new_weapons.get(&fighter_id) { + for weapon in weapons.iter() { + allowed_names.push(Hash40::new(&format!("param_{}", weapon.new_name)).0); + remap_names.insert( + Hash40::new(&format!("param_{}", weapon.old_name)).0, + Hash40::new(&format!("param_{}", weapon.new_name)).0, + ); } } diff --git a/src/utils.rs b/src/utils.rs index eed97ee7..6d54b4e8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,13 +5,13 @@ use smashline::{Costume, Hash40}; use crate::{ cloning::fighters::CURRENT_PLAYER_ID, - cloning::weapons::{try_get_new_agent, CURRENT_OWNER_KIND, NEW_AGENTS}, + cloning::weapons::{try_get_new_agent, CURRENT_OWNER_KIND, NEW_WEAPONS}, create_agent::{COSTUMES, LOWERCASE_WEAPON_NAMES, LOWERCASE_WEAPON_OWNER_NAMES, LOWERCASE_FIGHTER_NAMES} }; pub fn get_weapon_name(id: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); + let agents = NEW_WEAPONS.read(); if let Some(name) = try_get_new_agent(&agents, id, current_owner).map(|agent| agent.new_name.clone()) { Some(name) } else { @@ -21,7 +21,7 @@ pub fn get_weapon_name(id: i32) -> Option { pub fn get_weapon_owner_name(id: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); + let agents = NEW_WEAPONS.read(); if let Some(name) = try_get_new_agent(&agents, id, current_owner).map(|agent| agent.owner_name.clone()) { Some(name) @@ -32,7 +32,7 @@ pub fn get_weapon_owner_name(id: i32) -> Option { pub fn get_weapon_code_dependency(id: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_AGENTS.read(); + let agents = NEW_WEAPONS.read(); try_get_new_agent(&agents, id, current_owner).and_then(|x| x.use_original_code.then_some(x.old_owner_kind)) } From fabae377918d2e635192aee67e37c265867cb41c Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 14:55:41 -0500 Subject: [PATCH 19/31] Added a macro for mimicking echo weapons --- src/cloning/weapons.rs | 59 +++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 12c731fa..66029a36 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -354,6 +354,38 @@ unsafe fn kirby_get_copy_articles(ctx: &mut InlineCtx, store_reg: usize) { ctx.registers[store_reg].set_x(static_article_info as *const StaticArticleData as u64); } +macro_rules! decl_hooks_mimic_echo_weapon { + ($install_fn:ident; $($name:ident($offset:expr) -> $return_type:ty);*) => { + $( + #[skyline::hook(offset = $offset)] + unsafe fn $name(kind: i32) -> $return_type { + let k = if kind < ORIGINAL_WEAPON_COUNT as i32 + || kind >= WEAPON_COUNT.load(Ordering::Relaxed) as i32 { + kind + } else { + BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] + }; + + call_original!(k) + } + )* + + fn $install_fn() { + skyline::install_hooks!( + $( + $name, + )* + ); + } + }; +} + +decl_hooks_mimic_echo_weapon! { + install_mimic_echo_weapon_hooks; + get_weapon_bone_stuff(0x33aa1e0) -> *const c_void; + get_weapon_vtable(0x33be790) -> *const c_void +} + // Just going to assume "fighter" when getting the file // There's on;y 1 case where it's "enemy" instead #[skyline::hook(offset = 0x17e09a8, inline)] @@ -367,30 +399,6 @@ unsafe fn get_file_category(ctx: &mut InlineCtx) { } } -#[skyline::hook(offset = 0x33aa1e0)] -fn get_weapon_bone_stuff(kind: i32) -> *const c_void { - let k = if kind < ORIGINAL_WEAPON_COUNT as i32 - || kind >= WEAPON_COUNT.load(Ordering::Relaxed) as i32 { - kind - } else { - BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] - }; - - call_original!(k) -} - -#[skyline::hook(offset = 0x33be790)] -fn get_weapon_vtable(kind: i32) -> *const c_void { - let k = if kind < ORIGINAL_WEAPON_COUNT as i32 - || kind >= WEAPON_COUNT.load(Ordering::Relaxed) as i32 { - kind - } else { - BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT] - }; - - call_original!(k) -} - #[skyline::hook(offset = 0x33b5d10, inline)] unsafe fn mimic_echo_weapon(ctx: &mut InlineCtx) { let mut kind = ctx.registers[28].w(); @@ -413,12 +421,11 @@ pub fn install() { install_weapon_name_hooks(); install_weapon_owner_hooks(); install_weapon_owner_name_hooks(); + install_mimic_echo_weapon_hooks(); skyline::install_hooks!( get_static_fighter_data, get_file_category, - get_weapon_bone_stuff, - get_weapon_vtable, mimic_echo_weapon, restore_weapon_kind, ); From a4382cbda447f2bde999830a724c5a575c713e5e Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 15:13:20 -0500 Subject: [PATCH 20/31] Weapon kind hash hooks and whatnot --- src/api.rs | 5 ++++- src/cloning/weapons.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/api.rs b/src/api.rs index 730f305f..8f30f66b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -13,7 +13,7 @@ use smashline::{ use crate::{ callbacks::{StatusCallback, StatusCallbackFunction}, - cloning::weapons::{NewWeapon, BASE_WEAPON_KIND, WEAPON_COUNT, WEAPON_NAMES, WEAPON_OWNER_KINDS, WEAPON_OWNER_NAMES}, + cloning::weapons::{NewWeapon, BASE_WEAPON_KIND, WEAPON_COUNT, WEAPON_KIND_HASHES, WEAPON_NAMES, WEAPON_OWNER_KINDS, WEAPON_OWNER_NAMES}, create_agent::{ AcmdScript, StatusScript, StatusScriptFunction, LOWERCASE_FIGHTER_NAMES, LOWERCASE_WEAPON_NAMES @@ -378,6 +378,9 @@ pub extern "C" fn smashline_clone_weapon( WEAPON_NAMES.write().push(CString::new(new_name).unwrap().into_raw()); WEAPON_OWNER_NAMES.write().push(CString::new(new_owner).unwrap().into_raw()); WEAPON_OWNER_KINDS.write().push(new_owner_kind as i32); + WEAPON_KIND_HASHES.write().push(Hash40::new( + &format!("weapon_kind_{}_{}", new_owner, new_name) + ).0); BASE_WEAPON_KIND.write().push(original_owner_kind as i32); CloneWeaponInfo { diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 66029a36..ec17b40e 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -36,6 +36,7 @@ pub static WEAPON_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_WEAPON_COUNT); pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_WEAPON_COUNT)); pub static WEAPON_OWNER_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5188240, ORIGINAL_WEAPON_COUNT)); pub static WEAPON_OWNER_KINDS: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455d7e4, ORIGINAL_WEAPON_COUNT)); +pub static WEAPON_KIND_HASHES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x455e650, ORIGINAL_WEAPON_COUNT)); pub static BASE_WEAPON_KIND: RwLock> = RwLock::new(Vec::new()); pub static CURRENT_WEAPON_KIND: AtomicI32 = AtomicI32::new(-1); @@ -370,6 +371,30 @@ macro_rules! decl_hooks_mimic_echo_weapon { } )* + fn $install_fn() { + skyline::install_hooks!( + $( + $name, + )* + ); + } + }; + (hashes; $install_fn:ident; $($name:ident($offset:expr));*) => { + $( + #[skyline::hook(offset = $offset, inline)] + unsafe fn $name(ctx: &mut InlineCtx) { + let kind = ctx.registers[20].w() as i32; + + let hash = if kind > WEAPON_COUNT.load(Ordering::Relaxed) { + Hash40::new("weapon_kind_none").0 + } else { + WEAPON_KIND_HASHES.read()[kind as usize] + }; + + ctx.registers[4].set_x(hash); + } + )* + fn $install_fn() { skyline::install_hooks!( $( @@ -386,6 +411,13 @@ decl_hooks_mimic_echo_weapon! { get_weapon_vtable(0x33be790) -> *const c_void } +decl_hooks_mimic_echo_weapon! { + hashes; + install_mimic_echo_weapon_kind_hash_hooks; + get_hashes1(0x3ae24c); + get_hashes2(0x3ae7bc) +} + // Just going to assume "fighter" when getting the file // There's on;y 1 case where it's "enemy" instead #[skyline::hook(offset = 0x17e09a8, inline)] @@ -418,10 +450,15 @@ unsafe fn restore_weapon_kind(ctx: &mut InlineCtx) { } pub fn install() { + skyline::patching::Patch::in_text(0x3ae23c).nop().unwrap(); + skyline::patching::Patch::in_text(0x3ae7ac).nop().unwrap(); + install_weapon_name_hooks(); install_weapon_owner_hooks(); install_weapon_owner_name_hooks(); + install_mimic_echo_weapon_hooks(); + install_mimic_echo_weapon_kind_hash_hooks(); skyline::install_hooks!( get_static_fighter_data, From 9a9db6634a4465d31a84aa3e6ac17eb7ccbe3bc6 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 15:20:33 -0500 Subject: [PATCH 21/31] Forgot to nop something for get_file_weapon_name --- src/cloning/weapons.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index ec17b40e..25a244d8 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -452,6 +452,7 @@ unsafe fn restore_weapon_kind(ctx: &mut InlineCtx) { pub fn install() { skyline::patching::Patch::in_text(0x3ae23c).nop().unwrap(); skyline::patching::Patch::in_text(0x3ae7ac).nop().unwrap(); + skyline::patching::Patch::in_text(0x17e0894).nop().unwrap(); install_weapon_name_hooks(); install_weapon_owner_hooks(); From 4ed5babdb5890cff9d06f95db2827af867de69ef Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 15:47:47 -0500 Subject: [PATCH 22/31] Update file category hook --- src/api.rs | 4 ++-- src/cloning/weapons.rs | 28 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/api.rs b/src/api.rs index 8f30f66b..732bf8e9 100644 --- a/src/api.rs +++ b/src/api.rs @@ -375,8 +375,8 @@ pub extern "C" fn smashline_clone_weapon( use_original_code, }); - WEAPON_NAMES.write().push(CString::new(new_name).unwrap().into_raw()); - WEAPON_OWNER_NAMES.write().push(CString::new(new_owner).unwrap().into_raw()); + WEAPON_NAMES.write().push(CString::new(new_name.clone()).unwrap().into_raw()); + WEAPON_OWNER_NAMES.write().push(CString::new(new_owner.clone()).unwrap().into_raw()); WEAPON_OWNER_KINDS.write().push(new_owner_kind as i32); WEAPON_KIND_HASHES.write().push(Hash40::new( &format!("weapon_kind_{}_{}", new_owner, new_name) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 25a244d8..444d237b 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -385,7 +385,7 @@ macro_rules! decl_hooks_mimic_echo_weapon { unsafe fn $name(ctx: &mut InlineCtx) { let kind = ctx.registers[20].w() as i32; - let hash = if kind > WEAPON_COUNT.load(Ordering::Relaxed) { + let hash = if kind > WEAPON_COUNT.load(Ordering::Relaxed) as i32 { Hash40::new("weapon_kind_none").0 } else { WEAPON_KIND_HASHES.read()[kind as usize] @@ -418,17 +418,23 @@ decl_hooks_mimic_echo_weapon! { get_hashes2(0x3ae7bc) } -// Just going to assume "fighter" when getting the file -// There's on;y 1 case where it's "enemy" instead -#[skyline::hook(offset = 0x17e09a8, inline)] -unsafe fn get_file_category(ctx: &mut InlineCtx) { - if ctx.registers[26].x() >= ORIGINAL_WEAPON_COUNT as u64 { - use skyline::hooks; +#[skyline::hook(offset = 0x17e09a4, inline)] +unsafe fn mimic_echo_weapon_file_category(ctx: &mut InlineCtx) { + let mut kind = ctx.registers[26].x() as i32; - let text = hooks::getRegionAddress(hooks::Region::Text) as *const u8; - let fighter_string = text.add(0x4358c60) as *const c_char; - ctx.registers[25].set_x(fighter_string as u64); + if kind >= ORIGINAL_WEAPON_COUNT as i32 { + kind = BASE_WEAPON_KIND.read()[(kind as usize) - ORIGINAL_WEAPON_COUNT]; } + + // Just in case kind is still somehow larger + // than the original weapon count, then we're + // going to assume "fighter" when getting the file. + // There's only 1 case where it's "enemy" instead + if kind >= ORIGINAL_WEAPON_COUNT as i32 { + kind = 0; + } + + ctx.registers[26].set_x(kind as u64); } #[skyline::hook(offset = 0x33b5d10, inline)] @@ -463,7 +469,7 @@ pub fn install() { skyline::install_hooks!( get_static_fighter_data, - get_file_category, + mimic_echo_weapon_file_category, mimic_echo_weapon, restore_weapon_kind, ); From 5bc0ebc788e666a1a53b30591ef13749e93fe930 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 15:48:24 -0500 Subject: [PATCH 23/31] Renamed hook --- src/cloning/weapons.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 444d237b..9d881b79 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -438,7 +438,7 @@ unsafe fn mimic_echo_weapon_file_category(ctx: &mut InlineCtx) { } #[skyline::hook(offset = 0x33b5d10, inline)] -unsafe fn mimic_echo_weapon(ctx: &mut InlineCtx) { +unsafe fn save_weapon_kind(ctx: &mut InlineCtx) { let mut kind = ctx.registers[28].w(); CURRENT_WEAPON_KIND.store(kind as i32, Ordering::Relaxed); @@ -470,7 +470,7 @@ pub fn install() { skyline::install_hooks!( get_static_fighter_data, mimic_echo_weapon_file_category, - mimic_echo_weapon, + save_weapon_kind, restore_weapon_kind, ); From 0740e5351aabe0e484df581197aeb965b17a8216 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Thu, 25 Dec 2025 15:50:29 -0500 Subject: [PATCH 24/31] Added another mimic echo weapon hook --- src/cloning/weapons.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 9d881b79..d679a85a 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -408,7 +408,8 @@ macro_rules! decl_hooks_mimic_echo_weapon { decl_hooks_mimic_echo_weapon! { install_mimic_echo_weapon_hooks; get_weapon_bone_stuff(0x33aa1e0) -> *const c_void; - get_weapon_vtable(0x33be790) -> *const c_void + get_weapon_vtable(0x33be790) -> *const c_void; + idk(0x33afc00) -> i32 } decl_hooks_mimic_echo_weapon! { From ba201f79a6141fd742c2b826b388e0b46bfb5d4b Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 12:21:19 -0500 Subject: [PATCH 25/31] Oopsie, used the wrong var --- src/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api.rs b/src/api.rs index 732bf8e9..add19cc0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -381,7 +381,7 @@ pub extern "C" fn smashline_clone_weapon( WEAPON_KIND_HASHES.write().push(Hash40::new( &format!("weapon_kind_{}_{}", new_owner, new_name) ).0); - BASE_WEAPON_KIND.write().push(original_owner_kind as i32); + BASE_WEAPON_KIND.write().push(original_weapon_kind as i32); CloneWeaponInfo { kind, From 0268c148736340b143399d081431f14e9afef361 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 12:21:47 -0500 Subject: [PATCH 26/31] Forgot to update the weapon kind --- src/cloning/weapons.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index d679a85a..9787bd49 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -162,16 +162,17 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { } } - if let Some(new_articles) = NEW_WEAPONS.read().get(&kind) { + if let Some(new_weapons) = NEW_WEAPONS.read().get(&kind) { - for article in new_articles.iter() { - let source_data = call_original!(article.old_owner_kind); + for weapon in new_weapons.iter() { + let source_data = call_original!(weapon.old_owner_kind); unsafe { - let Some(article) = (*source_data).get_article(article.old_kind) else { + let Some(mut article) = (*source_data).get_article(weapon.old_kind) else { panic!("Failed to append article table"); }; + article.weapon_id = weapon.kind; new_descriptors.push(article); } } From 90f683ddfbf15ccbcd7e39b9796d67d61b0d16d4 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 12:22:07 -0500 Subject: [PATCH 27/31] Update file category hook thingy --- src/cloning/weapons.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 9787bd49..486ff494 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -420,7 +420,7 @@ decl_hooks_mimic_echo_weapon! { get_hashes2(0x3ae7bc) } -#[skyline::hook(offset = 0x17e09a4, inline)] +#[skyline::hook(offset = 0x17e09a8, inline)] unsafe fn mimic_echo_weapon_file_category(ctx: &mut InlineCtx) { let mut kind = ctx.registers[26].x() as i32; @@ -436,7 +436,11 @@ unsafe fn mimic_echo_weapon_file_category(ctx: &mut InlineCtx) { kind = 0; } - ctx.registers[26].set_x(kind as u64); + let text = skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as *const u8; + let file_category_table = text.add(0x5186f08) as *const *const c_char; + let file_category = *file_category_table.add(kind as usize); + + ctx.registers[25].set_x(file_category as u64); } #[skyline::hook(offset = 0x33b5d10, inline)] From cbb9212049c8cef64fe2b6eaba3e75bf30a2afb9 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 16:21:27 -0500 Subject: [PATCH 28/31] Make sure to properly update weapon count --- src/cloning/weapons.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 486ff494..6971e622 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -155,13 +155,6 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { new_descriptors.extend_from_slice(unsafe { (*original_data).articles_as_slice() }); - for article in new_descriptors.iter_mut() { - let weapon_count = WEAPON_COUNT_UPDATE.read(); - if let Some(new_count) = weapon_count.get(&article.weapon_id) { - article.max_count = *new_count; - } - } - if let Some(new_weapons) = NEW_WEAPONS.read().get(&kind) { for weapon in new_weapons.iter() { @@ -178,6 +171,13 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { } } + for article in new_descriptors.iter_mut() { + let weapon_count = WEAPON_COUNT_UPDATE.read(); + if let Some(new_count) = weapon_count.get(&article.weapon_id) { + article.max_count = *new_count; + } + } + let count = new_descriptors.len(); let ptr = new_descriptors.leak().as_ptr(); let static_article_info = Box::leak(Box::new(StaticArticleData { From e1aeff838be65805cf63779f535662c1269e02e4 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 16:23:35 -0500 Subject: [PATCH 29/31] Cloned weapons should now be able to use original code --- src/cloning/weapons.rs | 31 +++++++++++++++++++++++-------- src/create_agent.rs | 10 +++++----- src/utils.rs | 10 +++++++--- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 6971e622..96cff728 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -28,9 +28,9 @@ pub struct NewWeapon { // pub static NEW_ARTICLES: RwLock>> = RwLock::new(BTreeMap::new()); pub static NEW_WEAPONS: RwLock>> = RwLock::new(BTreeMap::new()); -pub static IGNORE_NEW_AGENTS: AtomicBool = AtomicBool::new(false); +pub static IS_USING_ORIGINAL_CODE: AtomicBool = AtomicBool::new(false); -const ORIGINAL_WEAPON_COUNT: usize = 0x267; +pub const ORIGINAL_WEAPON_COUNT: usize = 0x267; pub static WEAPON_COUNT: AtomicUsize = AtomicUsize::new(ORIGINAL_WEAPON_COUNT); pub static WEAPON_NAMES: RwLock> = RwLock::new(DynamicArrayAccessor::new(0x5185bd0, ORIGINAL_WEAPON_COUNT)); @@ -191,18 +191,33 @@ fn get_static_fighter_data(kind: i32) -> *const StaticFighterData { } fn weapon_owner_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { - let index = ctx.registers[source_register].x() >> shift; - ctx.registers[dst_register].set_x(WEAPON_OWNER_KINDS.read()[index as usize] as u64); + let mut kind = ctx.registers[source_register].x() >> shift; + + if IS_USING_ORIGINAL_CODE.load(Ordering::Relaxed) { + kind = BASE_WEAPON_KIND.read()[(kind as usize) - crate::cloning::weapons::ORIGINAL_WEAPON_COUNT] as u64; + } + + ctx.registers[dst_register].set_x(WEAPON_OWNER_KINDS.read()[kind as usize] as u64); } fn weapon_owner_name_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { - let index = ctx.registers[source_register].x() >> shift; - ctx.registers[dst_register].set_x(WEAPON_OWNER_NAMES.read()[index as usize] as u64); + let mut kind = ctx.registers[source_register].x() >> shift; + + if IS_USING_ORIGINAL_CODE.load(Ordering::Relaxed) { + kind = BASE_WEAPON_KIND.read()[(kind as usize) - crate::cloning::weapons::ORIGINAL_WEAPON_COUNT] as u64; + } + + ctx.registers[dst_register].set_x(WEAPON_OWNER_NAMES.read()[kind as usize] as u64); } fn weapon_name_hook(ctx: &mut InlineCtx, source_register: usize, shift: u32, dst_register: usize) { - let index = ctx.registers[source_register].x() >> shift; - ctx.registers[dst_register].set_x(WEAPON_NAMES.read()[index as usize] as u64); + let mut kind = ctx.registers[source_register].x() >> shift; + + if IS_USING_ORIGINAL_CODE.load(Ordering::Relaxed) { + kind = BASE_WEAPON_KIND.read()[(kind as usize) - crate::cloning::weapons::ORIGINAL_WEAPON_COUNT] as u64; + } + + ctx.registers[dst_register].set_x(WEAPON_NAMES.read()[kind as usize] as u64); } macro_rules! decl_hooks { diff --git a/src/create_agent.rs b/src/create_agent.rs index e064cbb4..e12ee3ae 100644 --- a/src/create_agent.rs +++ b/src/create_agent.rs @@ -25,7 +25,7 @@ use smashline::{ use vtables::{CustomDataAccessError, VirtualClass}; use crate::{ - cloning::weapons::{IGNORE_NEW_AGENTS, WEAPON_NAMES, WEAPON_OWNER_NAMES}, interpreter::LoadedScript, + cloning::weapons::{IS_USING_ORIGINAL_CODE, WEAPON_NAMES, WEAPON_OWNER_NAMES}, interpreter::LoadedScript, static_accessor::StaticArrayAccessor, callbacks::{CALLBACKS, StatusCallbackFunction} }; @@ -577,9 +577,9 @@ fn create_agent_hook( std::thread::sleep(Duration::from_millis(1)); } - IGNORE_NEW_AGENTS.store(true, Ordering::Relaxed); + IS_USING_ORIGINAL_CODE.store(true, Ordering::Relaxed); let result = original.call(object, boma, lua_state); - IGNORE_NEW_AGENTS.store(false, Ordering::Relaxed); + IS_USING_ORIGINAL_CODE.store(false, Ordering::Relaxed); if let Some(agent) = result { (agent, Some(fighter_id)) @@ -1069,9 +1069,9 @@ fn create_agent_status_weapon( std::thread::sleep(Duration::from_millis(1)); } - IGNORE_NEW_AGENTS.store(true, Ordering::Relaxed); + IS_USING_ORIGINAL_CODE.store(true, Ordering::Relaxed); let result = call_original!(object, boma, lua_state); - IGNORE_NEW_AGENTS.store(false, Ordering::Relaxed); + IS_USING_ORIGINAL_CODE.store(false, Ordering::Relaxed); if let Some(agent) = result { (false, agent, Some(fighter_id)) diff --git a/src/utils.rs b/src/utils.rs index 6d54b4e8..c368263a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -30,11 +30,15 @@ pub fn get_weapon_owner_name(id: i32) -> Option { } } -pub fn get_weapon_code_dependency(id: i32) -> Option { +pub fn get_weapon_code_dependency(kind: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_WEAPONS.read(); - try_get_new_agent(&agents, id, current_owner).and_then(|x| x.use_original_code.then_some(x.old_owner_kind)) + NEW_WEAPONS.read() + .get(¤t_owner) + .into_iter() + .flatten() + .find(|&x| x.kind == kind && x.use_original_code) + .map(|x| x.old_owner_kind) } pub fn get_costume_from_entry_id(entry_id: i32) -> Option { From 82785420b22a979f56d5e73cf4aa8be66b469ff3 Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 17:19:21 -0500 Subject: [PATCH 30/31] If it exists, return the appropriate CloneWeaponInfo --- src/api.rs | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/api.rs b/src/api.rs index add19cc0..19379763 100644 --- a/src/api.rs +++ b/src/api.rs @@ -333,34 +333,27 @@ pub extern "C" fn smashline_clone_weapon( let mut new_weapons = crate::cloning::weapons::NEW_WEAPONS.write(); - /* let mut new_articles = crate::cloning::weapons::NEW_ARTICLES.write(); - let articles = new_articles - .entry(new_owner_kind as i32) - .or_default(); */ - - /* if let Some(id) = articles.iter().position(|article| - article.original_owner == original_owner_kind as i32 && - article.weapon_id == original_weapon_kind - ) { - return id as i32; - } - - for agents in new_agents.values() { - if let Some(agent) = agents.iter().find(|agent| - agent.owner_name == new_owner && agent.new_name == new_name - ) { - let owner = LOWERCASE_FIGHTER_NAMES.get(agent.old_owner_kind as usize).unwrap(); - panic!( - "Weapon with the name '{}_{}' has already been cloned, but using '{}_{}' instead of '{}_{}'", - new_owner, new_name, owner, agent.old_name, original_owner, original_name - ); - } - } */ - let weapons = new_weapons .entry(new_owner_kind as i32) .or_default(); + for (i, weapon) in weapons.iter().enumerate() { + if weapon.old_owner_kind == original_owner_kind as i32 + && weapon.owner_name == new_owner + && weapon.new_name == new_name + && weapon.old_name == original_name + && weapon.old_kind == original_weapon_kind { + // TODO: Properly handle a situation where we're + // cloning a weapon, even though it already exists. + // Maybe only consider if new_name is the same? + + return CloneWeaponInfo { + kind: weapon.kind, + table_id: i as i32, + }; + } + } + let kind = WEAPON_COUNT.fetch_add(1, Ordering::Relaxed) as i32; let table_id = weapons.len(); From 3b7d4596fef0ad65f263bd530c35ef740ab2aa9f Mon Sep 17 00:00:00 2001 From: FatherOfEgg Date: Fri, 26 Dec 2025 17:22:17 -0500 Subject: [PATCH 31/31] Removed unused stuff --- src/cloning/weapons.rs | 15 --------------- src/params.rs | 2 +- src/utils.rs | 23 +---------------------- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/cloning/weapons.rs b/src/cloning/weapons.rs index 96cff728..7e80358a 100644 --- a/src/cloning/weapons.rs +++ b/src/cloning/weapons.rs @@ -21,12 +21,6 @@ pub struct NewWeapon { pub use_original_code: bool, } -/* pub struct NewArticle { - pub original_owner: i32, - pub weapon_id: i32, -} */ - -// pub static NEW_ARTICLES: RwLock>> = RwLock::new(BTreeMap::new()); pub static NEW_WEAPONS: RwLock>> = RwLock::new(BTreeMap::new()); pub static IS_USING_ORIGINAL_CODE: AtomicBool = AtomicBool::new(false); @@ -43,15 +37,6 @@ pub static CURRENT_WEAPON_KIND: AtomicI32 = AtomicI32::new(-1); pub static WEAPON_COUNT_UPDATE: RwLock> = RwLock::new(BTreeMap::new()); -pub fn try_get_new_agent( - map: &BTreeMap>, - weapon: i32, - owner: i32, -) -> Option<&NewWeapon> { - map.get(&weapon) - .and_then(|v| v.iter().find(|a| a.owner_kind == owner)) -} - pub static CURRENT_OWNER_KIND: AtomicI32 = AtomicI32::new(-1); pub static IS_KIRBY_COPYING: AtomicBool = AtomicBool::new(false); diff --git a/src/params.rs b/src/params.rs index 6f8e3f56..9e3dfa0c 100644 --- a/src/params.rs +++ b/src/params.rs @@ -15,7 +15,7 @@ use vtables::{vtable, VirtualClass}; use std::ops::{Deref, DerefMut}; -use crate::cloning::weapons::{try_get_new_agent, NEW_WEAPONS}; +use crate::cloning::weapons::{NEW_WEAPONS}; pub static WHITELISTED_PARAMS: RwLock>> = RwLock::new(BTreeMap::new()); diff --git a/src/utils.rs b/src/utils.rs index c368263a..0faede18 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,31 +5,10 @@ use smashline::{Costume, Hash40}; use crate::{ cloning::fighters::CURRENT_PLAYER_ID, - cloning::weapons::{try_get_new_agent, CURRENT_OWNER_KIND, NEW_WEAPONS}, + cloning::weapons::{CURRENT_OWNER_KIND, NEW_WEAPONS}, create_agent::{COSTUMES, LOWERCASE_WEAPON_NAMES, LOWERCASE_WEAPON_OWNER_NAMES, LOWERCASE_FIGHTER_NAMES} }; -pub fn get_weapon_name(id: i32) -> Option { - let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_WEAPONS.read(); - if let Some(name) = try_get_new_agent(&agents, id, current_owner).map(|agent| agent.new_name.clone()) { - Some(name) - } else { - LOWERCASE_WEAPON_NAMES.get(id as usize).map(|x| x.to_string()) - } -} - -pub fn get_weapon_owner_name(id: i32) -> Option { - let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed); - let agents = NEW_WEAPONS.read(); - - if let Some(name) = try_get_new_agent(&agents, id, current_owner).map(|agent| agent.owner_name.clone()) { - Some(name) - } else { - LOWERCASE_WEAPON_OWNER_NAMES.get(id as usize).map(|x| x.to_string()) - } -} - pub fn get_weapon_code_dependency(kind: i32) -> Option { let current_owner = CURRENT_OWNER_KIND.load(Ordering::Relaxed);