#![feature(proc_macro_hygiene)] #![feature(iter_intersperse)] #![feature(const_mut_refs)] #![feature(exclusive_range_pattern)] #![feature(c_variadic)] #![feature(pointer_byte_offsets)] #![allow( clippy::borrow_interior_mutable_const, clippy::declare_interior_mutable_const, clippy::not_unsafe_ptr_arg_deref, clippy::missing_safety_doc, clippy::wrong_self_convention, clippy::option_map_unit_fn, clippy::transmute_num_to_bytes )] use std::fs; use std::path::PathBuf; use skyline::nro::{self, NroInfo}; use training_mod_consts::{extra_bitflag_impls, OnOff, LEGACY_TRAINING_MODPACK_ROOT}; use crate::common::button_config::DEFAULT_OPEN_MENU_CONFIG; use crate::common::events::events_loop; use crate::common::*; use crate::consts::TRAINING_MODPACK_ROOT; use crate::events::{Event, EVENT_QUEUE}; use crate::logging::*; use crate::training::ui::notifications::notification; pub mod common; mod hazard_manager; mod hitbox_visualizer; mod training; mod logging; fn nro_main(nro: &NroInfo<'_>) { if nro.module.isLoaded { return; } if nro.name == "common" { skyline::install_hooks!( training::shield::handle_sub_guard_cont, training::directional_influence::handle_correct_damage_vector_common, training::tech::handle_change_status, ); } } #[skyline::main(name = "training_modpack")] pub fn main() { std::panic::set_hook(Box::new(|info| { let location = info.location().unwrap(); let msg = match info.payload().downcast_ref::<&'static str>() { Some(s) => *s, None => match info.payload().downcast_ref::() { Some(s) => &s[..], None => "Box", }, }; let err_msg = format!("SSBU Training Modpack has panicked at '{msg}', {location}"); skyline::error::show_error( 69, "SSBU Training Modpack has panicked! Please open the details and send a screenshot to the developer, then close the game.\n", err_msg.as_str(), ); })); init_logger().unwrap(); info!("Initialized."); unsafe { EVENT_QUEUE.push(Event::smash_open()); notification("Training Modpack".to_string(), "Welcome!".to_string(), 60); } hitbox_visualizer::hitbox_visualization(); hazard_manager::hazard_manager(); training::training_mods(); nro::add_hook(nro_main).unwrap(); fs::create_dir_all(TRAINING_MODPACK_ROOT) .expect("Could not create Training Modpack root folder!"); // Migrate legacy if exists if fs::metadata(LEGACY_TRAINING_MODPACK_ROOT).is_ok() { for entry in fs::read_dir(LEGACY_TRAINING_MODPACK_ROOT).unwrap() { let entry = entry.unwrap(); let src_path = &entry.path(); let dest_path = &PathBuf::from(TRAINING_MODPACK_ROOT).join(entry.file_name()); fs::rename(src_path, dest_path).unwrap_or_else(|e| { error!("Could not move file from {src_path:#?} to {dest_path:#?} with error {e}") }); } fs::remove_dir_all(LEGACY_TRAINING_MODPACK_ROOT).unwrap_or_else(|e| { error!("Could not delete legacy Training Modpack folder with error {e}") }); } menu::load_from_file(); if !is_emulator() { info!("Performing version check..."); let _updater = std::thread::Builder::new() .stack_size(0x20000) .spawn(move || { release::perform_version_check(); }) .unwrap(); let _result = _updater.join(); } else { info!("Skipping version check because we are using an emulator"); } unsafe { notification("Training Modpack".to_string(), "Welcome!".to_string(), 60); notification( "Open Menu".to_string(), if MENU.menu_open_start_press == OnOff::On { "Start".to_string() } else { DEFAULT_OPEN_MENU_CONFIG.to_string() }, 120, ); notification( "Save State".to_string(), MENU.save_state_save.to_string(), 120, ); notification( "Load State".to_string(), MENU.save_state_load.to_string(), 120, ); notification( "Input Record".to_string(), MENU.input_record.to_string(), 120, ); notification( "Input Playback".to_string(), MENU.input_playback.to_string(), 120, ); } std::thread::spawn(events_loop); }