diff --git a/reader_core/Cargo.lock b/reader_core/Cargo.lock index cac726b..a8b2bdd 100644 --- a/reader_core/Cargo.lock +++ b/reader_core/Cargo.lock @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "pokereader" -version = "0.8.0" +version = "0.8.2" dependencies = [ "binrw 0.14.0", "chrono", diff --git a/reader_core/Cargo.toml b/reader_core/Cargo.toml index c254d7c..fcf5cfb 100644 --- a/reader_core/Cargo.toml +++ b/reader_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pokereader" -version = "0.8.0" +version = "0.8.2" edition = "2021" [dependencies] diff --git a/reader_core/src/draw.rs b/reader_core/src/draw.rs index 383cbf7..97db45a 100644 --- a/reader_core/src/draw.rs +++ b/reader_core/src/draw.rs @@ -6,9 +6,18 @@ use crate::{pnp, utils::menu::MenuOptionValue}; use pkm_rs::{Nature, Pkx, Shiny}; pub const WHITE: u32 = 0xffffff; -pub const GREEN: u32 = 0x00cc00; pub const RED: u32 = 0xff0000; +pub const MAGMA_RED: u32 = 0xFF4433; +pub const ORANGE: u32 = 0xd75f00; +pub const ULTRA_ORANGE: u32 = 0xff5f1f; +pub const YELLOW: u32 = 0xd7ff00; +pub const GREEN: u32 = 0x00cc00; +pub const CYAN: u32 = 0x00ffff; pub const MUTED_CYAN: u32 = 0x00cccc; +pub const BLUE: u32 = 0x0000ff; +pub const PURPLE: u32 = 0xaf5fff; +pub const ULTRA_PURPLE: u32 = 0xaf00ff; +pub const HOT_PINK: u32 = 0xd7005f; fn get_shiny_color(is_shiny: bool) -> u32 { match is_shiny { @@ -116,21 +125,29 @@ macro_rules! print_stat { } pub fn print_pp(pp: u32) { - pnp::println!(color = if pp > 1 { WHITE } else { RED }, "PP Remaining: {}", pp); + let color = match pp >> 1 { + 0 => RED, + 1 => MAGMA_RED, + 2 => ORANGE, + 3 => ULTRA_ORANGE, + 4 => YELLOW, + _ => WHITE, + }; + pnp::println!(color = color, "PP Remaining: {}", pp); } pub fn print_title() { match loaded_title() { Ok(title) => match title { - LoadedTitle::S => pnp::println!(color = 0xd75f00, " Pokemon Sun"), - LoadedTitle::M => pnp::println!(color = 0xaf5fff, " Pokemon Moon"), - LoadedTitle::Us => pnp::println!(color = 0xff5f1f, " Pokemon Ultra Sun"), - LoadedTitle::Um => pnp::println!(color = 0xaf00ff, " Pokemon Ultra Moon"), - LoadedTitle::X => pnp::println!(color = 0x00ffff, " Pokemon X"), - LoadedTitle::Y => pnp::println!(color = 0xd7005f, " Pokemon Y"), - LoadedTitle::Or => pnp::println!(color = 0xFF4433, " Pokemon Omega Ruby"), - LoadedTitle::As => pnp::println!(color = 0x0000ff, " Pokemon Alpha Sapphire"), - LoadedTitle::Transporter => pnp::println!(color = 0xd7ff00, " Pokemon Transporter"), + LoadedTitle::S => pnp::println!(color = ORANGE, " Pokemon Sun"), + LoadedTitle::M => pnp::println!(color = PURPLE, " Pokemon Moon"), + LoadedTitle::Us => pnp::println!(color = ULTRA_ORANGE, " Pokemon Ultra Sun"), + LoadedTitle::Um => pnp::println!(color = ULTRA_PURPLE, " Pokemon Ultra Moon"), + LoadedTitle::X => pnp::println!(color = CYAN, " Pokemon X"), + LoadedTitle::Y => pnp::println!(color = HOT_PINK, " Pokemon Y"), + LoadedTitle::Or => pnp::println!(color = MAGMA_RED, " Pokemon Omega Ruby"), + LoadedTitle::As => pnp::println!(color = BLUE, " Pokemon Alpha Sapphire"), + LoadedTitle::Transporter => pnp::println!(color = YELLOW, " Pokemon Transporter"), LoadedTitle::CrystalEn | LoadedTitle::CrystalDe | LoadedTitle::CrystalFr @@ -153,8 +170,10 @@ pub fn shiny_type(pkx: &impl Pkx) -> &'static str { } } -pub fn draw_pkx_brief(pkx: &impl Pkx) { - let species = pkx.species_t().to_string(); +pub fn draw_pkx_brief(pkx: &impl Pkx) -> bool { + if !pkx.is_valid() { + return draw_invalid_pkx(); + } let ability = pkx.ability_t().to_string(); let shiny_type = shiny_type(pkx); @@ -168,11 +187,10 @@ pub fn draw_pkx_brief(pkx: &impl Pkx) { let nature = pkx.nature_t(); - pnp::println!("{} {}", nature, species); pnp::println!("Ability: ({}) {}", pkx.ability_number_t(), ability); pnp::println!("PID: {:08X}", pkx.pid()); pnp::println!(color = shiny_color, "PSV: {:04}, {}", pkx.psv(), shiny_type); - pnp::println!("HPower: {}", pkx.hidden_power_t()); + pnp::println!("Nature: {}", nature); pnp::println!( "IVs: {}/{}/{}/{}/{}/{}", iv_hp, @@ -182,9 +200,14 @@ pub fn draw_pkx_brief(pkx: &impl Pkx) { iv_spd, iv_spe ); + + return false; } -pub fn draw_pkx(pkx: &impl Pkx, pkx_type: PkxType) { +pub fn draw_pkx(pkx: &impl Pkx, pkx_type: PkxType) -> bool { + if !pkx.is_valid() { + return draw_invalid_pkx(); + } let species = pkx.species_t().to_string(); let ability = pkx.ability_t().to_string(); @@ -227,6 +250,13 @@ pub fn draw_pkx(pkx: &impl Pkx, pkx_type: PkxType) { print_stat!(iv_spa, ev_spa, SpA, &nature_stat, "SpA "); print_stat!(iv_spd, ev_spd, SpD, &nature_stat, "SpD "); print_stat!(iv_spe, ev_spe, Spe, &nature_stat, "Spe "); + + return false; +} + +pub fn draw_invalid_pkx() -> bool { + pnp::println!("No Data."); + return true; } pub fn draw_controls_help() { diff --git a/reader_core/src/gen7/call_rates.rs b/reader_core/src/gen7/call_rates.rs new file mode 100644 index 0000000..2580872 --- /dev/null +++ b/reader_core/src/gen7/call_rates.rs @@ -0,0 +1,262 @@ +use pkm_rs::{Pkx, Species::*}; + +pub fn lookup_call_rate(pkx: &impl Pkx, is_usum: bool) -> u32 { + match pkx.species_t() { + Slowbro + | Dhelmise + | Drampa + | Turtonator + | Lurantis + | Lycanroc + | Relicanth + | Glalie + | Absol + | Sharpedo + | Miltank + | Lapras + | Gyarados + | Tauros + | Pinsir + | Scyther + | Kangaskhan + | Marowak => 3, + Raticate + | HakamoO + | Bruxish + | Pyukumuku + | Palossand + | Steenee + | Bewear + | Shiinotic + | Araquanid + | Mudsdale + | Ribombee + | Oricorio + | Gumshoos + | Klefki + | Fletchinder + | Mandibuzz + | Braviary + | Alomomola + | Garbodor + | Krokorok + | Boldore + | Herdier + | Lumineon + | Munchlax + | Drifblim + | Gastrodon + | Metang + | Shelgon + | Luvdisc + | Whiscash + | Vibrava + | Spinda + | Torkoal + | Wailord + | Hariyama + | Masquerain + | Pelipper + | Skarmory + | Granbull + | Lanturn + | Ariados + | Ledian + | Dragonair + | Magmar + | Electabuzz + | Seaking + | Hypno + | Haunter + | Muk + | Magneton + | Graveler + | Tentacruel + | Machoke + | Kadabra + | Poliwhirl + | Primeape + | Golduck + | Persian + | Dugtrio + | Parasect + | Jigglypuff + | Clefairy + | Pikachu + | Fearow => 6, + Rattata + | JangmoO + | Mimikyu + | Togedemaru + | Sandygast + | Comfey + | Bounsweet + | Stufful + | Salandit + | Morelull + | Fomantis + | Dewpider + | Mudbray + | Wishiwashi + | Rockruff + | Cutiefly + | Crabrawler + | Grubbin + | Yungoos + | Trumbeak + | Phantump + | Carbink + | Pancham + | Fletchling + | Vullaby + | Rufflet + | Emolga + | Trubbish + | Sandile + | Petilil + | Cottonee + | Roggenrola + | Lillipup + | Finneon + | Drifloon + | Shellos + | Beldum + | Bagon + | Snorunt + | Feebas + | Barboach + | Trapinch + | Wailmer + | Carvanha + | Nosepass + | Makuhita + | Surskit + | Wingull + | Smeargle + | Delibird + | Corsola + | Sneasel + | Snubbull + | Misdreavus + | Murkrow + | Chinchou + | Spinarak + | Ledyba + | Dratini + | Eevee + | Ditto + | Magikarp + | Staryu + | Goldeen + | Drowzee + | Gastly + | Shellder + | Grimer + | Magnemite + | Slowpoke + | Geodude + | Machop + | Abra + | Poliwag + | Growlithe + | Mankey + | Meowth + | Diglett + | Paras + | Golbat + | Vulpix + | Sandshrew + | Spearow => 9, + Caterpie + | Passimian + | Oranguru + | Salazzle + | Charjabug + | Pikipek + | Riolu + | Bonsly + | Magby + | Elekid + | Igglybuff + | Cleffa + | Pichu + | Cubone + | Exeggcute + | Tentacool + | Psyduck + | Zubat + | Metapod => 15, + Lickitung + | Hawlucha + | Larvesta + | Druddigon + | Mienshao + | Tropius + | Remoraid + | Heracross + | Dunsparce => if is_usum { 3 } else { 0 }, + Arbok + | Noivern + | Clawitzer + | Dragalge + | Malamar + | Furfrou + | Pyroar + | Mienfoo + | Jellicent + | Zoroark + | Scrafty + | Lopunny + | Banette + | Crawdaunt + | Manectric + | Mawile + | Houndoom + | Forretress + | Flaaffy + | Xatu + | Noctowl + | Dewgong => if is_usum { 6 } else { 0 }, + Chansey + | Noibat + | Dedenne + | Clauncher + | Skrelp + | Inkay + | Floette // Warn: Apparently Only red Floette can call SOS + | Litleo + | Frillish + | Minccino + | Zorua + | Scraggy + | Basculin + | Buneary + | Shuppet + | Kecleon + | Corphish + | Electrike + | Pupitar + | Larvitar + | Houndour + | Pineco + | Aipom + | Mareep + | Natu + | Hoothoot + | Seel + | Ekans => if is_usum { 9 } else { 0 }, + Smoochum + | Flabebe + | Bisharp + | Golurk + | Golett + | Beheeyem + | Elgyem + | Mantyke + | MimeJr + | Clamperl + | Claydol + | Baltoy => if is_usum { 15 } else { 0 }, + _ => 0, + } +} diff --git a/reader_core/src/gen7/draw.rs b/reader_core/src/gen7/draw.rs index b0534a4..b7dcc1c 100644 --- a/reader_core/src/gen7/draw.rs +++ b/reader_core/src/gen7/draw.rs @@ -1,13 +1,19 @@ -use super::reader::Gen7Reader; +use super::{ + lookup_call_rate, + reader::{Gen7Reader, Gen7WildSide}, +}; use crate::{ pnp, - rng::{RngWrapper, Sfmt}, + rng::{RngWrapper, Sfmt32, Sfmt64}, utils::{format_egg_parent, is_daycare_masuda_method}, }; +use pkm_rs::Pkx; -pub use crate::draw::{GREEN, PkxType, RED, WHITE, draw_header, draw_pkx, draw_pkx_brief, get_pp, print_pp}; +pub use crate::draw::{ + GREEN, PkxType, RED, WHITE, draw_header, draw_invalid_pkx, draw_pkx, draw_pkx_brief, get_pp, print_pp, +}; -pub fn draw_rng(reader: &Gen7Reader, rng: &RngWrapper) { +pub fn draw_rng(reader: &Gen7Reader, rng: &RngWrapper) { let sfmt_state = rng.current_state(); pnp::println!("Seed: {:08X}", rng.init_seed()); @@ -30,19 +36,79 @@ pub fn draw_citra_info(reader: &Gen7Reader) { pnp::println!("Time offset: {}", main_rng_seed_context.time_offset_ms); } -pub fn draw_sos(reader: &Gen7Reader, slot: u32, correction: u32) { - pnp::println!("SOS Seed: {:08X}", reader.sos_seed()); - pnp::println!("SOS Chain Length: {}", reader.sos_chain()); +pub fn draw_sos( + reader: &Gen7Reader, + main_rng: &mut RngWrapper, + sos_rng: &mut RngWrapper, + menu_val: usize, +) -> bool { + let sos_seed: u32 = reader.sos_seed(); + let sos_state: u32 = reader.sos_state(); + let sos_chain: u16 = reader.sos_chain() as u16; + let sos_index: u16 = reader.sos_index(); + + sos_rng.reinit_if_needed(sos_seed); + if sos_seed > 0 { + if sos_index | sos_chain > 0 { + sos_rng.update_advances(sos_state); + } + } + + let caller_side = Gen7WildSide::new(menu_val); + let ally_side = caller_side.other(); + let caller_pkm = &reader.read_wild_side(caller_side); + let ally_pkm = &reader.read_wild_side(ally_side); + + if !caller_pkm.is_valid() { + return draw_invalid_pkx(); + } + + pnp::println!("SOS Seed: {:08X}", sos_rng.init_seed()); + pnp::println!("SOS Index: {}", sos_rng.advances()); + pnp::println!("SOS Chain Length: {}", sos_chain); + if reader.orb_active() { - pnp::println!(color = GREEN, "Orb Active") + pnp::println!(color = GREEN, "Orb Active"); + } else { + pnp::println!(color = RED, "Orb Not Active!"); + } + + pnp::println!("RNG Frame: {}", main_rng.advances()); + + pnp::println!(""); + if caller_pkm.is_valid() { + pnp::println!( + "{} {} ({}):", + caller_pkm.species_t(), + &reader.wild_slot_lookup(caller_side).label(), + caller_side.label() + ); + let call_rate = lookup_call_rate(caller_pkm, reader.is_usum()); + pnp::println!( + color = if call_rate == 0 { RED } else { WHITE }, + "Call Rate: {}", + call_rate + ); + print_pp(get_pp(caller_pkm)); } else { - pnp::println!(color = RED, "Orb Not Active"); + return true; } - pnp::println!("Caller Slot: {}", slot); - print_pp(get_pp(&reader.sos_caller_pkm(slot))); + pnp::println!(""); - pnp::println!("Ally Data (Slot {}):", reader.ally_slot(slot, correction) + 1); - draw_pkx_brief(&reader.sos_ally_pkm(slot, correction)); + if reader.sos_chain() > 0 { + pnp::println!( + "{} {} ({}):", + ally_pkm.species_t(), + reader.wild_slot_lookup(ally_side).label(), + ally_side.label() + ); + if ally_pkm.is_valid() { + draw_pkx_brief(ally_pkm); + return false; + } + } + pnp::println!("No Ally to display."); + return true; } pub fn draw_daycare(reader: &Gen7Reader) { diff --git a/reader_core/src/gen7/frame.rs b/reader_core/src/gen7/frame.rs index 0b38554..59cde7d 100644 --- a/reader_core/src/gen7/frame.rs +++ b/reader_core/src/gen7/frame.rs @@ -1,16 +1,15 @@ use super::{ draw::{PkxType, draw_citra_info, draw_daycare, draw_header, draw_pkx, draw_rng, draw_sos}, - reader::Gen7Reader, + reader::{Gen7PkmSlot, Gen7Reader}, }; use crate::{ pnp, - rng::{RngWrapper, Sfmt}, + rng::{RngWrapper, Sfmt32, Sfmt64}, utils::{ ShowView, help_menu::HelpMenu, menu::{Menu, MenuOption, MenuOptionValue}, sub_menu::SubMenu, - sub_menu_capture::SubMenuCapture, }, }; use once_cell::unsync::Lazy; @@ -47,39 +46,33 @@ impl MenuOptionValue for Gen7View { } struct PersistedState { - sfmt: RngWrapper, + sfmt: RngWrapper, + sos_sfmt: RngWrapper, show_view: ShowView, view: Gen7View, main_menu: Menu<9, Gen7View>, help_menu: HelpMenu, - wild_menu: SubMenu<1, 4>, + wild_menu: SubMenu<1, 5>, party_menu: SubMenu<1, 6>, - sos_menu: SubMenuCapture<1, 4>, + sos_menu: SubMenu<1, 2>, pelago_menu: SubMenu<1, 3>, } unsafe fn get_state() -> &'static mut PersistedState { static mut STATE: Lazy = Lazy::new(|| PersistedState { sfmt: RngWrapper::default(), + sos_sfmt: RngWrapper::default(), show_view: ShowView::default(), view: Gen7View::MainMenu, party_menu: SubMenu::default(), pelago_menu: SubMenu::default(), wild_menu: SubMenu::default(), - sos_menu: SubMenuCapture::default(), + sos_menu: SubMenu::default(), help_menu: HelpMenu::new(|| { pnp::println!("SOS Controls:"); - pnp::println!("[X] + [Right]:"); - pnp::println!(" Set Caller slot to"); - pnp::println!(" the current ally."); - pnp::println!(" Use this when you"); - pnp::println!(" faint the caller."); - pnp::println!(""); - pnp::println!("[X] + [Up]/[Down]:"); - pnp::println!(" Manually change"); - pnp::println!(" the caller slot."); - pnp::println!(" (Not recommended)"); - pnp::println!(""); + pnp::println!("[X]/[Up]/[Down]:"); + pnp::println!(" - Swap Caller"); + pnp::println!(" Left/Right") }), main_menu: Menu::new([ MenuOption::new(Gen7View::Rng), @@ -108,7 +101,6 @@ fn run_frame(reader: Gen7Reader) { state.sfmt.reinit_if_needed(init_seed); state.sfmt.update_advances(sfmt_state); - if !state.show_view.check() { return; } @@ -121,29 +113,33 @@ fn run_frame(reader: Gen7Reader) { Gen7View::Rng => draw_rng(&reader, &state.sfmt), Gen7View::Daycare => draw_daycare(&reader), Gen7View::WildPokemon => { - let slot = state.wild_menu.update_and_draw(is_locked); - draw_pkx(&reader.wild_pkm((slot - 1) as u32), PkxType::Wild); + let slot = Gen7PkmSlot::new(state.wild_menu.update_headless(is_locked)); + pnp::println!("Slot {}", slot.label()); + pnp::println!("[v] Next | Prev [^]"); + pnp::println!(""); + draw_pkx(&reader.read_wild_slot(slot), PkxType::Wild); } Gen7View::Sos => { - let prev_caller_slot = state.sos_menu.counter_value(); - let prev_correction_value = state.sos_menu.captured_value(); - let caller_slot = state.sos_menu.update_headless( - is_locked, - reader.sos_chain() as u32, - reader.ally_slot(prev_caller_slot as u32, prev_correction_value) as usize + 1, - ); - let correction_value = state.sos_menu.captured_value(); - draw_sos(&reader, caller_slot as u32, correction_value); + let caller_side = if pnp::is_just_pressed(pnp::Button::X) { + state.sos_menu.increment() + } else { + state.sos_menu.update_headless(is_locked) + }; + if draw_sos(&reader, &mut state.sfmt, &mut state.sos_sfmt, caller_side) { + state.sos_menu.reset(); // Default to right-side caller + } + } + Gen7View::Box => { + draw_pkx(&reader.box_pkm(), PkxType::Tame); } - Gen7View::Box => draw_pkx(&reader.box_pkm(), PkxType::Tame), Gen7View::Citra => draw_citra_info(&reader), Gen7View::Party => { let slot = state.party_menu.update_and_draw(is_locked); - draw_pkx(&reader.party_pkm((slot - 1) as u32), PkxType::Tame); + draw_pkx(&reader.party_pkm(Gen7PkmSlot::new(slot)), PkxType::Tame); } Gen7View::Pelago => { let slot = state.pelago_menu.update_and_draw(is_locked); - draw_pkx(&reader.pelago_pkm((slot - 1) as u32), PkxType::Wild) + draw_pkx(&reader.pelago_pkm((slot - 1) as u32), PkxType::Wild); } Gen7View::HelpMenu => state.help_menu.update_and_draw(is_locked), Gen7View::MainMenu => { diff --git a/reader_core/src/gen7/hook.rs b/reader_core/src/gen7/hook.rs index 4a3049e..8f90937 100644 --- a/reader_core/src/gen7/hook.rs +++ b/reader_core/src/gen7/hook.rs @@ -1,3 +1,4 @@ +use super::reader::{SM_SOS_SFMT_ADDR, USUM_SOS_SFMT_ADDR}; use crate::{pnp, utils}; use chrono::NaiveDateTime; static mut MAIN_RNG_SEED_TICKS: u32 = 0; @@ -9,8 +10,14 @@ pub fn sos_seed() -> u32 { unsafe { SOS_SEED } } -fn init_sfmt_hook(regs: &[u32], _stack_pointer: *mut u32) { - if regs[0] == 0x30038e30 { +fn init_sm_sfmt_hook(regs: &[u32], _stack_pointer: *mut u32) { + if regs[0] == SM_SOS_SFMT_ADDR { + unsafe { SOS_SEED = regs[1] }; + } +} + +fn init_usum_sfmt_hook(regs: &[u32], _stack_pointer: *mut u32) { + if regs[0] == USUM_SOS_SFMT_ADDR { unsafe { SOS_SEED = regs[1] }; } } @@ -54,7 +61,7 @@ fn init_main_rng_hook(_regs: &[u32], stack_pointer: *mut u32) { pub fn init_um() { utils::hook_game_branch! { game_name = um, - init_sfmt_hook = 0x361e60, + init_usum_sfmt_hook = 0x361e60, init_main_rng_hook = 0x3fcbc0, } } @@ -62,7 +69,7 @@ pub fn init_um() { pub fn init_us() { utils::hook_game_branch! { game_name = us, - init_sfmt_hook = 0x361e60, + init_usum_sfmt_hook = 0x361e60, init_main_rng_hook = 0x3fcbbc, } } @@ -70,7 +77,7 @@ pub fn init_us() { pub fn init_sm() { utils::hook_game_branch! { game_name = sm, - init_sfmt_hook = 0x359784, + init_sm_sfmt_hook = 0x359784, init_main_rng_hook = 0x3eab60, } } diff --git a/reader_core/src/gen7/mod.rs b/reader_core/src/gen7/mod.rs index ed5650d..1752f7e 100644 --- a/reader_core/src/gen7/mod.rs +++ b/reader_core/src/gen7/mod.rs @@ -1,8 +1,10 @@ +mod call_rates; mod draw; mod frame; mod game_lib; mod hook; mod reader; +use call_rates::lookup_call_rate; pub use frame::*; pub use hook::{init_sm, init_um, init_us}; diff --git a/reader_core/src/gen7/reader.rs b/reader_core/src/gen7/reader.rs index ad47ec6..6163354 100644 --- a/reader_core/src/gen7/reader.rs +++ b/reader_core/src/gen7/reader.rs @@ -3,19 +3,29 @@ use crate::pnp; use core::num::{NonZeroU8, NonZeroU32}; use pkm_rs::{Pk7, PokeCrypto}; +pub const SM_SOS_BASE_ADDR: u32 = 0x30038c34; +pub const USUM_SOS_BASE_ADDR: u32 = 0x30038e20; +pub const SM_SOS_SFMT_ADDR: u32 = SM_SOS_BASE_ADDR + 0x10; +pub const USUM_SOS_SFMT_ADDR: u32 = USUM_SOS_BASE_ADDR + 0x10; +pub const PKM_CONT_SIZE: u32 = 0x330; +pub const PK7_DATA_SIZE: u32 = 0x1E4; + struct Gen7Addresses { initial_seed: u32, sfmt_state_index: u32, sfmt_state: u32, + _sos_base_addr: u32, + sos_sfmt_state: u32, party: u32, wild: u32, - sos: u32, + sos_index: u32, + sos_chain_len: u32, + sos_battle_table: u32, + pkm_container_base: u32, + _sos_call_succeed: u32, orb_active: u32, - sos_chain_length: u32, // To be used in the future vvv - _sos_index: u32, - _ally_id: u32, - _prev_call_succeed: u32, + _sos_ally_id: u32, // pelago: u32, egg_ready: u32, @@ -35,14 +45,17 @@ const SM_ADDRESSES: Gen7Addresses = Gen7Addresses { initial_seed: 0x325a3878, sfmt_state_index: 0x33196548, sfmt_state: 0x33195b88, + _sos_base_addr: 0x30038C34, + sos_sfmt_state: SM_SOS_SFMT_ADDR, + sos_battle_table: 0x30000420, + pkm_container_base: 0x30004DA8, party: 0x34195e10, wild: 0x3002f7b8, - sos: 0x3002f7b8, - _sos_index: 0x30039614, - orb_active: 0x3003961c, - sos_chain_length: 0x3003960d, - _ally_id: 0x3003961e, - _prev_call_succeed: 0x3003961f, + sos_index: SM_SOS_BASE_ADDR + 0x9d0, + orb_active: SM_SOS_BASE_ADDR + 0x9d8, + sos_chain_len: SM_SOS_BASE_ADDR + 0x9d9, + _sos_ally_id: SM_SOS_BASE_ADDR + 0x9da, + _sos_call_succeed: SM_SOS_BASE_ADDR + 0x9db, pelago: 0x331110ca, egg_ready: 0x3313edd8, egg: 0x3313eddc, @@ -61,14 +74,17 @@ const USUM_ADDRESSES: Gen7Addresses = Gen7Addresses { initial_seed: 0x32663bf0, sfmt_state_index: 0x330d3f98, sfmt_state: 0x330d35d8, + _sos_base_addr: 0x30038E20, + sos_sfmt_state: USUM_SOS_SFMT_ADDR, + sos_battle_table: 0x30000420, + pkm_container_base: 0x30004DA8, party: 0x33f7fa44, wild: 0x3002f9a0, - sos: 0x3002f9a0, - _sos_index: 0x300397F0, - orb_active: 0x300397f8, - sos_chain_length: 0x300397f9, - _ally_id: 0x300397fA, - _prev_call_succeed: 0x300397fb, + sos_index: USUM_SOS_BASE_ADDR + 0x9d0, + orb_active: USUM_SOS_BASE_ADDR + 0x9d8, + sos_chain_len: USUM_SOS_BASE_ADDR + 0x9d9, + _sos_ally_id: USUM_SOS_BASE_ADDR + 0x9da, + _sos_call_succeed: USUM_SOS_BASE_ADDR + 0x9db, pelago: 0x3304d16a, egg_ready: 0x3307b1e8, egg: 0x3307b1ec, @@ -88,6 +104,93 @@ pub struct Gen7Reader { addrs: &'static Gen7Addresses, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Gen7WildSide { + Left, + Right, + Invalid, +} + +impl Gen7WildSide { + pub fn new(value: usize) -> Self { + match value { + 1 => Gen7WildSide::Right, + 2 => Gen7WildSide::Left, + _ => Gen7WildSide::Invalid, + } + } + pub fn other(&self) -> Self { + match self { + Gen7WildSide::Left => Gen7WildSide::Right, + Gen7WildSide::Right => Gen7WildSide::Left, + Gen7WildSide::Invalid => Gen7WildSide::Invalid, + } + } + + pub fn offset(&self) -> Option { + match self { + Gen7WildSide::Left => Some(0x4), + Gen7WildSide::Right => Some(0x0), + Gen7WildSide::Invalid => None, + } + } + + pub fn label(&self) -> &'static str { + match self { + Gen7WildSide::Left => "Left", + Gen7WildSide::Right => "Right", + Gen7WildSide::Invalid => "Invalid", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Gen7PkmSlot { + A, + B, + C, + D, + E, + F, + Invalid, +} + +impl Gen7PkmSlot { + pub fn new(value: usize) -> Self { + match value { + 1 => Gen7PkmSlot::A, + 2 => Gen7PkmSlot::B, + 3 => Gen7PkmSlot::C, + 4 => Gen7PkmSlot::D, + 5 => Gen7PkmSlot::E, + 6 => Gen7PkmSlot::F, + _ => Gen7PkmSlot::Invalid, + } + } + pub fn offset(&self) -> Option { + match self { + Gen7PkmSlot::A => Some(0x0), + Gen7PkmSlot::B => Some(0x1E4), + Gen7PkmSlot::C => Some(0x3c8), + Gen7PkmSlot::D => Some(0x5ac), + Gen7PkmSlot::E => Some(0x790), + Gen7PkmSlot::F => Some(0x974), + Gen7PkmSlot::Invalid => None, + } + } + pub fn label(&self) -> &'static str { + match self { + Gen7PkmSlot::A => "A", + Gen7PkmSlot::B => "B", + Gen7PkmSlot::C => "C", + Gen7PkmSlot::D => "D", + Gen7PkmSlot::E => "E", + Gen7PkmSlot::F => "F", + Gen7PkmSlot::Invalid => "Invalid", + } + } +} + impl Gen7Reader { pub fn sm() -> Self { Self { @@ -103,6 +206,10 @@ impl Gen7Reader { } } + pub fn is_usum(&self) -> bool { + self.is_usum + } + pub fn g7tid(&self) -> u32 { let sidtid = pnp::read::(self.addrs.id); @@ -130,12 +237,12 @@ impl Gen7Reader { } fn sfmt_state_index(&self) -> u32 { - pnp::read(self.addrs.sfmt_state_index) + let index = pnp::read(self.addrs.sfmt_state_index); + if index != 624 { index } else { 0 } } pub fn sfmt_state(&self) -> u64 { - let index = self.sfmt_state_index(); - pnp::read(self.addrs.sfmt_state + if index != 624 { index * 4 } else { 0 }) + pnp::read(self.addrs.sfmt_state + self.sfmt_state_index() * 4) } pub fn egg_seed(&self) -> [u32; 4] { @@ -151,32 +258,73 @@ impl Gen7Reader { hook::sos_seed() } + // We still use this naive check to hint to the RNG wrapper when it can rest. + pub fn sos_index(&self) -> u16 { + let index = pnp::read(self.addrs.sos_index); + if index != 624 { index } else { 0 } + } + + pub fn sos_state(&self) -> u32 { + pnp::read(self.addrs.sos_sfmt_state + (self.sos_index() as u32 * 4)) + } + + /* Note: Not very useful... + * Value does not update until after last + * input, which makes it rather misleading. */ + pub fn _sos_prevcall(&self) -> bool { + pnp::read_bool(self.addrs._sos_call_succeed) + } + pub fn sos_chain(&self) -> u8 { - pnp::read(self.addrs.sos_chain_length) + pnp::read(self.addrs.sos_chain_len) } + pub fn orb_active(&self) -> bool { - ((pnp::read::(self.addrs.orb_active) & 0x1) > 0) as bool + pnp::read_bool(self.addrs.orb_active) } - pub fn ally_slot(&self, caller_slot: u32, correction: u32) -> u32 { - if self.sos_chain() == 0 { - 0 - } else { - ((caller_slot - 1) + ((self.sos_chain() as i32 - (correction as i32 + 1)) % 3) as u32 + 1) % 4 + + pub fn wild_slot_lookup(&self, side: Gen7WildSide) -> Gen7PkmSlot { + match side.offset() { + Some(offset) => { + let battle_table = self.addrs.sos_battle_table + offset; + let pkx_container_ptr = pnp::read::(battle_table).clamp( + self.addrs.pkm_container_base, + self.addrs.pkm_container_base + (PKM_CONT_SIZE * 6), + ); + Gen7PkmSlot::new( + (((pnp::read::(pkx_container_ptr) + 0x40 - self.addrs.wild) / PK7_DATA_SIZE) + 1) + .clamp(1, 6) as usize, + ) + } + None => Gen7PkmSlot::Invalid, + } + } + + pub fn read_wild_slot(&self, slot: Gen7PkmSlot) -> Pk7 { + match slot.offset() { + Some(offset) => self.read_pk7(self.addrs.wild + offset), + None => Pk7::default(), } } + pub fn read_wild_side(&self, side: Gen7WildSide) -> Pk7 { + self.read_wild_slot(self.wild_slot_lookup(side)) + } + fn read_pk7(&self, offset: u32) -> Pk7 { let bytes = pnp::read_array::<{ Pk7::STORED_SIZE }>(offset); Pk7::new_valid(bytes) } - pub fn party_pkm(&self, slot: u32) -> Pk7 { - let offset = (slot * 484) + self.addrs.party; - self.read_pk7(offset) + pub fn party_pkm(&self, slot: Gen7PkmSlot) -> Pk7 { + match slot.offset() { + Some(offset) => self.read_pk7(self.addrs.party + offset), + None => Pk7::default(), + } } fn egg_parent(&self, is_present: u32, pkm: u32) -> Option { - let is_parent_present = pnp::read::(is_present) != 0; + let is_parent_present = pnp::read_bool(is_present); if !is_parent_present { return None; @@ -194,11 +342,6 @@ impl Gen7Reader { self.egg_parent(self.addrs.is_parent2_occupied, self.addrs.parent2) } - pub fn wild_pkm(&self, slot: u32) -> Pk7 { - let offset = (slot * 484) + self.addrs.wild; - self.read_pk7(offset) - } - pub fn box_pkm(&self) -> Pk7 { self.read_pk7(self.addrs.box_cursor) } @@ -207,15 +350,8 @@ impl Gen7Reader { self.read_pk7((slot * 236) + self.addrs.pelago) } - pub fn sos_caller_pkm(&self, caller_slot: u32) -> Pk7 { - self.read_pk7(((caller_slot - 1) * 484) + self.addrs.sos) - } - pub fn sos_ally_pkm(&self, caller_slot: u32, correction: u32) -> Pk7 { - self.read_pk7((self.ally_slot(caller_slot, correction) * 484) + self.addrs.sos) - } - pub fn is_egg_ready(&self) -> bool { - pnp::read::(self.addrs.egg_ready) != 0 + pnp::read_bool(self.addrs.egg_ready) } fn has_item(&self, offset: u32, item_id: u32, count: u32) -> bool { diff --git a/reader_core/src/pnp/memory.rs b/reader_core/src/pnp/memory.rs index 62e1afa..68e78a8 100644 --- a/reader_core/src/pnp/memory.rs +++ b/reader_core/src/pnp/memory.rs @@ -31,6 +31,10 @@ where T::read_le(&mut reader).unwrap_or_default() } +pub fn read_bool(addr: u32) -> bool { + read::(addr) != 0 +} + pub fn write_slice(addr: u32, buf: &[u8]) { unsafe { bindings::host_write_mem(addr, buf.len() as u32, buf.as_ptr() as u32); diff --git a/reader_core/src/rng/rng_wrapper.rs b/reader_core/src/rng/rng_wrapper.rs index 7085c90..5cd833e 100644 --- a/reader_core/src/rng/rng_wrapper.rs +++ b/reader_core/src/rng/rng_wrapper.rs @@ -37,7 +37,6 @@ impl RngWrapper { self.reinit(seed); return true; } - false } @@ -64,19 +63,19 @@ impl RngWrapper { #[cfg(test)] mod test { use super::*; - use crate::rng::{MT, Rng, Sfmt, TinyMT}; + use crate::rng::{MT, Rng, Sfmt32, Sfmt64, TinyMT}; #[test] fn should_track_sfmt_advances() { let seed = 0x92845c35; - let mut rng = Sfmt::new(seed); + let mut rng = Sfmt64::new(seed); for _ in 0..478 { rng.next_state(); } assert_eq!(rng.current_state(), 0x5a1ef513d10eccfb); - let mut wrapper = RngWrapper::::default(); + let mut wrapper = RngWrapper::::default(); wrapper.reinit(seed); wrapper.update_advances(rng.current_state()); assert_eq!(wrapper.advances(), 478); diff --git a/reader_core/src/rng/sfmt.rs b/reader_core/src/rng/sfmt.rs index e6968be..d705fcc 100644 --- a/reader_core/src/rng/sfmt.rs +++ b/reader_core/src/rng/sfmt.rs @@ -1,12 +1,12 @@ use crate::rng::Rng; #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Sfmt { +pub struct Sfmt32 { index: usize, sfmt: [u32; 624], } -impl Sfmt { +impl Sfmt32 { pub fn new(seed: u32) -> Self { let mut rng = Self { sfmt: [0; 624], @@ -36,21 +36,26 @@ impl Sfmt { self.sfmt[0] ^= !inner & 1; } - fn get_current_state(&self) -> u64 { - let index = if self.index != 624 { self.index } else { 0 }; - let low = self.sfmt[index] as u64; - let high = self.sfmt[index + 1] as u64; + fn get_current_state(&self) -> u32 { + // sgrace: I think it's better to panic if this overflows, + // As index > 624 should never naturally occur. + let index = if self.index == 624 { 0 } else { self.index }; + self.sfmt[index] + } - low | (high << 32) + fn get_next_state(&self) -> u32 { + // sgrace: This one, however, can naturally overflow + // in Sfmt64 at index = 0, so handle that. + let index = (self.index + 1) % 624; + self.sfmt[index] } - pub fn next(&mut self) -> u64 { + pub fn next(&mut self) -> u32 { if self.index == 624 { self.shuffle(); } - self.index += 2; - + self.index += 1; self.get_current_state() } @@ -95,7 +100,7 @@ impl Sfmt { } } -impl Default for Sfmt { +impl Default for Sfmt32 { fn default() -> Self { Self { index: 0, @@ -104,12 +109,63 @@ impl Default for Sfmt { } } -impl Rng for Sfmt { +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Sfmt64 { + sfmt: Sfmt32, +} + +impl Sfmt64 { + pub fn new(seed: u32) -> Self { + Self { + sfmt: Sfmt32::new(seed), + } + } + + fn get_current_state(&self) -> u64 { + let lo = self.sfmt.get_current_state() as u64; + let hi = self.sfmt.get_next_state() as u64; + lo | (hi << 32) + } + + pub fn next(&mut self) -> u64 { + self.sfmt.next(); + let lo = self.sfmt.next() as u64; + let hi = self.sfmt.get_next_state() as u64; + lo | (hi << 32) + } +} + +impl Default for Sfmt64 { + fn default() -> Self { + Self { + sfmt: Sfmt32::default(), + } + } +} + +impl Rng for Sfmt32 { + type Seed = u32; + type CurrentState = u32; + + fn new(seed: Self::Seed) -> Self { + Sfmt32::new(seed) + } + + fn next_state(&mut self) -> Self::CurrentState { + self.next() + } + + fn current_state(&mut self) -> Self::CurrentState { + self.get_current_state() + } +} + +impl Rng for Sfmt64 { type Seed = u32; type CurrentState = u64; fn new(seed: Self::Seed) -> Self { - Sfmt::new(seed) + Sfmt64::new(seed) } fn next_state(&mut self) -> Self::CurrentState { @@ -127,7 +183,7 @@ mod test { #[test] fn test_shuffle() { - let mut rng = Sfmt::new(0x7725e5e1); + let mut rng = Sfmt64::new(0x7725e5e1); for _ in 0..1000 { rng.next(); } @@ -138,7 +194,7 @@ mod test { #[test] fn test_next_should_return_state_before_shuffle() { - let mut rng = Sfmt::new(0xc91cc389); + let mut rng = Sfmt64::new(0xc91cc389); for _ in 0..624 { rng.next(); } diff --git a/reader_core/src/utils/circular_counter.rs b/reader_core/src/utils/circular_counter.rs index 3d70b96..a845f43 100644 --- a/reader_core/src/utils/circular_counter.rs +++ b/reader_core/src/utils/circular_counter.rs @@ -14,7 +14,6 @@ impl CircularCounter { } else { self.value += 1; } - self.value } @@ -27,10 +26,16 @@ impl CircularCounter { self.value } - pub fn set(&mut self, value: usize) -> usize { + + pub fn _set(&mut self, value: usize) -> usize { self.value = value.clamp(MIN, MAX); self.value } + + pub fn reset(&mut self) -> usize { + self.value = MIN; + MIN + } } impl Default for CircularCounter { diff --git a/reader_core/src/utils/mod.rs b/reader_core/src/utils/mod.rs index 31bd68a..87d0abd 100644 --- a/reader_core/src/utils/mod.rs +++ b/reader_core/src/utils/mod.rs @@ -9,7 +9,6 @@ mod hook_gen6_seed; pub mod menu; mod show_view; pub mod sub_menu; -pub mod sub_menu_capture; pub use circular_counter::*; pub use daycare::*; diff --git a/reader_core/src/utils/sub_menu.rs b/reader_core/src/utils/sub_menu.rs index f9f0fce..897bf82 100644 --- a/reader_core/src/utils/sub_menu.rs +++ b/reader_core/src/utils/sub_menu.rs @@ -36,4 +36,15 @@ impl SubMenu { self.update_counter(is_locked); self.counter.value() } + + pub fn _set(&mut self, value: usize) -> usize { + self.counter._set(value) + } + + pub fn reset(&mut self) -> usize { + self.counter.reset() + } + pub fn increment(&mut self) -> usize { + self.counter.increment() + } } diff --git a/reader_core/src/utils/sub_menu_capture.rs b/reader_core/src/utils/sub_menu_capture.rs deleted file mode 100644 index 23713c7..0000000 --- a/reader_core/src/utils/sub_menu_capture.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::CircularCounter; -use crate::pnp; - -#[derive(Default)] -pub struct SubMenuCapture { - counter: CircularCounter, - value: u32, -} - -impl SubMenuCapture { - fn update_counter(&mut self, is_locked: bool, capture_value: u32, set_value: usize) { - if is_locked { - return; - } - - if pnp::is_just_pressed(pnp::Button::Ddown | pnp::Button::X) { - self.counter.increment(); - self.value = capture_value; - } else if pnp::is_just_pressed(pnp::Button::Dup | pnp::Button::X) { - self.counter.decrement(); - self.value = capture_value; - } else if pnp::is_just_pressed(pnp::Button::Dright | pnp::Button::X) { - self.counter.set(set_value); - self.value = capture_value; - } - } - - pub fn update_headless(&mut self, is_locked: bool, capture_value: u32, set_value: usize) -> usize { - self.update_counter(is_locked, capture_value, set_value); - self.counter.value() - } - - pub fn captured_value(&mut self) -> u32 { - self.value - } - - pub fn counter_value(&self) -> usize { - self.counter.value() - } -}