added pokemon data decryption and parsing
parent
d26da8776b
commit
642cd9a76a
|
|
@ -1,10 +1,33 @@
|
|||
use super::inspector::{Battle, PokemonInstance};
|
||||
use crate::pokemon::*;
|
||||
|
||||
const PLAYER_PARTY_OFFSET: usize = 0x21E42C;
|
||||
const ENEMY_PARTY_OFFSET: usize = 0x258874;
|
||||
const POKEMON_SIZE: usize = 0xDC;
|
||||
const PARTY_MAX_SIZE: usize = 6;
|
||||
|
||||
/// All the block offsets for table A (growth) based on pokemon's shift value
|
||||
const OFFSET_TABLE_A: [usize; 24] = [
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3,
|
||||
];
|
||||
/// All the block offsets for table B (attack) based on pokemon's shift value
|
||||
const OFFSET_TABLE_B: [usize; 24] = [
|
||||
1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2,
|
||||
];
|
||||
/// All the block offsets for table C (effort) based on pokemon's shift value
|
||||
const OFFSET_TABLE_C: [usize; 24] = [
|
||||
2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1,
|
||||
];
|
||||
/// All the block offsets for table D (misc) based on pokemon's shift value
|
||||
const OFFSET_TABLE_D: [usize; 24] = [
|
||||
3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
struct Header {
|
||||
pid: u32,
|
||||
checksum: u16,
|
||||
}
|
||||
|
||||
pub fn parse_battle_party(data: &Vec<u8>) -> Result<Battle, String> {
|
||||
let mut battle = Battle::default();
|
||||
for i in 0..PARTY_MAX_SIZE {
|
||||
|
|
@ -24,24 +47,99 @@ fn try_parse(data: &[u8], offset: usize) -> Option<PokemonInstance> {
|
|||
return None;
|
||||
}
|
||||
let decrypted = decrypt_pokemon(encrypted).ok()?;
|
||||
// println!("pokemon:\n{decrypted:?}");
|
||||
let pokemon = parse_pokemon(&decrypted).ok()?;
|
||||
Some(pokemon)
|
||||
}
|
||||
|
||||
fn decrypt_pokemon(encrypted: &[u8]) -> Result<Vec<u8>, String> {
|
||||
validate_size(encrypted)?;
|
||||
let mut result = vec![0; POKEMON_SIZE];
|
||||
// NOTIMP
|
||||
Ok(result)
|
||||
const PRNG_ADD: u32 = 0x6073;
|
||||
const PRNG_MULT: u32 = 0x41C64E6D;
|
||||
|
||||
let mut decrypted = vec![0; POKEMON_SIZE];
|
||||
let header = read_header(encrypted)?;
|
||||
|
||||
// First 8 bytes are not encrypted
|
||||
for i in 0x00..0x08 {
|
||||
decrypted[i] = encrypted[i]
|
||||
}
|
||||
|
||||
fn parse_pokemon(decrypted: &[u8]) -> Result<PokemonInstance, String> {
|
||||
validate_size(decrypted)?;
|
||||
let mut prng = header.checksum as u32;
|
||||
for i in (0x08..0x88).step_by(2) {
|
||||
prng = prng.wrapping_mul(PRNG_MULT).wrapping_add(PRNG_ADD);
|
||||
let decrypted_bytes = (read_short(encrypted, i) ^ (prng >> 16) as u16).to_le_bytes();
|
||||
decrypted[i] = decrypted_bytes[0];
|
||||
decrypted[i + 1] = decrypted_bytes[1];
|
||||
}
|
||||
|
||||
let mut prng = header.pid as u32;
|
||||
// for i in (0x88..0xEC).step_by(2) {
|
||||
for i in (0x88..0xDC).step_by(2) {
|
||||
prng = prng.wrapping_mul(PRNG_MULT).wrapping_add(PRNG_ADD);
|
||||
let decrypted_bytes = (read_short(encrypted, i) ^ (prng >> 16) as u16).to_le_bytes();
|
||||
decrypted[i] = decrypted_bytes[0];
|
||||
decrypted[i + 1] = decrypted_bytes[1];
|
||||
}
|
||||
|
||||
Ok(decrypted)
|
||||
}
|
||||
|
||||
fn parse_pokemon(data: &[u8]) -> Result<PokemonInstance, String> {
|
||||
validate_size(data)?;
|
||||
const SHUFFLE_AND: u32 = 0x3E000;
|
||||
const SHUFFLE_RSHIFT: u32 = 0xD;
|
||||
const BLOCK_SIZE: usize = 0x20;
|
||||
|
||||
let mut instance = PokemonInstance::default();
|
||||
// NOTIMP
|
||||
|
||||
let header = read_header(data)?;
|
||||
let shift = ((header.pid & SHUFFLE_AND) >> SHUFFLE_RSHIFT) % 24;
|
||||
let offset_a: usize = OFFSET_TABLE_A[shift as usize] * BLOCK_SIZE + 0x08;
|
||||
let offset_b: usize = OFFSET_TABLE_B[shift as usize] * BLOCK_SIZE + 0x08;
|
||||
let offset_c: usize = OFFSET_TABLE_C[shift as usize] * BLOCK_SIZE + 0x08;
|
||||
let offset_d: usize = OFFSET_TABLE_D[shift as usize] * BLOCK_SIZE + 0x08;
|
||||
|
||||
instance.nature = Some(Nature {
|
||||
pid: (header.pid % 25) as u8,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
instance.pokemon.national = read_short(data, offset_a + 0x00);
|
||||
|
||||
let ivs = read_long(data, offset_b + 0x10);
|
||||
instance
|
||||
.ivs
|
||||
.insert(crate::pokemon::BaseStat::Hp, ((ivs >> 0) & 31) as u8);
|
||||
instance
|
||||
.ivs
|
||||
.insert(crate::pokemon::BaseStat::Attack, ((ivs >> 5) & 31) as u8);
|
||||
instance
|
||||
.ivs
|
||||
.insert(crate::pokemon::BaseStat::Defense, ((ivs >> 10) & 31) as u8);
|
||||
instance
|
||||
.ivs
|
||||
.insert(crate::pokemon::BaseStat::Speed, ((ivs >> 15) & 31) as u8);
|
||||
instance.ivs.insert(
|
||||
crate::pokemon::BaseStat::SpecialAttack,
|
||||
((ivs >> 20) & 31) as u8,
|
||||
);
|
||||
instance.ivs.insert(
|
||||
crate::pokemon::BaseStat::SpecialDefense,
|
||||
((ivs >> 25) & 31) as u8,
|
||||
);
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
fn read_header(data: &[u8]) -> Result<Header, String> {
|
||||
validate_size(data)?;
|
||||
Ok(Header {
|
||||
pid: read_long(data, 0),
|
||||
checksum: read_short(data, 6),
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the raw data contains a pokemon.
|
||||
/// If the header section (8 bytes) are all 0x00, then the data doens't contain a pokemon.
|
||||
/// This works with both encrypted and decryped forms, as the header is never encrypted.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{database::Database, pokemon::*};
|
||||
|
||||
use super::{
|
||||
battle::parse_battle_party,
|
||||
inspector::{Battle, Inspector},
|
||||
|
|
@ -32,10 +34,44 @@ struct EventQueue {
|
|||
updates: Arc<Mutex<Vec<ParsedBattleEvent>>>,
|
||||
}
|
||||
|
||||
fn flush_updates(queue: Res<EventQueue>, mut events: EventWriter<ParsedBattleEvent>) {
|
||||
fn flush_updates(
|
||||
queue: Res<EventQueue>,
|
||||
pokedex: Res<Database<Pokemon>>,
|
||||
natures: Res<Database<Nature>>,
|
||||
mut events: EventWriter<ParsedBattleEvent>,
|
||||
) {
|
||||
match queue.updates.lock() {
|
||||
Ok(mut updates) => {
|
||||
for update in updates.drain(..) {
|
||||
for mut update in updates.drain(..) {
|
||||
for mut instance in update
|
||||
.0
|
||||
.player_party
|
||||
.iter_mut()
|
||||
.chain(update.0.enemy_party.iter_mut())
|
||||
{
|
||||
// Fill pokemon data
|
||||
if let Some(pokemon) = pokedex
|
||||
.map
|
||||
.values()
|
||||
.find(|pokemon| pokemon.national == instance.pokemon.national)
|
||||
{
|
||||
instance.pokemon = pokemon.clone();
|
||||
} else {
|
||||
warn!("National dex not found: {}", instance.pokemon.national);
|
||||
}
|
||||
|
||||
// Fill nature data
|
||||
if let Some(instance_nature) = instance.nature.clone() {
|
||||
if let Some(nature) = natures
|
||||
.map
|
||||
.values()
|
||||
.find(|nature| nature.pid == instance_nature.pid)
|
||||
{
|
||||
instance.nature = Some(nature.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events.send(update)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue