1
0
Fork 0
mirror of https://github.com/jugeeya/UltimateTrainingModpack.git synced 2024-11-20 00:46:34 +00:00

Use UI text box for Frame Advantage; add ui2d backend (#449)

* A bunch of things

* Current progress

* Fix for ResAnimationContent

* Figure out Parts*

* Cleanup, just because

* New pane working!!!

* New null pane for hierarchy

* Success with parent pane

* Generate multiple panes

* Multiple panes, almost working text color

* MaterialColor test, but fails

* Forgot bitfield-struct

* Vtable for material. Fixes SetWhiteColor!

* Refactor color changing, change naming scheme

* Just Frame Advantage

* Merge

* Delete T_test.txt

* Delete set_txt_num_01.txt

* Delete libtraining_modpack.nro

* Format Rust code using rustfmt

* Ignore shell scripts in repo languages

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
jugeeya 2022-12-22 14:22:37 -08:00 committed by GitHub
parent c8349005c3
commit a1c9e35b19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 808 additions and 13 deletions

View file

@ -2,7 +2,7 @@
// https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/docker-from-docker // https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/docker-from-docker
{ {
"name": "Cargo Skyline", "name": "Cargo Skyline",
"image": "jugeeya/cargo-skyline:3.2.0", "image": "jugeeya/cargo-skyline:3.2.0-no-dkp",
"mounts": [ "mounts": [
"source=ultimatetrainingmodpack-bashhistory,target=/commandhistory,type=volume" "source=ultimatetrainingmodpack-bashhistory,target=/commandhistory,type=volume"
], ],

2
.gitattributes vendored
View file

@ -1,2 +1,4 @@
.devcontainer/** linguist-vendored .devcontainer/** linguist-vendored
.vscode/** linguist-vendored .vscode/** linguist-vendored
ryujinx_build.ps1 linguist-vendored
ryujinx_build.sh linguist-vendored

View file

@ -12,6 +12,7 @@ skyline = { git = "https://github.com/ultimate-research/skyline-rs.git" }
skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git", branch = "no-cache" } skyline_smash = { git = "https://github.com/ultimate-research/skyline-smash.git", branch = "no-cache" }
skyline-web = { git = "https://github.com/skyline-rs/skyline-web.git" } skyline-web = { git = "https://github.com/skyline-rs/skyline-web.git" }
bitflags = "1.2.1" bitflags = "1.2.1"
bitfield-struct = "0.1.8"
parking_lot = { version = "0.12.0", features = ["nightly"] } parking_lot = { version = "0.12.0", features = ["nightly"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
owo-colors = "2.1.0" owo-colors = "2.1.0"

4
ryujinx_build.ps1 vendored Normal file
View file

@ -0,0 +1,4 @@
$IP=(Test-Connection -ComputerName (hostname) -Count 1 | Select -ExpandProperty IPV4Address).IPAddressToString
cargo skyline build --release
Copy-Item target/aarch64-skyline-switch/release/libtraining_modpack.nro 'C:\Users\Jdsam\AppData\Roaming\Ryujinx\mods\contents\01006A800016E000\romfs\skyline\plugins\'
cargo skyline listen --ip=$IP

View file

@ -2,6 +2,7 @@
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(exclusive_range_pattern)] #![feature(exclusive_range_pattern)]
#![feature(once_cell)] #![feature(once_cell)]
#![feature(c_variadic)]
#![allow( #![allow(
clippy::borrow_interior_mutable_const, clippy::borrow_interior_mutable_const,
clippy::not_unsafe_ptr_arg_deref, clippy::not_unsafe_ptr_arg_deref,
@ -84,6 +85,8 @@ pub fn main() {
EVENT_QUEUE.push(Event::smash_open()); EVENT_QUEUE.push(Event::smash_open());
} }
training::ui_hacks::install_hooks();
hitbox_visualizer::hitbox_visualization(); hitbox_visualizer::hitbox_visualization();
hazard_manager::hazard_manager(); hazard_manager::hazard_manager();
training::training_mods(); training::training_mods();

View file

@ -44,15 +44,9 @@ unsafe fn is_actionable(module_accessor: *mut app::BattleObjectModuleAccessor) -
}) || CancelModule::is_enable_cancel(module_accessor) }) || CancelModule::is_enable_cancel(module_accessor)
} }
fn update_frame_advantage( fn update_frame_advantage(new_frame_adv: i32) {
module_accessor: *mut app::BattleObjectModuleAccessor,
new_frame_adv: i32,
) {
unsafe { unsafe {
FRAME_ADVANTAGE = new_frame_adv; FRAME_ADVANTAGE = new_frame_adv;
if MENU.frame_advantage == consts::OnOff::On {
raygun_printer::print_string(&mut *module_accessor, &format!("{}", FRAME_ADVANTAGE));
}
} }
} }
@ -87,7 +81,6 @@ pub unsafe fn is_enable_transition_term(
let cpu_module_accessor = get_module_accessor(FighterId::CPU); let cpu_module_accessor = get_module_accessor(FighterId::CPU);
if was_in_hitstun(cpu_module_accessor) || was_in_shieldstun(cpu_module_accessor) { if was_in_hitstun(cpu_module_accessor) || was_in_shieldstun(cpu_module_accessor) {
update_frame_advantage( update_frame_advantage(
module_accessor,
(CPU_ACTIVE_FRAME as i64 - PLAYER_ACTIVE_FRAME as i64) as i32, (CPU_ACTIVE_FRAME as i64 - PLAYER_ACTIVE_FRAME as i64) as i32,
); );
} }
@ -139,10 +132,7 @@ pub unsafe fn get_command_flag_cat(module_accessor: &mut app::BattleObjectModule
// if both are now active // if both are now active
if PLAYER_ACTIONABLE && CPU_ACTIONABLE && FRAME_ADVANTAGE_CHECK { if PLAYER_ACTIONABLE && CPU_ACTIONABLE && FRAME_ADVANTAGE_CHECK {
if was_in_hitstun(cpu_module_accessor) || was_in_shieldstun(cpu_module_accessor) { if was_in_hitstun(cpu_module_accessor) || was_in_shieldstun(cpu_module_accessor) {
update_frame_advantage( update_frame_advantage((CPU_ACTIVE_FRAME as i64 - PLAYER_ACTIVE_FRAME as i64) as i32);
player_module_accessor,
(CPU_ACTIVE_FRAME as i64 - PLAYER_ACTIVE_FRAME as i64) as i32,
);
} }
frame_counter::stop_counting(FRAME_COUNTER_INDEX); frame_counter::stop_counting(FRAME_COUNTER_INDEX);

View file

@ -23,6 +23,8 @@ pub mod sdi;
pub mod shield; pub mod shield;
pub mod tech; pub mod tech;
pub mod throw; pub mod throw;
pub mod ui;
pub mod ui_hacks;
mod air_dodge_direction; mod air_dodge_direction;
mod attack_angle; mod attack_angle;

432
src/training/ui/mod.rs Normal file
View file

@ -0,0 +1,432 @@
#![allow(dead_code)]
use bitfield_struct::bitfield;
mod resources;
pub use resources::*;
macro_rules! c_str {
($l:tt) => {
[$l.as_bytes(), "\u{0}".as_bytes()].concat().as_ptr()
};
}
#[repr(C)]
#[derive(Debug)]
pub struct ResAnimationContent {
name: [skyline::libc::c_char; 28],
count: u8,
anim_content_type: u8,
padding: [skyline::libc::c_char; 2],
}
/**
* Block Header Kind
*
* ANIM_TAG: pat1
* ANIM_SHARE: pah1
* ANIM_INFO: pai1
*/
#[repr(C)]
#[derive(Debug)]
pub struct ResAnimationBlock {
block_header_kind: u32,
block_header_size: u32,
num_frames: u16,
is_loop: bool,
pad: [skyline::libc::c_char; 1],
file_count: u16,
anim_cont_count: u16,
anim_cont_offsets_offset: u32,
}
#[repr(C)]
pub struct AnimTransform {
res_animation_block: *mut ResAnimationBlock,
frame: f32,
enabled: bool,
}
impl AnimTransform {
pub unsafe fn parse_anim_transform(&mut self) {
let res_animation_block_data_start = (*self).res_animation_block as u64;
let res_animation_block = &*(*self).res_animation_block;
let mut anim_cont_offsets = (res_animation_block_data_start
+ res_animation_block.anim_cont_offsets_offset as u64)
as *const u32;
for anim_cont_idx in 0..res_animation_block.anim_cont_count {
let anim_cont_offset = *anim_cont_offsets;
let res_animation_cont = (res_animation_block_data_start + anim_cont_offset as u64)
as *const ResAnimationContent;
let name = skyline::try_from_c_str((*res_animation_cont).name.as_ptr())
.unwrap_or("UNKNOWN".to_string());
let anim_type = (*res_animation_cont).anim_content_type;
let frame = (*self).frame;
println!(
"animTransform/resAnimationContent_{anim_cont_idx}: {name} of type {anim_type} on frame {frame}",
);
// AnimContentType 1 == MATERIAL
if (name == "dig_3_anim" || name == "set_dmg_num_3") && anim_type == 1 {
(*self).frame = 4.0;
}
if (name == "dig_2_anim" || name == "set_dmg_num_2") && anim_type == 1 {
(*self).frame = 2.0;
}
if (name == "dig_1_anim" || name == "set_dmg_num_1") && anim_type == 1 {
(*self).frame = 8.0;
}
anim_cont_offsets = anim_cont_offsets.add(1);
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct AnimTransformNode {
prev: *mut AnimTransformNode,
next: *mut AnimTransformNode,
}
impl AnimTransformNode {
pub unsafe fn iterate_anim_list(&mut self) {
let mut curr = self as *mut AnimTransformNode;
let mut _anim_idx = 0;
while !curr.is_null() {
// Only if valid
if curr != (*curr).next {
let anim_transform = (curr as *mut u64).add(2) as *mut AnimTransform;
anim_transform.as_mut().unwrap().parse_anim_transform();
}
curr = (*curr).next;
_anim_idx += 1;
if curr == self as *mut AnimTransformNode || curr == (*curr).next {
break;
}
}
}
}
#[repr(C)]
pub struct AnimTransformList {
root: AnimTransformNode,
}
#[repr(C, align(8))]
#[derive(Debug, Copy, Clone)]
pub struct Pane {
vtable: u64,
pub link: PaneNode,
pub parent: *mut Pane,
pub children_list: PaneNode,
pub pos_x: f32,
pub pos_y: f32,
pos_z: f32,
rot_x: f32,
rot_y: f32,
rot_z: f32,
pub scale_x: f32,
pub scale_y: f32,
pub size_x: f32,
pub size_y: f32,
pub flags: u8,
pub alpha: u8,
pub global_alpha: u8,
base_position: u8,
flag_ex: u8,
// This is supposed to be 3 bytes padding + flags of 4 bytes + padding of 4 bytes
pad: [u8; 3 + 4 + 4 + 8],
global_matrix: [[f32; 3]; 4],
user_matrix: *const u64,
ext_user_data_list: *const u64,
pub name: [skyline::libc::c_char; 25],
user_data: [skyline::libc::c_char; 9],
}
impl Pane {
pub unsafe fn find_pane_by_name_recursive(&self, s: &str) -> Option<&mut Pane> {
find_pane_by_name_recursive(self, c_str!(s)).as_mut()
}
pub unsafe fn find_pane_by_name(&self, s: &str, recursive: bool) -> Option<&mut Pane> {
find_pane_by_name(self, c_str!(s), recursive).as_mut()
}
pub unsafe fn set_text_string(&self, s: &str) {
pane_set_text_string(self, c_str!(s));
}
pub unsafe fn remove_child(&self, child: &Pane) {
pane_remove_child(self, child as *const Pane);
}
pub unsafe fn append_child(&self, child: &Pane) {
pane_append_child(self, child as *const Pane);
}
pub unsafe fn as_parts(&mut self) -> *mut Parts {
self as *mut Pane as *mut Parts
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Parts {
pub pane: Pane,
// Some IntrusiveList
link: PaneNode,
pub layout: *mut Layout,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Picture {
pub pane: Pane,
material: *mut u8,
vertex_colors: [[u8; 4]; 4],
shared_memory: *mut u8,
}
#[bitfield(u16)]
pub struct TextBoxBits {
#[bits(2)]
text_alignment: u8,
#[bits(1)]
is_ptdirty: u8,
shadow_enabled: bool,
invisible_border_enabled: bool,
double_drawn_border_enabled: bool,
width_limit_enabled: bool,
per_character_transform_enabled: bool,
center_ceiling_enabled: bool,
per_character_transform_split_by_char_width: bool,
per_character_transform_auto_shadow_alpha: bool,
draw_from_right_to_left: bool,
per_character_transform_origin_to_center: bool,
per_character_transform_fix_space: bool,
linefeed_by_character_height_enabled: bool,
per_character_transform_split_by_char_width_insert_space_enabled: bool,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct TextBox {
pub pane: Pane,
// Actually a union
m_text_buf: *const skyline::libc::c_char,
m_p_text_id: *const skyline::libc::c_char,
m_text_colors: [[u8; 4]; 2],
m_p_font: *const skyline::libc::c_void,
m_font_size_x: f32,
m_font_size_y: f32,
m_line_space: f32,
m_char_space: f32,
// Actually a union
m_p_tag_processor: *const skyline::libc::c_char,
m_text_buf_len: u16,
m_text_len: u16,
m_bits: TextBoxBits,
m_text_position: u8,
m_is_utf8: bool,
m_italic_ratio: f32,
m_shadow_offset_x: f32,
m_shadow_offset_y: f32,
m_shadow_scale_x: f32,
m_shadow_scale_y: f32,
m_shadow_top_color: [u8; 4],
m_shadow_bottom_color: [u8; 4],
m_shadow_italic_ratio: f32,
m_p_line_width_offset: *const skyline::libc::c_void,
pub m_p_material: *mut Material,
m_p_disp_string_buf: *const skyline::libc::c_void,
m_p_per_character_transform: *const skyline::libc::c_void,
}
impl TextBox {
pub fn set_color(&mut self, r: u8, g: u8, b: u8, a: u8) {
let input_color = [r, g, b, a];
let mut dirty: bool = false;
self.m_text_colors
.iter_mut()
.for_each(|top_or_bottom_color| {
if *top_or_bottom_color != input_color {
dirty = true;
}
*top_or_bottom_color = input_color;
});
if dirty {
self.m_bits.set_is_ptdirty(1);
}
}
}
#[repr(C)]
pub union MaterialColor {
byte_color: [[u8; 4]; 2],
p_float_color: *mut *mut f32,
}
use std::fmt;
impl fmt::Debug for MaterialColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
f.debug_struct("MaterialColor")
.field("byteColor", &self.byte_color)
.field("pFloatColor", &self.p_float_color)
.finish()
}
}
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum MaterialColorType {
BlackColor,
WhiteColor,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum MaterialFlags {
FlagsUserAllocated,
FlagsTextureOnly,
FlagsThresholdingAlphaInterpolation,
FlagsBlackColorFloat,
FlagsWhiteColorFloat,
FlagsDynamicAllocatedColorData,
}
#[repr(C)]
#[derive(Debug)]
pub struct Material {
vtable: u64,
m_colors: MaterialColor,
// Actually a struct
m_mem_cap: u32,
// Actually a struct
m_mem_count: u32,
m_p_mem: *mut skyline::libc::c_void,
m_p_shader_info: *const skyline::libc::c_void,
m_p_name: *const skyline::libc::c_char,
m_vertex_shader_constant_buffer_offset: u32,
m_pixel_shader_constant_buffer_offset: u32,
m_p_user_shader_constant_buffer_information: *const skyline::libc::c_void,
m_p_blend_state: *const skyline::libc::c_void,
m_packed_values: u8,
m_flag: u8,
m_shader_variation: u16,
}
impl Material {
pub fn set_color_int(&mut self, idx: usize, r: u8, g: u8, b: u8, a: u8) {
let input_color = [r, g, b, a];
unsafe {
self.m_colors.byte_color[idx] = input_color;
}
}
pub fn set_color_float(&mut self, idx: usize, r: f32, g: f32, b: f32, a: f32) {
unsafe {
*(*(self.m_colors.p_float_color.add(idx)).add(0)) = r;
*(*(self.m_colors.p_float_color.add(idx)).add(1)) = g;
*(*(self.m_colors.p_float_color.add(idx)).add(2)) = b;
*(*(self.m_colors.p_float_color.add(idx)).add(3)) = a;
}
}
pub fn set_color(&mut self, color_type: MaterialColorType, r: f32, g: f32, b: f32, a: f32) {
let (is_float_flag, idx) = if color_type == MaterialColorType::BlackColor {
(MaterialFlags::FlagsBlackColorFloat as u8, 0)
} else {
(MaterialFlags::FlagsWhiteColorFloat as u8, 1)
};
if self.m_flag & (0x1 << is_float_flag) != 0 {
self.set_color_float(idx, r, g, b, a);
} else {
self.set_color_int(idx, r as u8, g as u8, b as u8, a as u8);
}
}
pub fn set_white_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
self.set_color(MaterialColorType::WhiteColor, r, g, b, a);
}
pub fn set_black_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
self.set_color(MaterialColorType::BlackColor, r, g, b, a);
}
}
#[repr(C)]
#[derive(Debug)]
pub struct RawLayout {
pub anim_trans_list: AnimTransformNode,
pub root_pane: *const Pane,
group_container: u64,
layout_size: f64,
pub layout_name: *const skyline::libc::c_char,
}
#[derive(Debug, Copy, Clone)]
pub struct PaneNode {
pub prev: *mut PaneNode,
pub next: *mut PaneNode,
}
#[repr(C)]
pub struct Group {
pane_list: PaneNode,
name: *const skyline::libc::c_char,
}
#[repr(C)]
pub struct GroupContainer {}
#[repr(C)]
#[derive(Debug)]
pub struct Layout {
vtable: u64,
pub raw_layout: RawLayout,
}
#[skyline::from_offset(0x59970)]
pub unsafe fn find_pane_by_name_recursive(
pane: *const Pane,
s: *const skyline::libc::c_char,
) -> *mut Pane;
#[skyline::from_offset(0x583c0)]
pub unsafe fn find_pane_by_name(
pane: *const Pane,
s: *const skyline::libc::c_char,
recursive: bool,
) -> *mut Pane;
#[skyline::from_offset(0x37a1270)]
pub unsafe fn pane_set_text_string(pane: *const Pane, s: *const skyline::libc::c_char);
#[skyline::from_offset(0x58290)]
pub unsafe fn pane_remove_child(pane: *const Pane, child: *const Pane);
#[skyline::from_offset(0x58250)]
pub unsafe fn pane_append_child(pane: *const Pane, child: *const Pane);
pub unsafe fn get_typeinfo_name(cls_vtable: u64) -> String {
let typeinfo_ptr_addr = (cls_vtable - 8) as *const u64;
let typeinfo_addr = *typeinfo_ptr_addr;
let typeinfo_name_ptr_addr = (typeinfo_addr + 8) as *const u64;
let type_info_name_addr = (*typeinfo_name_ptr_addr) as *const skyline::libc::c_char;
skyline::from_c_str(type_info_name_addr)
}

View file

@ -0,0 +1,170 @@
// Maybe needs a vtable.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResVec2 {
x: f32,
y: f32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResVec3 {
x: f32,
y: f32,
z: f32,
}
impl ResVec3 {
pub fn default() -> ResVec3 {
ResVec3 {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
pub fn new(x: f32, y: f32, z: f32) -> ResVec3 {
ResVec3 { x, y, z }
}
}
// Maybe needs a vtable.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResColor {
r: u8,
g: u8,
b: u8,
a: u8,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResPane {
block_header_kind: u32,
block_header_size: u32,
flag: u8,
base_position: u8,
alpha: u8,
flag_ex: u8,
pub name: [skyline::libc::c_char; 24],
pub user_data: [skyline::libc::c_char; 8],
pub pos: ResVec3,
rot_x: f32,
rot_y: f32,
rot_z: f32,
pub scale_x: f32,
pub scale_y: f32,
pub size_x: f32,
pub size_y: f32,
}
impl ResPane {
// For null pane
pub fn new(name: &str) -> ResPane {
let mut pane = ResPane {
block_header_kind: u32::from_le_bytes([b'p', b'a', b'n', b'1']),
block_header_size: 84,
/// Visible | InfluencedAlpha
flag: 0x3,
base_position: 0,
alpha: 0xFF,
flag_ex: 0,
name: [0; 24],
user_data: [0; 8],
pos: ResVec3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
rot_x: 0.0,
rot_y: 0.0,
rot_z: 0.0,
scale_x: 1.0,
scale_y: 1.0,
size_x: 30.0,
size_y: 40.0,
};
pane.set_name(name);
pane
}
pub fn set_name(&mut self, name: &str) {
assert!(
name.len() <= 24,
"Name of pane must be at most 24 characters"
);
unsafe {
std::ptr::copy_nonoverlapping(name.as_ptr(), self.name.as_mut_ptr(), name.len());
}
}
pub fn set_pos(&mut self, pos: ResVec3) {
self.pos = pos;
}
pub fn name_matches(&self, other: &str) -> bool {
self.name
.iter()
.take_while(|b| **b != 0)
.map(|b| *b as char)
.collect::<String>()
== other
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResTextBox {
pub pane: ResPane,
text_buf_bytes: u16,
text_str_bytes: u16,
material_idx: u16,
font_idx: u16,
text_position: u8,
text_alignment: u8,
text_box_flag: u16,
italic_ratio: f32,
text_str_offset: u32,
text_cols: [ResColor; 2],
font_size: ResVec2,
char_space: f32,
line_space: f32,
text_id_offset: u32,
shadow_offset: ResVec2,
shadow_scale: ResVec2,
shadow_cols: [ResColor; 2],
shadow_italic_ratio: f32,
line_width_offset_offset: u32,
per_character_transform_offset: u32,
/* Additional Info
uint16_t text[]; // Text.
char textId[]; // The text ID.
u8 lineWidthOffsetCount; // The quantity of widths and offsets for each line.
float lineOffset[]; // The offset for each line.
float lineWidth[]; // The width of each line.
ResPerCharacterTransform perCharacterTransform // Information for per-character animation.
ResAnimationInfo perCharacterTransformAnimationInfo; // Animation information for per-character animation.
*/
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResPicture {
pub pane: ResPane,
vtx_cols: [ResColor; 4],
material_idx: u16,
tex_coord_count: u8,
flags: u8,
/* Additional Info
ResVec2 texCoords[texCoordCount][VERTEX_MAX];
uint32_t shapeBinaryIndex;
*/
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ResPictureWithTex<const TEX_COORD_COUNT: usize> {
pub picture: ResPicture,
tex_coords: [[ResVec2; TEX_COORD_COUNT]; 4],
}

191
src/training/ui_hacks.rs Normal file
View file

@ -0,0 +1,191 @@
use crate::training::combo::FRAME_ADVANTAGE;
use crate::training::ui::*;
use training_mod_consts::OnOff;
#[skyline::hook(offset = 0x4b620)]
pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) {
let layout_name = skyline::from_c_str((*layout).raw_layout.layout_name);
let layout_root_pane = &*(*layout).raw_layout.root_pane;
let _anim_list = &mut (*layout).raw_layout.anim_trans_list;
// anim_list.iterate_anim_list();
if layout_name == "info_training" {
if let Some(parent) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0") {
if crate::common::MENU.frame_advantage == OnOff::On {
parent.alpha = 255;
parent.global_alpha = 255;
} else {
parent.alpha = 0;
parent.global_alpha = 0;
}
}
if let Some(header) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0_header") {
header.set_text_string("Frame Advantage");
}
if let Some(text) = layout_root_pane.find_pane_by_name_recursive("trMod_disp_0_txt") {
text.set_text_string(format!("{FRAME_ADVANTAGE}").as_str());
let text = text as *mut Pane as *mut TextBox;
if FRAME_ADVANTAGE < 0 {
(*text).set_color(200, 8, 8, 255);
} else if FRAME_ADVANTAGE == 0 {
(*text).set_color(0, 0, 0, 255);
} else {
(*text).set_color(31, 198, 0, 255);
}
}
}
original!()(layout, draw_info, cmd_buffer);
}
#[skyline::hook(offset = 0x493a0)]
pub unsafe fn layout_build_parts_impl(
layout: *mut Layout,
out_build_result_information: *mut u8,
device: *const u8,
data: *mut u8,
parts_build_data_set: *const u8,
build_arg_set: *const u8,
build_res_set: *const u8,
kind: u32,
) -> *mut Pane {
let layout_name = skyline::from_c_str((*layout).raw_layout.layout_name);
let _kind_str: String = kind.to_le_bytes().map(|b| b as char).iter().collect();
if layout_name != "info_training" {
return original!()(
layout,
out_build_result_information,
device,
data,
parts_build_data_set,
build_arg_set,
build_res_set,
kind,
);
}
let root_pane = (*layout).raw_layout.root_pane;
let block = data as *mut ResPane;
let num_display_panes = 1;
(0..num_display_panes).for_each(|idx| {
let mod_prefix = "trMod_disp_";
let parent_name = format!("{mod_prefix}{idx}");
let pic_name = format!("{mod_prefix}{idx}_base");
let header_name = format!("{mod_prefix}{idx}_header");
let txt_name = format!("{mod_prefix}{idx}_txt");
if (*block).name_matches("pic_numbase_01") {
let block = block as *mut ResPictureWithTex<1>;
let mut pic_block = (*block).clone();
pic_block.picture.pane.set_name(pic_name.as_str());
pic_block.picture.pane.set_pos(ResVec3::default());
let pic_pane = original!()(
layout,
out_build_result_information,
device,
&mut pic_block as *mut ResPictureWithTex<1> as *mut u8,
parts_build_data_set,
build_arg_set,
build_res_set,
kind,
);
(*(*pic_pane).parent).remove_child(&*pic_pane);
// pic is loaded first, we can create our parent pane here.
let disp_pane_kind = u32::from_le_bytes([b'p', b'a', b'n', b'1']);
let mut disp_pane_block = ResPane::new(parent_name.as_str());
disp_pane_block.set_pos(ResVec3::new(806.0, 390.0 - (idx as f32 * 110.0), 0.0));
let disp_pane = original!()(
layout,
out_build_result_information,
device,
&mut disp_pane_block as *mut ResPane as *mut u8,
parts_build_data_set,
build_arg_set,
build_res_set,
disp_pane_kind,
);
(*(*disp_pane).parent).remove_child(&*disp_pane);
(*root_pane).append_child(&*disp_pane);
(*disp_pane).append_child(&*pic_pane);
}
if (*block).name_matches("set_txt_num_01") {
let disp_pane = (*root_pane)
.find_pane_by_name(parent_name.as_str(), true)
.unwrap();
let block = data as *mut ResTextBox;
let mut text_block = (*block).clone();
text_block.pane.set_name(txt_name.as_str());
text_block.pane.set_pos(ResVec3::new(-10.0, -25.0, 0.0));
let text_pane = original!()(
layout,
out_build_result_information,
device,
&mut text_block as *mut ResTextBox as *mut u8,
parts_build_data_set,
build_arg_set,
build_res_set,
kind,
);
(*text_pane).set_text_string(format!("Pane {idx}!").as_str());
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
(*((*(text_pane as *mut TextBox)).m_p_material))
.set_white_color(255.0, 255.0, 255.0, 255.0);
(*((*(text_pane as *mut TextBox)).m_p_material)).set_black_color(0.0, 0.0, 0.0, 255.0);
(*(*text_pane).parent).remove_child(&*text_pane);
(*disp_pane).append_child(&*text_pane);
}
if (*block).name_matches("txt_cap_01") {
let disp_pane = (*root_pane)
.find_pane_by_name(parent_name.as_str(), true)
.unwrap();
let block = data as *mut ResTextBox;
let mut header_block = (*block).clone();
header_block.pane.set_name(header_name.as_str());
header_block.pane.set_pos(ResVec3::new(0.0, 25.0, 0.0));
let header_pane = original!()(
layout,
out_build_result_information,
device,
&mut header_block as *mut ResTextBox as *mut u8,
parts_build_data_set,
build_arg_set,
build_res_set,
kind,
);
(*header_pane).set_text_string(format!("Header {idx}").as_str());
// Ensure Material Colors are not hardcoded so we can just use SetTextColor.
(*((*(header_pane as *mut TextBox)).m_p_material))
.set_white_color(255.0, 255.0, 255.0, 255.0);
(*((*(header_pane as *mut TextBox)).m_p_material))
.set_black_color(0.0, 0.0, 0.0, 255.0);
// Header should be white text
(*(header_pane as *mut TextBox)).set_color(255, 255, 255, 255);
(*(*header_pane).parent).remove_child(&*header_pane);
(*disp_pane).append_child(&*header_pane);
}
});
original!()(
layout,
out_build_result_information,
device,
data,
parts_build_data_set,
build_arg_set,
build_res_set,
kind,
)
}
pub fn install_hooks() {
skyline::install_hooks!(handle_draw, layout_build_parts_impl,);
}