From 08dfb0aa80668b80c4a31bd064f5f2d729e5b7f6 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Fri, 18 Aug 2017 13:49:00 -0400 Subject: Started working on serializing pokemon data The GBA program now sends serialized data about the first pokemon in the player's party over to the Wii. This data doesn't yet include all of the information that we will eventually want. It does, however, not transfer any private data, specifically IVs, EVs, and the personality value. It does this by deriving the public information (stats, nature, gender, shiny) before sending the pokemon over. Because of this, lookup tables for things such as base stats were needed, and given that these are large tables, it was easier to use the tables already existent in the game's ROM. Thus, the addresses of the three lookup tables that are now used are necessary for each ROM that this tool supports. I derived the addresses for version 1 of English Pokemon LeafGreen by dumping my own copy and searching through it with a text editor. Thus, at the current time, that cartridge is the only one that is supported. I will supplement this soon with addresses for the other four gen 3 carts that I have, but that will still not provide a very large amount of coverage. I have not yet decided how to address this issue. There is one current bug with the serialized data: the Wii doesn't seem to see the original trainer ID. Will fix. --- gba/source/serialize.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 gba/source/serialize.c (limited to 'gba/source/serialize.c') diff --git a/gba/source/serialize.c b/gba/source/serialize.c new file mode 100644 index 0000000..6303772 --- /dev/null +++ b/gba/source/serialize.c @@ -0,0 +1,227 @@ +#include "serialize.h" +#include "gamedata.h" +#include "link.h" + +#define UNOWN_SPECIES_INDEX 201 +#define SHEDINJA_SPECIES_INDEX 303 + +enum Stat { + StatAttack, + StatDefense, + StatSpeed, + StatSpAttack, + StatSpDefense +}; + +u32 CalculateStat( + u8 base, + u32 iv, + u8 ev, + u8 level, + u8 statIndex, + u8 nature) +{ + u32 n = (((2 * base + iv + ev / 4) * level) / 100) + 5; + + u8 naturePlus = (nature / 5); + u8 natureMinus = (nature % 5); + + if (naturePlus != natureMinus) + { + if (statIndex == naturePlus) + { + return (u32)(n * 110) / 100; + } else if (statIndex == natureMinus) + { + return (u32)(n * 90) / 100; + } + } + + return n; +} + +void PokemonIntermediateInit( + struct PokemonIntermediate* pki, + struct BoxPokemon* bpkm, + u16 trainerId, + u16 secretId, + const struct GameData* gameData) +{ + DecryptBoxPokemon(bpkm); + + struct PokemonSubstruct0* sub0 = GetBoxPokemonSubstruct0(bpkm); + struct PokemonSubstruct1* sub1 = GetBoxPokemonSubstruct1(bpkm); + struct PokemonSubstruct2* sub2 = GetBoxPokemonSubstruct2(bpkm); + struct PokemonSubstruct3* sub3 = GetBoxPokemonSubstruct3(bpkm); + + struct BaseStats* baseStats = &gameData->baseStats[sub0->species]; + + for (int i=0; inickname[i] = bpkm->nickname[i]; + } + + for (int i=0; iotName[i] = bpkm->otName[i]; + } + + pki->otId = bpkm->otId; + pki->otGender = sub3->otGender; + pki->species = gameData->natOrder[sub0->species - 1]; + pki->heldItem = sub0->heldItem; + pki->experience = sub0->experience; + + pki->moves[0] = sub1->moves[0]; + pki->moves[1] = sub1->moves[1]; + pki->moves[2] = sub1->moves[2]; + pki->moves[3] = sub1->moves[3]; + + pki->ppBonuses = sub0->ppBonuses; + pki->metLevel = sub3->metLevel; + pki->metLocation = sub3->metLocation; + pki->pokeball = sub3->pokeball; + pki->altAbility = sub3->altAbility; + + // Derive nature from the personality value. + pki->nature = (bpkm->personality % 25); + + // Derive gender from the personality value. + int genderThreshold = baseStats->genderRatio; + + if ((genderThreshold == 0) || (genderThreshold == 255)) + { + pki->gender = 0; + } else if (genderThreshold == 254) + { + pki->gender = 1; + } else { + u8 genderDeterminer = bpkm->personality & 0x000000FF; + + if (genderDeterminer >= genderThreshold) + { + pki->gender = 0; + } else { + pki->gender = 1; + } + } + + // Determine shininess from the personality value. + u16 shinyDeterminer = + (trainerId) + ^ (secretId) + ^ ((bpkm->personality >> 16) & 0x0000FFFF) + ^ (bpkm->personality & 0x0000FFFF); + + if (shinyDeterminer < 8) + { + pki->shiny = 1; + } else { + pki->shiny = 0; + } + + // Determine Unown letter from the personality value. + if (sub0->species == UNOWN_SPECIES_INDEX) + { + u8 unownDeterminer = + ((bpkm->personality & 0x07000000) >> 18) + | ((bpkm->personality & 0x00070000) >> 12) + | ((bpkm->personality & 0x00000700) >> 6) + | (bpkm->personality & 0x00000007); + + pki->unownLetter = (unownDeterminer % 28); + } + + // Calculate level from experience. + pki->level = 1; + + const u32* expTable = gameData->expTables[baseStats->growthRate]; + while ((pki->level <= 100) && (expTable[pki->level] <= sub0->experience)) + { + pki->level++; + } + + pki->level--; + + // Calculate stats. + if (sub0->species == SHEDINJA_SPECIES_INDEX) + { + pki->hp = 1; + } else { + u32 n = 2 * baseStats->baseHP + sub3->hpIV; + pki->hp = (((n + sub2->hpEV / 4) * pki->level) / 100) + pki->level + 10; + } + + pki->attack = CalculateStat( + baseStats->baseAttack, + sub3->attackIV, + sub2->attackEV, + pki->level, + 0, + pki->nature); + + pki->defense = CalculateStat( + baseStats->baseDefense, + sub3->defenseIV, + sub2->defenseEV, + pki->level, + 1, + pki->nature); + + pki->speed = CalculateStat( + baseStats->baseSpeed, + sub3->speedIV, + sub2->speedEV, + pki->level, + 2, + pki->nature); + + pki->spAttack = CalculateStat( + baseStats->baseSpAttack, + sub3->spAttackIV, + sub2->spAttackEV, + pki->level, + 3, + pki->nature); + + pki->spDefense = CalculateStat( + baseStats->baseSpDefense, + sub3->spDefenseIV, + sub2->spDefenseEV, + pki->level, + 4, + pki->nature); + + // Approximate the values of the contest conditions. + pki->cool = ((u16)(sub2->cool) * 10) / 255; + pki->beauty = ((u16)(sub2->beauty) * 10) / 255; + pki->cute = ((u16)(sub2->cute) * 10) / 255; + pki->smart = ((u16)(sub2->smart) * 10) / 255; + pki->tough = ((u16)(sub2->tough) * 10) / 255; + pki->sheen = ((u16)(sub2->sheen) * 10) / 255; + + // Determine Pokerus status. + if (sub3->pokerus & 0xF0) + { + if (sub3->pokerus & 0x0F) + { + pki->pokerus = 1; + } else { + pki->pokerus = 2; + } + } else { + pki->pokerus = 0; + } + + EncryptBoxPokemon(bpkm); +} + +void PokemonIntermediateStream(struct PokemonIntermediate* pki) +{ + u32* raw = (u32*)pki; + + for (int i=0; i<(sizeof(struct PokemonIntermediate)/4); i++) + { + directSendU32(raw[i]); + } +} -- cgit 1.4.1