added pokemon data decryption and parsing

master
hheik 2023-04-01 16:07:09 +03:00
parent d26da8776b
commit 642cd9a76a
2 changed files with 142 additions and 8 deletions

View File

@ -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.

View File

@ -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)
}
}