From 51c542d8d9e2c64d171ea70cedd789859d4a402f Mon Sep 17 00:00:00 2001 From: zaksabeast <19464008+zaksabeast@users.noreply.github.com> Date: Sun, 30 Jan 2022 15:18:34 -0600 Subject: [PATCH] Add basic arceus support --- libs/csight-core/Cargo.lock | 2 +- libs/csight-core/Cargo.toml | 2 +- libs/csight-core/c-export/arceus.h | 28 ++++ libs/csight-core/c-export/csight-core.h | 3 + libs/csight-core/c-export/pk9.h | 130 +++++++++++++++++ libs/csight-core/c-export/spawn.h | 71 ++++++++++ libs/csight-core/src/arceus/mod.rs | 7 + libs/csight-core/src/arceus/offsets.rs | 9 ++ libs/csight-core/src/arceus/read.rs | 124 ++++++++++++++++ libs/csight-core/src/arceus/rng.rs | 13 ++ libs/csight-core/src/arceus/spawn.rs | 74 ++++++++++ libs/csight-core/src/lib.rs | 1 + libs/csight-core/src/pkm/mod.rs | 3 + libs/csight-core/src/pkm/pk9.rs | 179 ++++++++++++++++++++++++ src/constants.hpp | 1 + src/main.cpp | 4 + src/views/arceus/main-view.hpp | 30 ++++ src/views/arceus/spawn-list-view.hpp | 44 ++++++ src/views/arceus/spawn-view.hpp | 43 ++++++ 19 files changed, 766 insertions(+), 2 deletions(-) create mode 100644 libs/csight-core/c-export/arceus.h create mode 100644 libs/csight-core/c-export/pk9.h create mode 100644 libs/csight-core/c-export/spawn.h create mode 100644 libs/csight-core/src/arceus/mod.rs create mode 100644 libs/csight-core/src/arceus/offsets.rs create mode 100644 libs/csight-core/src/arceus/read.rs create mode 100644 libs/csight-core/src/arceus/rng.rs create mode 100644 libs/csight-core/src/arceus/spawn.rs create mode 100644 libs/csight-core/src/pkm/pk9.rs create mode 100644 src/views/arceus/main-view.hpp create mode 100644 src/views/arceus/spawn-list-view.hpp create mode 100644 src/views/arceus/spawn-view.hpp diff --git a/libs/csight-core/Cargo.lock b/libs/csight-core/Cargo.lock index 7b9743d3..04fbd655 100644 --- a/libs/csight-core/Cargo.lock +++ b/libs/csight-core/Cargo.lock @@ -104,7 +104,7 @@ dependencies = [ [[package]] name = "pkm-rs" version = "0.1.0" -source = "git+https://github.com/zaksabeast/pkm-rs.git?rev=d8c9316#d8c931604bc2fc6b29b89eac5ad64896f0741b36" +source = "git+https://github.com/zaksabeast/pkm-rs.git?rev=f9b89aa#f9b89aa05e3ceed3056e281c0f9ec22a9dd06724" dependencies = [ "no_std_io", "num_enum", diff --git a/libs/csight-core/Cargo.toml b/libs/csight-core/Cargo.toml index b8c441e7..7e2c8491 100644 --- a/libs/csight-core/Cargo.toml +++ b/libs/csight-core/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["staticlib"] num_enum = { version = "0.5", default-features = false } cstr_core = { version = "0.2.4", default-features = false, features = ["alloc"] } no_std_io = { git = "https://github.com/zaksabeast/no_std_io.git", rev = "77499d6" } -pkm-rs = { git = "https://github.com/zaksabeast/pkm-rs.git", rev = "d8c9316" } +pkm-rs = { git = "https://github.com/zaksabeast/pkm-rs.git", rev = "f9b89aa" } safe-transmute = { version = "0.11", default-features = false } [target.'cfg(target_os = "horizon")'.dependencies] diff --git a/libs/csight-core/c-export/arceus.h b/libs/csight-core/c-export/arceus.h new file mode 100644 index 00000000..8359b0f5 --- /dev/null +++ b/libs/csight-core/c-export/arceus.h @@ -0,0 +1,28 @@ +#pragma once + +#include "pk9.h" +#include "rng_tracker.h" + +#ifdef __cplusplus +#include + +namespace csight::arceus { + extern "C" { +#endif + + pk9_t *arceus_read_party_pokemon(u8 index); + pk9_t *arceus_read_wild_pokemon(); + xoroshiro_tracker_t *arceus_get_main_rng_tracker(); + +#ifdef __cplusplus + } + + std::shared_ptr read_party_pokemon(u8 index) { return std::make_shared(arceus_read_party_pokemon(index)); } + + std::shared_ptr read_wild_pokemon() { return std::make_shared(arceus_read_wild_pokemon()); } + + std::shared_ptr get_main_rng_tracker() { + return std::make_shared((void *)arceus_get_main_rng_tracker(), RngType::Xoroshiro); + } +}; +#endif diff --git a/libs/csight-core/c-export/csight-core.h b/libs/csight-core/c-export/csight-core.h index 88ade6d9..0569c9fa 100644 --- a/libs/csight-core/c-export/csight-core.h +++ b/libs/csight-core/c-export/csight-core.h @@ -1,9 +1,12 @@ #pragma once +#include "arceus.h" #include "bdsp.h" #include "den.h" #include "pk8.h" +#include "pk9.h" #include "rng_tracker.h" +#include "spawn.h" #include "swsh.h" #include "trainer_info.h" #include diff --git a/libs/csight-core/c-export/pk9.h b/libs/csight-core/c-export/pk9.h new file mode 100644 index 00000000..5009be4f --- /dev/null +++ b/libs/csight-core/c-export/pk9.h @@ -0,0 +1,130 @@ +#pragma once + +#include "pkx.h" +#include + +#ifdef __cplusplus + +#include +#include +#include + +namespace csight { + extern "C" { +#endif + + typedef struct pk9 pk9_t; + + void free_pk9(pk9_t *ptr); + + u32 pk9_encryption_constant(pk9_t *ptr); + void pk9_species_string(pk9_t *ptr, char *out, size_t out_size); + u16 pk9_tid(pk9_t *ptr); + u16 pk9_sid(pk9_t *ptr); + void pk9_ability_string(pk9_t *ptr, char *out, size_t out_size); + u8 pk9_ability_number(pk9_t *ptr); + u32 pk9_pid(pk9_t *ptr); + void pk9_nature_string(pk9_t *ptr, char *out, size_t out_size); + void pk9_minted_nature_string(pk9_t *ptr, char *out, size_t out_size); + u8 pk9_gender(pk9_t *ptr); + Stats pk9_evs(pk9_t *ptr); + Stats pk9_ivs(pk9_t *ptr); + void pk9_move1_string(pk9_t *ptr, char *out, size_t out_size); + void pk9_move2_string(pk9_t *ptr, char *out, size_t out_size); + void pk9_move3_string(pk9_t *ptr, char *out, size_t out_size); + void pk9_move4_string(pk9_t *ptr, char *out, size_t out_size); + u8 pk9_language(pk9_t *ptr); + u8 pk9_current_friendship(pk9_t *ptr); + bool pk9_is_shiny(pk9_t *ptr); + bool pk9_is_egg(pk9_t *ptr); + bool find_pk9_raid_seed(u64 *out, pk9_t *ptr); + +#ifdef __cplusplus + } + + class Pk9 : public Pkx { + public: + static const size_t StoredSize = 328; + + Pk9(pk9_t *pk9) : m_pk9(pk9) { } + ~Pk9() { free_pk9(m_pk9); } + + u32 EncryptionConstant() { return pk9_encryption_constant(m_pk9); } + + std::string SpeciesString() { + char text[13]; + pk9_species_string(m_pk9, text, 13); + return std::string(text); + } + + u16 Tid() { return pk9_tid(m_pk9); } + + u16 Sid() { return pk9_sid(m_pk9); } + + std::string AbilityString() { + char text[17]; + pk9_ability_string(m_pk9, text, 17); + return std::string(text); + } + + u8 AbilityNumber() { return pk9_ability_number(m_pk9); } + + u32 Pid() { return pk9_pid(m_pk9); } + + std::string NatureString() { + char text[8]; + pk9_nature_string(m_pk9, text, 8); + return std::string(text); + } + + std::string MintedNatureString() { + char text[8]; + pk9_minted_nature_string(m_pk9, text, 8); + return std::string(text); + } + + u8 Gender() { return pk9_gender(m_pk9); } + + Stats Evs() { return pk9_evs(m_pk9); } + + Stats Ivs() { return pk9_ivs(m_pk9); } + + std::string Move1String() { + char text[28]; + pk9_move1_string(m_pk9, text, 28); + return std::string(text); + } + + std::string Move2String() { + char text[28]; + pk9_move2_string(m_pk9, text, 28); + return std::string(text); + } + + std::string Move3String() { + char text[28]; + pk9_move3_string(m_pk9, text, 28); + return std::string(text); + } + + std::string Move4String() { + char text[28]; + pk9_move4_string(m_pk9, text, 28); + return std::string(text); + } + + u8 CurrentFriendship() { return pk9_current_friendship(m_pk9); } + + u8 Language() { return pk9_language(m_pk9); } + + bool IsShiny() { return pk9_is_shiny(m_pk9); } + + bool IsEgg() { return pk9_is_egg(m_pk9); } + + std::optional FindRaidSeed() { return std::nullopt; } + + private: + pk9_t *m_pk9; + }; +}; +#endif diff --git a/libs/csight-core/c-export/spawn.h b/libs/csight-core/c-export/spawn.h new file mode 100644 index 00000000..5251d683 --- /dev/null +++ b/libs/csight-core/c-export/spawn.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus + +namespace csight::arceus { + extern "C" { +#endif + typedef struct arceus_spawn arceus_spawn_t; + + u64 spawn_get_hash(arceus_spawn_t *ptr); + u64 spawn_get_seed(arceus_spawn_t *ptr); + bool spawn_get_is_active(arceus_spawn_t *ptr); + arceus_spawn_t *free_spawn(arceus_spawn_t *ptr); + arceus_spawn_t *arceus_read_spawn(size_t index); + arceus_spawn_t *arceus_read_next_active_spawn(size_t start_index, size_t *found_index); + size_t arceus_read_active_spawn_count(); + size_t arceus_read_spawn_count(); + +#ifdef __cplusplus + } + + class Spawn { + public: + static const size_t Size = 0x18; + + Spawn(size_t index, bool find_next_active = false) { + if (find_next_active) { + m_spawn = arceus_read_next_active_spawn(index, &m_index); + } else { + m_spawn = arceus_read_spawn(index); + m_index = index; + } + } + ~Spawn() { free_spawn(m_spawn); } + + size_t Index() { return m_index; } + + u64 Hash() { return spawn_get_hash(m_spawn); } + + u64 Seed() { return spawn_get_seed(m_spawn); } + + bool IsActive() { return spawn_get_is_active(m_spawn); } + + private: + arceus_spawn_t *m_spawn; + size_t m_index; + }; + + // TODO: clean. This was super dirty and quick + std::vector> readActiveSpawns(size_t start_index, size_t end_index) { + std::vector> result; + + for (size_t next_index = start_index; next_index < end_index;) { + auto spawn = std::make_shared(next_index, true); + + if (!spawn->IsActive()) { + break; + } + + next_index = spawn->Index() + 1; + result.push_back(spawn); + } + + return result; + } +}; +#endif diff --git a/libs/csight-core/src/arceus/mod.rs b/libs/csight-core/src/arceus/mod.rs new file mode 100644 index 00000000..da600f89 --- /dev/null +++ b/libs/csight-core/src/arceus/mod.rs @@ -0,0 +1,7 @@ +mod read; +pub use read::*; + +mod offsets; +pub mod rng; + +mod spawn; diff --git a/libs/csight-core/src/arceus/offsets.rs b/libs/csight-core/src/arceus/offsets.rs new file mode 100644 index 00000000..57cabaac --- /dev/null +++ b/libs/csight-core/src/arceus/offsets.rs @@ -0,0 +1,9 @@ +use num_enum::IntoPrimitive; + +#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive)] +#[repr(u64)] +pub enum Offset { + PlayerSingleton = 0x4268000, + BattleSingleton = 0x4267f00, + SpawnSingleton = 0x4267ee0, +} diff --git a/libs/csight-core/src/arceus/read.rs b/libs/csight-core/src/arceus/read.rs new file mode 100644 index 00000000..745dc6b1 --- /dev/null +++ b/libs/csight-core/src/arceus/read.rs @@ -0,0 +1,124 @@ +use super::{offsets::Offset, spawn::Spawn}; +use crate::dmntcht::DmntReader; +use alloc::boxed::Box; +use pkm_rs::{Pk9, Pk9Data}; + +pub fn get_player_singleton() -> DmntReader { + DmntReader::new_from_main_nso(Offset::PlayerSingleton).follow(0) +} + +fn read_pokemon_from_poke_param(reader: DmntReader) -> Pk9 { + reader + .follow(0x98) + .follow(0x10) + .read_offset::(0) + .into() +} + +fn read_party_pokemon(index: u8) -> Pk9 { + let player_party = get_player_singleton().follow(0xd0).follow(0x58); + let party_member_param = player_party.follow(0x58 + (index as u64) * 8); + read_pokemon_from_poke_param(party_member_param) +} + +fn read_wild_pokemon() -> Pk9 { + let battle_setup = DmntReader::new_from_main_nso(Offset::BattleSingleton) + .follow(0) + .follow(0xb0) + .follow(0xe0) + .follow(0xd0); + let pokemon_count: u64 = battle_setup.read_offset(0x1a0); + let pokemon_container = battle_setup.follow(0xb0 + 8 * (pokemon_count - 1)); + let poke_param = pokemon_container.follow(0x70).follow(0x60); + read_pokemon_from_poke_param(poke_param) +} + +// Todo: make better after playing the game +fn get_spawn_list() -> DmntReader { + DmntReader::new_from_main_nso(Offset::SpawnSingleton) + .follow(0) + .follow(0x330) +} + +fn read_spawn_count() -> usize { + let spawn_list_byte_size = get_spawn_list().read_offset::(0x18) as usize; + (spawn_list_byte_size / Spawn::DATA_SIZE) - 1 +} + +fn read_spawn(index: usize) -> Spawn { + // Same value as Spawn::DATA_SIZE, but they aren't linked + let list_start = get_spawn_list().add(0x70); + let offset = index * Spawn::DATA_SIZE; + list_start.read_offset(offset as u64) +} + +fn read_active_spawn_count() -> usize { + let spawn_count = read_spawn_count(); + + let mut active_spawn_count = 0; + + for index in 0..spawn_count { + let spawn = read_spawn(index); + if spawn.get_is_active() { + active_spawn_count += 1; + } + } + + active_spawn_count +} + +fn read_next_active_spawn(start_index: usize) -> (Spawn, usize) { + let spawn_count = read_spawn_count(); + + for index in start_index..spawn_count { + let spawn = read_spawn(index); + if spawn.get_is_active() { + return (spawn, index); + } + } + + (Spawn::default(), 0) +} + +mod c_api { + use super::*; + + #[no_mangle] + pub unsafe extern "C" fn arceus_read_party_pokemon(index: u8) -> *mut Pk9 { + Box::into_raw(Box::new(read_party_pokemon(index))) + } + + #[no_mangle] + pub unsafe extern "C" fn arceus_read_wild_pokemon() -> *mut Pk9 { + Box::into_raw(Box::new(read_wild_pokemon())) + } + + #[no_mangle] + pub extern "C" fn arceus_read_active_spawn_count() -> usize { + read_active_spawn_count() + } + + #[no_mangle] + pub extern "C" fn arceus_read_spawn_count() -> usize { + read_spawn_count() + } + + #[no_mangle] + pub unsafe extern "C" fn arceus_read_spawn(index: usize) -> *mut Spawn { + Box::into_raw(Box::new(read_spawn(index))) + } + + #[no_mangle] + pub unsafe extern "C" fn arceus_read_next_active_spawn( + start_index: usize, + found_index: *mut usize, + ) -> *mut Spawn { + let (spawn, index) = read_next_active_spawn(start_index); + + assert!(!found_index.is_null()); + + *found_index = index; + + Box::into_raw(Box::new(spawn)) + } +} diff --git a/libs/csight-core/src/arceus/rng.rs b/libs/csight-core/src/arceus/rng.rs new file mode 100644 index 00000000..cdc8138c --- /dev/null +++ b/libs/csight-core/src/arceus/rng.rs @@ -0,0 +1,13 @@ +use super::read::get_player_singleton; + +mod c_api { + use super::*; + use crate::rng::XoroshiroTracker; + use alloc::boxed::Box; + + #[no_mangle] + pub unsafe extern "C" fn arceus_get_main_rng_tracker() -> *mut XoroshiroTracker { + let reader = get_player_singleton().follow(0xd8); + Box::into_raw(Box::new(XoroshiroTracker::new_from_reader(reader))) + } +} diff --git a/libs/csight-core/src/arceus/spawn.rs b/libs/csight-core/src/arceus/spawn.rs new file mode 100644 index 00000000..6eb69227 --- /dev/null +++ b/libs/csight-core/src/arceus/spawn.rs @@ -0,0 +1,74 @@ +use no_std_io::Reader; +use safe_transmute::TriviallyTransmutable; + +#[derive(Debug, Clone, Copy)] +pub struct Spawn { + data: [u8; Self::DATA_SIZE], +} + +impl Spawn { + pub const DATA_SIZE: usize = 0x80; + + pub fn get_hash(&self) -> u64 { + self.default_read(0) + } + + pub fn get_seed(&self) -> u64 { + self.default_read(0x20) + } + + pub fn get_is_active(&self) -> bool { + self.get_seed() != 0 + } +} + +impl Default for Spawn { + fn default() -> Self { + Self { + data: [0; Self::DATA_SIZE], + } + } +} + +impl Reader for Spawn { + fn get_slice(&self) -> &[u8] { + &self.data + } +} + +unsafe impl TriviallyTransmutable for Spawn {} + +mod c_api { + use super::*; + use alloc::boxed::Box; + + #[no_mangle] + pub unsafe extern "C" fn free_spawn(ptr: *mut Spawn) { + if ptr.is_null() { + return; + } + + Box::from_raw(ptr); + } + + #[no_mangle] + pub unsafe extern "C" fn spawn_get_hash(ptr: *mut Spawn) -> u64 { + assert!(!ptr.is_null()); + let spawn = &*ptr; + spawn.get_hash() + } + + #[no_mangle] + pub unsafe extern "C" fn spawn_get_seed(ptr: *mut Spawn) -> u64 { + assert!(!ptr.is_null()); + let spawn = &*ptr; + spawn.get_seed() + } + + #[no_mangle] + pub unsafe extern "C" fn spawn_get_is_active(ptr: *mut Spawn) -> bool { + assert!(!ptr.is_null()); + let spawn = &*ptr; + spawn.get_is_active() + } +} diff --git a/libs/csight-core/src/lib.rs b/libs/csight-core/src/lib.rs index c0b3266f..5b70449a 100644 --- a/libs/csight-core/src/lib.rs +++ b/libs/csight-core/src/lib.rs @@ -3,6 +3,7 @@ extern crate alloc; +mod arceus; mod bdsp; mod c_str; mod dmntcht; diff --git a/libs/csight-core/src/pkm/mod.rs b/libs/csight-core/src/pkm/mod.rs index ca4b9855..442f042b 100644 --- a/libs/csight-core/src/pkm/mod.rs +++ b/libs/csight-core/src/pkm/mod.rs @@ -1,2 +1,5 @@ mod pk8; mod pkx; + +mod pk9; +pub use pk9::*; diff --git a/libs/csight-core/src/pkm/pk9.rs b/libs/csight-core/src/pkm/pk9.rs new file mode 100644 index 00000000..d2346f7e --- /dev/null +++ b/libs/csight-core/src/pkm/pk9.rs @@ -0,0 +1,179 @@ +mod c_api { + use crate::c_str; + use alloc::boxed::Box; + use pkm_rs::{types, Pk9, Pkx}; + + #[no_mangle] + pub unsafe extern "C" fn free_pk9(ptr: *mut Pk9) { + if ptr.is_null() { + return; + } + + Box::from_raw(ptr); + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_encryption_constant(ptr: *mut Pk9) -> u32 { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.encryption_constant() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_species_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.species(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_tid(ptr: *mut Pk9) -> u16 { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.tid() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_sid(ptr: *mut Pk9) -> u16 { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.sid() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_ability_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.ability(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_ability_number(ptr: *mut Pk9) -> types::AbilityNumber { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.ability_number() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_pid(ptr: *mut Pk9) -> u32 { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.pid() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_nature_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.nature(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_minted_nature_string( + ptr: *mut Pk9, + dst: *mut u8, + dst_length: usize, + ) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.minted_nature(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_gender(ptr: *mut Pk9) -> types::Gender { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + if pk9.is_valid() { + pk9.gender() + } else { + types::Gender::Unknown + } + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_evs(ptr: *mut Pk9) -> types::Stats { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.evs() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_ivs(ptr: *mut Pk9) -> types::Stats { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.ivs() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_move1_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.move1(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_move2_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.move2(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_move3_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.move3(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_move4_string(ptr: *mut Pk9, dst: *mut u8, dst_length: usize) { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + + let value = pk9.move4(); + c_str::copy_display_as_c_str(value, dst, dst_length) + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_language(ptr: *mut Pk9) -> types::Language { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.language() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_is_shiny(ptr: *mut Pk9) -> bool { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.is_shiny() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_is_egg(ptr: *mut Pk9) -> bool { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.is_egg() + } + + #[no_mangle] + pub unsafe extern "C" fn pk9_current_friendship(ptr: *mut Pk9) -> u8 { + assert!(!ptr.is_null()); + let pk9 = &*ptr; + pk9.current_friendship() + } +} diff --git a/src/constants.hpp b/src/constants.hpp index 1db6c2f8..2af63823 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -5,4 +5,5 @@ enum SupportedGame { Shield = 0x01008DB008C2C000, BrilliantDiamond = 0x0100000011D90000, ShiningPearl = 0x010018E011D92000, + Arceus = 0x01001F5010DFA000, }; diff --git a/src/main.cpp b/src/main.cpp index 651e9e22..08872756 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "./utils/debug.hpp" #include "./utils/general.hpp" #include "constants.hpp" +#include "views/arceus/main-view.hpp" #include "views/bdsp/main-view.hpp" #include "views/error-view.hpp" #include "views/swsh/main-view.hpp" @@ -49,6 +50,9 @@ class MainOverlay : public tsl::Overlay { std::array supported_pearl_build_id = { 0x04, 0x6D, 0x13, 0x0F, 0x08, 0x73, 0x31, 0x4A }; tryGameWithBuildId(MainBdSpView, SupportedGame::ShiningPearl, supported_pearl_build_id); + std::array supported_arceus_build_id = { 0x5E, 0x42, 0x07, 0x16, 0x00, 0xD2, 0xBF, 0x1D }; + tryGameWithBuildId(MainArceusView, SupportedGame::Arceus, supported_arceus_build_id); + return initially("Unsupported game!\n\nTitle Id:\n" + utils::num_to_hex(title_id)); } diff --git a/src/views/arceus/main-view.hpp b/src/views/arceus/main-view.hpp new file mode 100644 index 00000000..9906f072 --- /dev/null +++ b/src/views/arceus/main-view.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "../../utils/general.hpp" +#include "../party-list-view.hpp" +#include "../rng-view.hpp" +#include "./spawn-list-view.hpp" +#include +#include + +class MainArceusView : public tsl::Gui { + public: + MainArceusView() { } + + virtual tsl::elm::Element *createUI() override { + auto frame = new tsl::elm::OverlayFrame("CaptureSight", " "); + auto list = new tsl::elm::List(); + + list->addItem(new tsl::elm::CategoryHeader("Pokemon")); + list->addItem(new PokemonViewButton("Wild", csight::arceus::read_wild_pokemon)); + list->addItem(new PartyListViewButton(csight::arceus::read_party_pokemon)); + list->addItem(new SpawnListViewButton()); + + list->addItem(new tsl::elm::CategoryHeader("RNG")); + list->addItem(new RngViewButton("Main RNG", csight::arceus::get_main_rng_tracker)); + + frame->setContent(list); + + return frame; + } +}; diff --git a/src/views/arceus/spawn-list-view.hpp b/src/views/arceus/spawn-list-view.hpp new file mode 100644 index 00000000..a0ef8133 --- /dev/null +++ b/src/views/arceus/spawn-list-view.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "../../components/button.hpp" +#include "../../constants.hpp" +#include "./spawn-view.hpp" +#include +#include +#include +#include +#include +#include + +class SpawnListView : public tsl::Gui { + public: + SpawnListView() { m_count = csight::arceus::arceus_read_spawn_count(); } + + virtual tsl::elm::Element *createUI() override { + auto frame = new tsl::elm::OverlayFrame("Active Spawns", " "); + auto list = new tsl::elm::List(); + + list->addItem(new tsl::elm::CategoryHeader("Spawn list")); + + size_t step = 100; + for (size_t i = 0; i < m_count; i += step) { + size_t end_count = i + std::min(m_count - i, step); + std::string label = "Spawns " + std::to_string(i) + " - " + std::to_string(end_count); + list->addItem(new SpawnViewButton(label, i, i + step)); + } + + frame->setContent(list); + + return frame; + } + + private: + size_t m_count; +}; + +class SpawnListViewButton : public Button { + public: + SpawnListViewButton() : Button("Active Spawns") { + this->onClick([]() { tsl::changeTo(); }); + } +}; diff --git a/src/views/arceus/spawn-view.hpp b/src/views/arceus/spawn-view.hpp new file mode 100644 index 00000000..2772fe81 --- /dev/null +++ b/src/views/arceus/spawn-view.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "../../components/button.hpp" +#include "../../constants.hpp" +#include "../../utils/general.hpp" +#include +#include +#include +#include +#include +#include + +class SpawnView : public tsl::Gui { + public: + SpawnView(size_t start_index, size_t end_index) { m_spawns = csight::arceus::readActiveSpawns(start_index, end_index); } + + virtual tsl::elm::Element *createUI() override { + auto frame = new tsl::elm::OverlayFrame("Active Spawns", " "); + auto list = new tsl::elm::List(); + + list->addItem(new tsl::elm::CategoryHeader("Spawn list")); + + for (auto spawn : m_spawns) { + std::string label = "Idx " + std::to_string(spawn->Index()) + " " + utils::num_to_hex(spawn->Seed()); + auto spawn_item = new tsl::elm::ListItem(label); + list->addItem(spawn_item); + } + + frame->setContent(list); + + return frame; + } + + private: + std::vector> m_spawns; +}; + +class SpawnViewButton : public Button { + public: + SpawnViewButton(std::string label, size_t start_index, size_t end_index) : Button(label) { + this->onClick([start_index, end_index]() { tsl::changeTo(start_index, end_index); }); + } +};