diff --git a/Cargo.toml b/Cargo.toml
index 6964ac8b..24625b58 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "skyline_rs_template"
+name = "training_modpack"
 version = "0.1.0"
 authors = ["jam1garner <jam1.mcleod@hotmail.com>"]
 edition = "2018"
@@ -17,3 +17,6 @@ panic = "abort"
 [profile.release]
 panic = "abort"
 lto = true
+
+[package.metadata.skyline]
+titleid = "01006A800016E000"
diff --git a/src/common/consts.rs b/src/common/consts.rs
new file mode 100644
index 00000000..bbbb186e
--- /dev/null
+++ b/src/common/consts.rs
@@ -0,0 +1,93 @@
+pub const NONE : i32 = 0;
+
+// Side Taunt
+
+// DI
+/*
+ 0, 0.785398, 1.570796, 2.356194, -3.14159, -2.356194,  -1.570796, -0.785398
+ 0, pi/4,     pi/2,     3pi/4,    pi,       5pi/4,      3pi/2,     7pi/4
+*/
+
+/* DI */
+pub static mut DI_STATE : i32 = NONE;
+pub const DI_RANDOM_IN_AWAY : i32 = 9;
+// const std::vector<std::string> di_items{"None", "Out", "Up Out", "Up", "Up In", "In", "Down In", "Down", "Down Out", "Random"};
+
+// Attack Option
+pub const MASH_NAIR : i32 = 0;
+pub const MASH_FAIR : i32 = 1;
+pub const MASH_BAIR : i32 = 2;
+pub const MASH_UPAIR : i32 = 3;
+pub const MASH_DAIR : i32 = 4;
+pub const MASH_NEUTRAL_B : i32 = 5;
+pub const MASH_SIDE_B : i32 = 6;
+pub const MASH_UP_B : i32 = 7;
+pub const MASH_DOWN_B : i32 = 8;
+pub const MASH_UP_SMASH : i32 = 9;
+pub const MASH_GRAB : i32 = 10;
+// pub const std::vector<std::string> attack_items{"Neutral Air", "Forward Air", "Back Air", "Up Air", "Down Air", "Neutral B", "Side B", "Up B", "Down B", "Up Smash", "Grab"};
+
+// Ledge Option
+pub const RANDOM_LEDGE : i32 = 1;
+pub const NEUTRAL_LEDGE : i32 = 2;
+pub const ROLL_LEDGE : i32 = 3;
+pub const JUMP_LEDGE : i32 = 4;
+pub const ATTACK_LEDGE : i32 = 5;
+// pub const std::vector<std::string> ledge_items{"None", "Random", "Ntrl. Getup", "Roll", "Jump", "Attack"};
+
+// Tech Option
+pub const RANDOM_TECH : i32 = 1;
+pub const TECH_IN_PLACE : i32 = 2;
+pub const TECH_ROLL : i32 = 3;
+pub const TECH_MISS : i32 = 4;
+// pub const std::vector<std::string> tech_items{"None", "Random", "In-Place", "Roll", "Miss Tech"};
+
+// Mash States
+pub const MASH_AIRDODGE : i32 = 1;
+pub const MASH_JUMP : i32 = 2;
+pub const MASH_ATTACK : i32 = 3;
+pub const MASH_SPOTDODGE : i32 = 4;
+pub const MASH_RANDOM : i32 = 5;
+// pub const std::vector<std::string> mash_items{"None", "Airdodge", "Jump", "Attack", "Spotdodge", "Random"};
+
+// Shield States
+pub const SHIELD_INFINITE : i32 = 1;
+pub const SHIELD_HOLD : i32 = 2;
+// pub const std::vector<std::string> shield_items{"None", "Infinite", "Hold"};
+
+// Defensive States
+pub const RANDOM_DEFENSIVE : i32 = 1;
+pub const DEFENSIVE_SPOTDODGE : i32 = 2;
+pub const DEFENSIVE_ROLL : i32 = 3;
+pub const DEFENSIVE_JAB : i32 = 4;
+pub const DEFENSIVE_SHIELD : i32 = 5;
+// pub const std::vector<std::string> defensive_items{"None", "Random", "Spotdodge", "Roll", "Jab", "Flash Shield"};
+
+#[repr(C)]
+pub struct TrainingModpackMenu
+{
+    pub HITBOX_VIS : bool,
+    pub DI_STATE : i32,
+    pub ATTACK_STATE : i32,
+    pub LEDGE_STATE : i32,
+    pub TECH_STATE : i32,
+    pub MASH_STATE : i32,
+    pub SHIELD_STATE : i32,
+    pub DEFENSIVE_STATE : i32,
+}
+
+impl Default for TrainingModpackMenu {
+    fn default() -> TrainingModpackMenu {
+        TrainingModpackMenu {
+            HITBOX_VIS : true,
+            DI_STATE : NONE,
+            ATTACK_STATE : MASH_NAIR,
+            LEDGE_STATE : RANDOM_LEDGE,
+            TECH_STATE : RANDOM_TECH,
+            MASH_STATE : NONE,
+            SHIELD_STATE : NONE,
+            DEFENSIVE_STATE : RANDOM_DEFENSIVE,
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/src/common/mod.rs b/src/common/mod.rs
new file mode 100644
index 00000000..4388f9d6
--- /dev/null
+++ b/src/common/mod.rs
@@ -0,0 +1,90 @@
+pub mod consts;
+
+use smash::lib::lua_const::{*};
+use crate::common::consts::*;
+use smash::app::{self};
+use smash::app::lua_bind::*;
+use smash::hash40;
+
+pub static menu : consts::TrainingModpackMenu = consts::TrainingModpackMenu{
+    HITBOX_VIS : true,
+    DI_STATE : NONE,
+    ATTACK_STATE : MASH_NAIR,
+    LEDGE_STATE : RANDOM_LEDGE,
+    TECH_STATE : RANDOM_TECH,
+    MASH_STATE : NONE,
+    SHIELD_STATE : NONE,
+    DEFENSIVE_STATE : RANDOM_DEFENSIVE,
+};
+
+static fighter_manager_addr: u64 = 0;
+
+extern "C" {
+    #[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"]
+    pub fn is_training_mode() -> bool;
+}
+
+// pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> u8 {
+// 	return (u8)(*(u32*)(module_accessor + 8) >> 28);
+// }
+
+// pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool{
+//     if (get_category(module_accessor) != BATTLE_OBJECT_CATEGORY_FIGHTER)
+//         return false;
+
+//     let entry_id = WorkModule::get_int(module_accessor, FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
+//     u64 fighter_information = FighterManager::get_fighter_information(LOAD64(fighter_manager_addr), entry_id);
+
+//     return FighterInformation::is_operation_cpu(fighter_information);
+// }
+
+pub unsafe fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
+    let status_kind = StatusModule::status_kind(module_accessor) as i32;
+    return status_kind >= FIGHTER_STATUS_KIND_DAMAGE &&
+           status_kind <= FIGHTER_STATUS_KIND_DAMAGE_FALL;
+}
+
+pub unsafe fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
+    let status_kind = StatusModule::status_kind(module_accessor) as i32;
+    let prev_status = StatusModule::prev_status_kind(module_accessor, 0) as i32;
+    // If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun
+    if status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE || 
+        (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE && status_kind == FIGHTER_STATUS_KIND_GUARD_OFF) {
+        return true
+    }
+
+    false
+}
+
+
+pub unsafe fn is_in_landing(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
+    let status_kind = StatusModule::status_kind(module_accessor) as i32;
+    (FIGHTER_STATUS_KIND_LANDING..FIGHTER_STATUS_KIND_LANDING_DAMAGE_LIGHT)
+        .contains(&status_kind)
+}
+
+
+// pub fn perform_defensive_option(module_accessor: &mut app::BattleObjectModuleAccessor, flag: &i32) {
+//     if menu.DEFENSIVE_STATE == RANDOM_DEFENSIVE {
+//         let NUM_DEFENSIVE_CMDS = 4;
+//         let random_cmds = vec![
+//             FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE,
+//             FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_F,
+//             FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_B,
+//             FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_N
+//         ];
+
+//         let random_cmd_index = app::sv_math::rand(hash40("fighter"), random_cmds.len() as i32) as usize;
+//         flag |= random_cmds[random_cmd_index];
+//     } else if menu.DEFENSIVE_STATE == DEFENSIVE_ROLL {
+//         if app::sv_math::rand(hash40("fighter"), 2) == 0 {
+//             flag |= FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_F;
+//         } else {
+//             flag |= FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE_B;
+//         }
+//     } else if menu.DEFENSIVE_STATE == DEFENSIVE_SPOTDODGE {
+//         flag |= FIGHTER_PAD_CMD_CAT1_FLAG_ESCAPE;
+//     } else if menu.DEFENSIVE_STATE == DEFENSIVE_JAB {
+//         flag |= FIGHTER_PAD_CMD_CAT1_FLAG_ATTACK_N;
+//     }
+// }
\ No newline at end of file
diff --git a/src/hitbox_visualizer/mod.rs b/src/hitbox_visualizer/mod.rs
new file mode 100644
index 00000000..bc0fcbcd
--- /dev/null
+++ b/src/hitbox_visualizer/mod.rs
@@ -0,0 +1,272 @@
+use smash::hash40;
+use smash::app::BattleObjectModuleAccessor;
+use smash::app::sv_animcmd::{self};
+use smash::app::lua_bind::*;
+use smash::lib::{self, L2CAgent, L2CValue};
+use smash::phx::{Hash40, Vector3f};
+use smash::lib::lua_const::{*};
+use smash::app::sv_system::{self};
+use smash::app::{self};
+use skyline::logging::hex_dump_ptr;
+use crate::common::*;
+
+/**
+ * Rounds a number to the nearest multiple of another number.
+ */
+ pub fn round_to(val: f32, align: f32) -> f32 { (val / align).round() * align }
+
+ /**
+  * Linearly interpolates between two numbers, without bounds checking.
+  */
+pub fn lerp(min: f32, max: f32, t: f32) -> f32 { min + (max - min) * t }
+ 
+pub fn unlerp(min: f32, max: f32, val: f32) -> f32 { (val - min) / (max - min) }
+ 
+ /**
+  * Linearly interpolates between two numbers, with bounds checking.
+  */
+pub fn lerp_bounded(min: f32, max: f32, t: f32) -> f32 {
+    if t <= 0.0 { min } else { if t >= 1.0 { max } else { lerp(min, max, t) } }
+}
+ 
+pub fn unlerp_bounded(min: f32, max: f32, val: f32) -> f32 {
+    if val <= min { 0.0 } else { if val >= max { 1.0 } else { unlerp(min, max, val)} }
+}
+ 
+ /**
+  * Linearly nterpolates between two colors, with bounds checking, accounting for
+  * gamma. arguments:
+  * - min_color (Vector3f) -- xyz maps to rgb, components are usually in the
+  * range [0.0f, 1.0f] but can go beyond to account for super-bright or
+  * super-dark colors
+  * - max_Color (Vector3f) -- same as minColor
+  * - t (float) -- how far to interpolate between the colors
+  * - gamma (float = 2.0f) -- used for color correction, helps avoid ugly dark
+  * colors when interpolating b/t bright colors
+  */
+ 
+pub fn color_lerp(min_color: Vector3f, max_color: Vector3f, t: f32,
+                    gamma: f32) -> Vector3f {
+     let gamma_inv = 1.0 / gamma;
+     let align =
+         1.0 / 255.0;  // color components must be a multiple of 1/255
+    Vector3f{x: round_to(lerp_bounded(min_color.x.powf(gamma),
+                                        max_color.x.powf(gamma), t).powf(
+                           gamma_inv),
+                      align),
+             y: round_to(lerp_bounded(min_color.y.powf(gamma),
+                                        max_color.y.powf(gamma), t).powf(
+                           gamma_inv),
+                      align),
+             z: round_to(lerp_bounded(min_color.z.powf(gamma),
+                                        max_color.z.powf(gamma), t).powf(
+                           gamma_inv),
+                      align)}
+ }
+
+const ID_COLORS: &[Vector3f] = &[
+    // used to tint the hitbox effects -- make sure that at least one component
+    // is equal to 1.0
+    Vector3f{x: 1.0, y: 0.0, z: 0.0},  // #ff0000 (red)
+    Vector3f{x: 1.0, y: 0.4, z: 0.0},  // #ff9900 (orange)
+    Vector3f{x: 0.8, y: 1.0, z: 0.0},  // #ccff00 (yellow)
+    Vector3f{x: 0.2, y: 1.0, z: 0.2},  // #00ff33 (green)
+    Vector3f{x: 0.0, y: 0.8, z: 1.0},  // #00ccff (sky blue)
+    Vector3f{x: 0.4, y: 0.4, z: 1.0},  // #6666ff (blue)
+    Vector3f{x: 0.8, y: 0.0, z: 1.0},  // #cc00ff (purple)
+    Vector3f{x: 1.0, y: 0.2, z: 0.8},  // #ff33cc (pink)
+];
+const MAX_EFFECTS_PER_HITBOX: i32 = 16; // max # of circles drawn for an extended hitbox
+
+pub unsafe fn wrap(func: unsafe extern "C" fn(lua_state: u64), agent: &mut L2CAgent, vals: &mut Vec::<L2CValue>) {
+    agent.clear_lua_stack();
+    for val in vals {
+            agent.push_lua_stack(val);
+    }
+    func(agent.lua_state_agent);
+    agent.clear_lua_stack();
+}
+
+pub unsafe fn generate_hitbox_effects(l2c_agent: &mut L2CAgent, bone: L2CValue,
+        size: L2CValue, x: L2CValue, y: L2CValue,
+        z: L2CValue, x2: L2CValue, y2: L2CValue,
+        z2: L2CValue, color: Vector3f) {
+    let red = L2CValue::new_num(color.x);
+    let green = L2CValue::new_num(color.y);
+    let blue = L2CValue::new_num(color.z);
+
+    let size_mult = 19.0 / 200.0;
+
+    let shield_effect = L2CValue::new_int(hash40("sys_shield"));
+    let zero_rot = L2CValue::new_num(0.0);
+    let terminate = L2CValue::new_bool(true);
+    let effect_size = L2CValue::new_num(size.get_num() * size_mult);
+
+    let rate = L2CValue::new_num(8.0);
+
+    let x_dist : f32;
+    let y_dist : f32;
+    let z_dist : f32;
+    let mut n_effects : i32;
+    if let lib::L2CValueType::Void = x2.val_type{ // && let lib::L2CValueType::Void = y2.val_type && let lib::L2CValueType::Void = z2.val_type {  // extended hitbox
+        x_dist = 0.0; y_dist = 0.0; z_dist = 0.0;
+        n_effects = 1;
+    } 
+    else {  // non-extended hitbox
+        x_dist = x2.get_num() - x.get_num();
+        y_dist = y2.get_num() - y.get_num();
+        z_dist = z2.get_num() - z.get_num();
+        let dist_sq : f32 = x_dist * x_dist + y_dist * y_dist + z_dist * z_dist;
+        let dist = dist_sq.sqrt();
+        n_effects = ((dist / (size.get_num() * 1.75)) + 1.0).ceil() as i32;  // just enough effects to form a continuous line
+        if n_effects < 2 {
+            n_effects = 2;
+        } else if n_effects > MAX_EFFECTS_PER_HITBOX {
+            n_effects = MAX_EFFECTS_PER_HITBOX;
+        }
+    }
+
+    for i in  0..n_effects {
+        let mut t = 0.0;
+        if n_effects > 1 {
+            t = (i as f32) / ((n_effects - 1) as f32);
+        }
+        let x_curr = L2CValue::new_num(x.get_num() + x_dist * t);
+        let y_curr = L2CValue::new_num(y.get_num() + y_dist * t);
+        let z_curr = L2CValue::new_num(z.get_num() + z_dist * t);
+
+        wrap(sv_animcmd::EFFECT_FOLLOW_NO_SCALE,
+            l2c_agent, 
+            &mut [
+            shield_effect, bone, x_curr, 
+            y_curr, z_curr, zero_rot, zero_rot,
+            zero_rot, effect_size, terminate].to_vec());
+
+        // set to hitbox ID color
+        wrap(sv_animcmd::LAST_EFFECT_SET_COLOR, l2c_agent,
+            &mut [red, green, blue].to_vec());
+
+        // speed up animation by rate to remove pulsing effect
+        wrap(sv_animcmd::LAST_EFFECT_SET_RATE, l2c_agent, 
+            &mut [rate].to_vec());
+    }
+}
+
+#[allow(unused_unsafe)]
+#[skyline::hook(replace = sv_animcmd::ATTACK)]
+unsafe fn handle_attack(lua_state: u64) {
+    let mut l2c_agent = L2CAgent::new(lua_state);
+
+    // get all necessary hitbox params
+    let id = l2c_agent.pop_lua_stack(1);      // int
+    let bone = l2c_agent.pop_lua_stack(3);    // hash40
+    let damage = l2c_agent.pop_lua_stack(4);  // float
+    let _angle = l2c_agent.pop_lua_stack(5);   // int
+    let kbg = l2c_agent.pop_lua_stack(6);     // int
+    let fkb = l2c_agent.pop_lua_stack(7);     // int
+    let bkb = l2c_agent.pop_lua_stack(8);     // int
+    let size = l2c_agent.pop_lua_stack(9);    // float
+    let x = l2c_agent.pop_lua_stack(10);      // float
+    let y = l2c_agent.pop_lua_stack(11);      // float
+    let z = l2c_agent.pop_lua_stack(12);      // float
+    let x2 = l2c_agent.pop_lua_stack(13);     // float or void
+    let y2 = l2c_agent.pop_lua_stack(14);     // float or void
+    let z2 = l2c_agent.pop_lua_stack(15);     // float or void
+
+    original!()(lua_state);
+
+    if menu.HITBOX_VIS && is_training_mode() {  // generate hitbox effect(s)
+        let color_scale: f32;
+        if false {  // color intensity scales with damage
+            color_scale = unlerp_bounded(1.0, 18.0, damage.get_num());
+        } else {  // color intensity scales with total KB
+            // calculate the expected KB a character with 95 weight will receive
+            // at 80% pre-hit
+            let target_percent = 80.0;
+            let target_weight = 95.0;
+            let percent_component: f32;
+            if fkb.get_int() > 0 {
+                percent_component = (10.0 + fkb.get_int() as f32) * 0.1 * (1.0 + fkb.get_int() as f32 * 0.5);
+            } else {
+                percent_component =  (target_percent + damage.get_num()) * 0.1 *
+                                    (1.0 + damage.get_num() * 0.5);
+            }
+            let weight_component: f32 = 200.0 / (target_weight + 100.0);
+            let kb: f32 = (percent_component * weight_component * 1.4 + 18.0) *
+                           (kbg.get_int() as f32 * 0.01) + bkb.get_int() as f32;
+            color_scale = unlerp_bounded(50.0, 200.0, kb);
+        }
+        // non-linear scaling to magnify
+        // differences at lower values
+        let color_t: f32 = 0.8 + 0.2 * color_scale.powf(0.5);
+        let color = color_lerp(
+            Vector3f{x: 1.0, y: 1.0, z: 1.0},
+            ID_COLORS[(id.get_int() % 8) as usize],
+            color_t, 
+            2.0
+        );
+        generate_hitbox_effects(&mut l2c_agent, bone, size, x, y, z, x2, y2, z2, color);
+    }
+}
+
+#[allow(unused_unsafe)]
+#[skyline::hook(replace = sv_animcmd::CATCH)]
+unsafe fn handle_catch(lua_state: u64) {
+    let mut l2c_agent = L2CAgent::new(lua_state);
+
+    // get all necessary grabbox params
+    let id = l2c_agent.pop_lua_stack(1);     // int
+    let joint = l2c_agent.pop_lua_stack(2);  // hash40
+    let size = l2c_agent.pop_lua_stack(3);   // float
+    let x = l2c_agent.pop_lua_stack(4);      // float
+    let y = l2c_agent.pop_lua_stack(5);      // float
+    let z = l2c_agent.pop_lua_stack(6);      // float
+    let x2 = l2c_agent.pop_lua_stack(7);     // float or void
+    let y2 = l2c_agent.pop_lua_stack(8);     // float or void
+    let z2 = l2c_agent.pop_lua_stack(9);     // float or void
+
+    original!()(lua_state);
+
+    if menu.HITBOX_VIS && is_training_mode() {
+        generate_hitbox_effects(&mut l2c_agent, joint, size, x, y, z, x2, y2, z2, ID_COLORS[(id.get_int() + 3 % 8) as usize]);
+    }
+}
+
+pub unsafe fn is_shielding(module_accessor: *mut BattleObjectModuleAccessor) -> bool {
+    let status_kind = StatusModule::status_kind(module_accessor) as i32;
+    (FIGHTER_STATUS_KIND_GUARD_ON..=FIGHTER_STATUS_KIND_GUARD_OFF).contains(&status_kind)
+}
+
+#[allow(unused_unsafe)]
+#[skyline::hook(replace = AttackModule::clear_all)]
+pub unsafe fn handle_clear_all(module_accessor: *mut BattleObjectModuleAccessor) {
+    if is_training_mode() {
+        // only if we're not shielding
+        if !is_shielding(module_accessor) {
+            EffectModule::kill_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false, true);
+        }
+    }
+
+    original!()(module_accessor);
+}
+
+#[allow(unused_unsafe)]
+#[skyline::hook(replace = GrabModule::set_rebound)]
+pub unsafe fn handle_set_rebound(module_accessor: *mut BattleObjectModuleAccessor, rebound: bool) {
+    if is_training_mode() && rebound == false {
+        // only if we're not shielding
+        if !is_shielding(module_accessor) {
+            EffectModule::kill_kind(module_accessor, Hash40{hash: hash40("sys_shield")}, false, true);
+        }
+    }
+
+    original!()(module_accessor, rebound);
+}
+
+pub fn hitbox_visualization() {
+    println!("Applying hitbox visualization mods.");
+    skyline::install_hook!(handle_attack);
+    skyline::install_hook!(handle_catch);
+    skyline::install_hook!(handle_clear_all);
+    skyline::install_hook!(handle_set_rebound);
+}
diff --git a/src/lib.rs b/src/lib.rs
index 98dec292..7aba96cc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,30 +1,66 @@
 #![feature(proc_macro_hygiene)]
+#![allow(unused_imports)]
+#![allow(unused_variables)]
+#![allow(dead_code)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![feature(with_options)]
 
-use skyline::{hook, install_hook};
+mod hitbox_visualizer;
+mod common;
 
-extern "C" fn test() -> u32 {
-    2
+use smash::hash40;
+use smash::lib::lua_const::{*};
+use smash::lib::{self, L2CAgent, L2CValue};
+use smash::app::{self};
+use smash::app::lua_bind::{*};
+use smash::app::sv_animcmd::{self};
+use smash::app::sv_system::{self};
+use skyline::libc::{size_t, c_int, c_void, strlen};
+use smash::Result;
+use skyline::nn;
+use skyline::patching::patch_data_from_text;
+use skyline::{from_c_str, c_str};
+use std::fs;
+
+#[allow(unused_unsafe)]
+#[skyline::hook(replace = nn::ro::LoadModule)]
+pub unsafe fn handle_load_module(
+    p_out_module: *mut skyline::nn::ro::Module, 
+    p_image: *const c_void, 
+    buffer: *mut c_void, 
+    buffer_size: size_t, 
+    flag: c_int) -> Result {
+
+    let ret = original!()(p_out_module, p_image, buffer, buffer_size, flag);
+
+    let name = from_c_str(&(*p_out_module).Name as *const u8);
+    println!("[handleLoadModule] NRO name: {}\n", name);
+    let text_start = (*(*p_out_module).ModuleObject).module_base;
+    println!("Module base: {}\n", text_start);
+    if name.starts_with("common") {
+        println!("Is common!");
+        // raw const_value_table is at : 0x635b70
+        let fighter_status_kind_fall : u64 = 0x8ee6c39e9be4f0b5;
+        let res = match patch_data_from_text(text_start as *const u8, 0x6362b8, &fighter_status_kind_fall) {
+            Ok(v) => format!("Patched!"),
+            Err(e) => format!("Error patching with e: {}", e)
+        };
+
+        println!("{}", res);
+    }
+
+    ret
 }
 
-#[hook(replace = test)]
-fn test_replacement() -> u32 {
-
-    let original_test = original!();
-
-    let val = original_test();
-
-    println!("[override] original value: {}", val); // 2
-
-    val + 1
-}
-
-#[skyline::main(name = "skyline_rs_template")]
+#[skyline::main(name = "test")]
 pub fn main() {
-    println!("Hello from Skyline Rust Plugin!");
+    println!("Training modpack initialized.");
+    hitbox_visualizer::hitbox_visualization();
 
-    install_hook!(test_replacement);
+    println!("OpenMode_Write: {} {}", nn::fs::OpenMode_OpenMode_Write, nn::fs::OpenMode_OpenMode_Write as i32);
+    let buffer = format!("{:x}", &common::menu as *const _ as u64);
+    println!("Writing training_modpack.log with {}...\n", buffer);
 
-    let x = test();
-
-    println!("[main] test returned: {}", x); // 3
+    // skyline::install_hook!(handle_load_module);
 }