From 7c6cff1fb3f93e0b8220db34269c74bb81183c84 Mon Sep 17 00:00:00 2001 From: Nephrite Date: Mon, 28 Jul 2025 13:42:28 +0300 Subject: [PATCH] Get/SetMonData optimisation (#7313) --- include/gba/defines.h | 1 + include/pokemon.h | 10 +- src/pokemon.c | 596 +++++++++++++++++++++--------------------- test/pokemon.c | 25 ++ 4 files changed, 326 insertions(+), 306 deletions(-) diff --git a/include/gba/defines.h b/include/gba/defines.h index 674c5fdc14..754e95a6dd 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -26,6 +26,7 @@ #define ALIGNED(n) __attribute__((aligned(n))) #define PACKED __attribute__((packed)) #define TRANSPARENT __attribute__ ((__transparent_union__)) +#define ALWAYS_INLINE inline __attribute__((always_inline)) #define NONNULL __attribute__((__nonnull__)) #define SOUND_INFO_PTR (*(struct SoundInfo **)0x3007FF0) diff --git a/include/pokemon.h b/include/pokemon.h index ac525624cc..f3eea90000 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -16,7 +16,7 @@ #define FORM_SPECIES_END (0xffff) // Property labels for Get(Box)MonData / Set(Box)MonData -enum { +enum MonData { MON_DATA_PERSONALITY, MON_DATA_STATUS, MON_DATA_OT_ID, @@ -232,6 +232,14 @@ struct PokemonSubstruct3 max(sizeof(struct PokemonSubstruct2), \ sizeof(struct PokemonSubstruct3))))) +enum SubstructType +{ + SUBSTRUCT_TYPE_0, + SUBSTRUCT_TYPE_1, + SUBSTRUCT_TYPE_2, + SUBSTRUCT_TYPE_3, +}; + union PokemonSubstruct { struct PokemonSubstruct0 type0; diff --git a/src/pokemon.c b/src/pokemon.c index 6433acd701..8ddfd5d5b4 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -76,7 +76,9 @@ struct SpeciesItem }; static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon); -static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType); +static u16 CalculateBoxMonChecksumDecrypt(struct BoxPokemon *boxMon); +static u16 CalculateBoxMonChecksumReencrypt(struct BoxPokemon *boxMon); +static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, enum SubstructType substructType); static void EncryptBoxMon(struct BoxPokemon *boxMon); static void DecryptBoxMon(struct BoxPokemon *boxMon); static void Task_PlayMapChosenOrBattleBGM(u8 taskId); @@ -1656,24 +1658,36 @@ void CreateEnemyEventMon(void) static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon) { - u16 checksum = 0; - union PokemonSubstruct *substruct0 = GetSubstruct(boxMon, boxMon->personality, 0); - union PokemonSubstruct *substruct1 = GetSubstruct(boxMon, boxMon->personality, 1); - union PokemonSubstruct *substruct2 = GetSubstruct(boxMon, boxMon->personality, 2); - union PokemonSubstruct *substruct3 = GetSubstruct(boxMon, boxMon->personality, 3); - s32 i; + u32 checksum = 0; - for (i = 0; i < (s32)ARRAY_COUNT(substruct0->raw); i++) - checksum += substruct0->raw[i]; + for (u32 i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) + checksum += boxMon->secure.raw[i] + (boxMon->secure.raw[i] >> 16); - for (i = 0; i < (s32)ARRAY_COUNT(substruct1->raw); i++) - checksum += substruct1->raw[i]; + return checksum; +} - for (i = 0; i < (s32)ARRAY_COUNT(substruct2->raw); i++) - checksum += substruct2->raw[i]; +static u16 CalculateBoxMonChecksumDecrypt(struct BoxPokemon *boxMon) +{ + u32 checksum = 0; - for (i = 0; i < (s32)ARRAY_COUNT(substruct3->raw); i++) - checksum += substruct3->raw[i]; + for (u32 i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) + { + boxMon->secure.raw[i] ^= (boxMon->otId ^ boxMon->personality); + checksum += boxMon->secure.raw[i] + (boxMon->secure.raw[i] >> 16); + } + + return checksum; +} + +static u16 CalculateBoxMonChecksumReencrypt(struct BoxPokemon *boxMon) +{ + u32 checksum = 0; + + for (u32 i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) + { + checksum += boxMon->secure.raw[i] + (boxMon->secure.raw[i] >> 16); + boxMon->secure.raw[i] ^= (boxMon->otId ^ boxMon->personality); + } return checksum; } @@ -2194,8 +2208,7 @@ void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPositio static void EncryptBoxMon(struct BoxPokemon *boxMon) { - u32 i; - for (i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) + for (u32 i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) { boxMon->secure.raw[i] ^= boxMon->personality; boxMon->secure.raw[i] ^= boxMon->otId; @@ -2204,70 +2217,26 @@ static void EncryptBoxMon(struct BoxPokemon *boxMon) static void DecryptBoxMon(struct BoxPokemon *boxMon) { - u32 i; - for (i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) + for (u32 i = 0; i < ARRAY_COUNT(boxMon->secure.raw); i++) { boxMon->secure.raw[i] ^= boxMon->otId; boxMon->secure.raw[i] ^= boxMon->personality; } } -#define SUBSTRUCT_CASE(n, v1, v2, v3, v4) \ -case n: \ - { \ - \ - switch (substructType) \ - { \ - case 0: \ - substruct = &boxMon->secure.substructs[v1]; \ - break; \ - case 1: \ - substruct = &boxMon->secure.substructs[v2]; \ - break; \ - case 2: \ - substruct = &boxMon->secure.substructs[v3]; \ - break; \ - case 3: \ - substruct = &boxMon->secure.substructs[v4]; \ - break; \ - } \ - break; \ - } \ - - -static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType) +static const u8 sSubstructOffsets[4][24] = { - union PokemonSubstruct *substruct = NULL; + [SUBSTRUCT_TYPE_0] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3}, + [SUBSTRUCT_TYPE_1] = {1, 1, 2, 2, 3, 3, 0, 0, 2, 2, 3, 3, 0, 0, 1, 1, 3, 3, 0, 0, 1, 1, 2, 2}, + [SUBSTRUCT_TYPE_2] = {2, 3, 1, 3, 1, 2, 2, 3, 0, 3, 0, 2, 1, 3, 0, 3, 0, 1, 1, 2, 0, 2, 0, 1}, + [SUBSTRUCT_TYPE_3] = {3, 2, 3, 1, 2, 1, 3, 2, 3, 0, 2, 0, 3, 1, 3, 0, 1, 0, 2, 1, 2, 0, 1, 0} +}; - switch (personality % 24) - { - SUBSTRUCT_CASE( 0,0,1,2,3) - SUBSTRUCT_CASE( 1,0,1,3,2) - SUBSTRUCT_CASE( 2,0,2,1,3) - SUBSTRUCT_CASE( 3,0,3,1,2) - SUBSTRUCT_CASE( 4,0,2,3,1) - SUBSTRUCT_CASE( 5,0,3,2,1) - SUBSTRUCT_CASE( 6,1,0,2,3) - SUBSTRUCT_CASE( 7,1,0,3,2) - SUBSTRUCT_CASE( 8,2,0,1,3) - SUBSTRUCT_CASE( 9,3,0,1,2) - SUBSTRUCT_CASE(10,2,0,3,1) - SUBSTRUCT_CASE(11,3,0,2,1) - SUBSTRUCT_CASE(12,1,2,0,3) - SUBSTRUCT_CASE(13,1,3,0,2) - SUBSTRUCT_CASE(14,2,1,0,3) - SUBSTRUCT_CASE(15,3,1,0,2) - SUBSTRUCT_CASE(16,2,3,0,1) - SUBSTRUCT_CASE(17,3,2,0,1) - SUBSTRUCT_CASE(18,1,2,3,0) - SUBSTRUCT_CASE(19,1,3,2,0) - SUBSTRUCT_CASE(20,2,1,3,0) - SUBSTRUCT_CASE(21,3,1,2,0) - SUBSTRUCT_CASE(22,2,3,1,0) - SUBSTRUCT_CASE(23,3,2,1,0) - } +ARM_FUNC NOINLINE static u32 ConstantMod24(u32 a) { return a % 24; } - return substruct; +static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, enum SubstructType substructType) +{ + return &boxMon->secure.substructs[sSubstructOffsets[substructType][ConstantMod24(personality)]]; } /* GameFreak called GetMonData with either 2 or 3 arguments, for type @@ -2337,19 +2306,58 @@ u32 GetMonData2(struct Pokemon *mon, s32 field) return GetMonData3(mon, field, NULL); } -struct EvolutionTrackerBitfield -{ - u16 a: 5; - u16 b: 5; - u16 unused: 6; -}; union EvolutionTracker { - u16 value; - struct EvolutionTrackerBitfield asField; + u16 combinedValue:10; + struct { + u16 tracker1: 5; + u16 tracker2: 5; + }; }; +static ALWAYS_INLINE struct PokemonSubstruct0 *GetSubstruct0(struct BoxPokemon *boxMon) +{ + return &(GetSubstruct(boxMon, boxMon->personality, SUBSTRUCT_TYPE_0)->type0); +} + +static ALWAYS_INLINE struct PokemonSubstruct1 *GetSubstruct1(struct BoxPokemon *boxMon) +{ + return &(GetSubstruct(boxMon, boxMon->personality, SUBSTRUCT_TYPE_1)->type1); +} + +static ALWAYS_INLINE struct PokemonSubstruct2 *GetSubstruct2(struct BoxPokemon *boxMon) +{ + return &(GetSubstruct(boxMon, boxMon->personality, SUBSTRUCT_TYPE_2)->type2); +} + +static ALWAYS_INLINE struct PokemonSubstruct3 *GetSubstruct3(struct BoxPokemon *boxMon) +{ + return &(GetSubstruct(boxMon, boxMon->personality, SUBSTRUCT_TYPE_3)->type3); +} + +static bool32 IsBadEgg(struct BoxPokemon *boxMon) +{ + if (boxMon->isBadEgg) + return TRUE; + + if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum) + { + boxMon->isBadEgg = TRUE; + boxMon->isEgg = TRUE; + GetSubstruct3(boxMon)->isEgg = TRUE; + + return TRUE; + } + + return FALSE; +} + +static ALWAYS_INLINE bool32 IsEggOrBadEgg(struct BoxPokemon *boxMon) +{ + return GetSubstruct3(boxMon)->isEgg || IsBadEgg(boxMon); +} + /* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type * safety we have a GetBoxMonData macro (in include/pokemon.h) which * dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the @@ -2358,35 +2366,18 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) { s32 i; u32 retVal = 0; - struct PokemonSubstruct0 *substruct0 = NULL; - struct PokemonSubstruct1 *substruct1 = NULL; - struct PokemonSubstruct2 *substruct2 = NULL; - struct PokemonSubstruct3 *substruct3 = NULL; - union EvolutionTracker evoTracker; // Any field greater than MON_DATA_ENCRYPT_SEPARATOR is encrypted and must be treated as such if (field > MON_DATA_ENCRYPT_SEPARATOR) { - substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0); - substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1); - substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2); - substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3); - DecryptBoxMon(boxMon); - if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum) - { - boxMon->isBadEgg = TRUE; - boxMon->isEgg = TRUE; - substruct3->isEgg = TRUE; - } - switch (field) { case MON_DATA_NICKNAME: case MON_DATA_NICKNAME10: { - if (boxMon->isBadEgg) + if (IsBadEgg(boxMon)) { for (retVal = 0; retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS; @@ -2425,6 +2416,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) // so if both are 0 we assume that this is a vanilla // Pokémon and replace them with EOS. This means that // two CHAR_SPACE at the end of a nickname are trimmed. + struct PokemonSubstruct0 *substruct0 = GetSubstruct0(boxMon); if (field != MON_DATA_NICKNAME10 && POKEMON_NAME_LENGTH >= 12) { if (substruct0->nickname11 == 0 && substruct0->nickname12 == 0) @@ -2455,182 +2447,184 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; } case MON_DATA_SPECIES: - retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species; + retVal = IsBadEgg(boxMon) ? SPECIES_EGG : GetSubstruct0(boxMon)->species; break; case MON_DATA_HELD_ITEM: - retVal = substruct0->heldItem; + retVal = GetSubstruct0(boxMon)->heldItem; break; case MON_DATA_EXP: - retVal = substruct0->experience; + retVal = GetSubstruct0(boxMon)->experience; break; case MON_DATA_PP_BONUSES: - retVal = substruct0->ppBonuses; + retVal = GetSubstruct0(boxMon)->ppBonuses; break; case MON_DATA_FRIENDSHIP: - retVal = substruct0->friendship; + retVal = GetSubstruct0(boxMon)->friendship; break; case MON_DATA_MOVE1: - retVal = substruct1->move1; + retVal = GetSubstruct1(boxMon)->move1; break; case MON_DATA_MOVE2: - retVal = substruct1->move2; + retVal = GetSubstruct1(boxMon)->move2; break; case MON_DATA_MOVE3: - retVal = substruct1->move3; + retVal = GetSubstruct1(boxMon)->move3; break; case MON_DATA_MOVE4: - retVal = substruct1->move4; + retVal = GetSubstruct1(boxMon)->move4; break; case MON_DATA_PP1: - retVal = substruct1->pp1; + retVal = GetSubstruct1(boxMon)->pp1; break; case MON_DATA_PP2: - retVal = substruct1->pp2; + retVal = GetSubstruct1(boxMon)->pp2; break; case MON_DATA_PP3: - retVal = substruct1->pp3; + retVal = GetSubstruct1(boxMon)->pp3; break; case MON_DATA_PP4: - retVal = substruct1->pp4; + retVal = GetSubstruct1(boxMon)->pp4; break; case MON_DATA_HP_EV: - retVal = substruct2->hpEV; + retVal = GetSubstruct2(boxMon)->hpEV; break; case MON_DATA_ATK_EV: - retVal = substruct2->attackEV; + retVal = GetSubstruct2(boxMon)->attackEV; break; case MON_DATA_DEF_EV: - retVal = substruct2->defenseEV; + retVal = GetSubstruct2(boxMon)->defenseEV; break; case MON_DATA_SPEED_EV: - retVal = substruct2->speedEV; + retVal = GetSubstruct2(boxMon)->speedEV; break; case MON_DATA_SPATK_EV: - retVal = substruct2->spAttackEV; + retVal = GetSubstruct2(boxMon)->spAttackEV; break; case MON_DATA_SPDEF_EV: - retVal = substruct2->spDefenseEV; + retVal = GetSubstruct2(boxMon)->spDefenseEV; break; case MON_DATA_COOL: - retVal = substruct2->cool; + retVal = GetSubstruct2(boxMon)->cool; break; case MON_DATA_BEAUTY: - retVal = substruct2->beauty; + retVal = GetSubstruct2(boxMon)->beauty; break; case MON_DATA_CUTE: - retVal = substruct2->cute; + retVal = GetSubstruct2(boxMon)->cute; break; case MON_DATA_SMART: - retVal = substruct2->smart; + retVal = GetSubstruct2(boxMon)->smart; break; case MON_DATA_TOUGH: - retVal = substruct2->tough; + retVal = GetSubstruct2(boxMon)->tough; break; case MON_DATA_SHEEN: - retVal = substruct2->sheen; + retVal = GetSubstruct2(boxMon)->sheen; break; case MON_DATA_POKERUS: - retVal = substruct3->pokerus; + retVal = GetSubstruct3(boxMon)->pokerus; break; case MON_DATA_MET_LOCATION: - retVal = substruct3->metLocation; + retVal = GetSubstruct3(boxMon)->metLocation; break; case MON_DATA_MET_LEVEL: - retVal = substruct3->metLevel; + retVal = GetSubstruct3(boxMon)->metLevel; break; case MON_DATA_MET_GAME: - retVal = substruct3->metGame; + retVal = GetSubstruct3(boxMon)->metGame; break; case MON_DATA_POKEBALL: - retVal = substruct0->pokeball; + retVal = GetSubstruct0(boxMon)->pokeball; break; case MON_DATA_OT_GENDER: - retVal = substruct3->otGender; + retVal = GetSubstruct3(boxMon)->otGender; break; case MON_DATA_HP_IV: - retVal = substruct3->hpIV; + retVal = GetSubstruct3(boxMon)->hpIV; break; case MON_DATA_ATK_IV: - retVal = substruct3->attackIV; + retVal = GetSubstruct3(boxMon)->attackIV; break; case MON_DATA_DEF_IV: - retVal = substruct3->defenseIV; + retVal = GetSubstruct3(boxMon)->defenseIV; break; case MON_DATA_SPEED_IV: - retVal = substruct3->speedIV; + retVal = GetSubstruct3(boxMon)->speedIV; break; case MON_DATA_SPATK_IV: - retVal = substruct3->spAttackIV; + retVal = GetSubstruct3(boxMon)->spAttackIV; break; case MON_DATA_SPDEF_IV: - retVal = substruct3->spDefenseIV; + retVal = GetSubstruct3(boxMon)->spDefenseIV; break; case MON_DATA_IS_EGG: - retVal = substruct3->isEgg; + retVal = IsEggOrBadEgg(boxMon); break; case MON_DATA_ABILITY_NUM: - retVal = substruct3->abilityNum; + retVal = GetSubstruct3(boxMon)->abilityNum; break; case MON_DATA_COOL_RIBBON: - retVal = substruct3->coolRibbon; + retVal = GetSubstruct3(boxMon)->coolRibbon; break; case MON_DATA_BEAUTY_RIBBON: - retVal = substruct3->beautyRibbon; + retVal = GetSubstruct3(boxMon)->beautyRibbon; break; case MON_DATA_CUTE_RIBBON: - retVal = substruct3->cuteRibbon; + retVal = GetSubstruct3(boxMon)->cuteRibbon; break; case MON_DATA_SMART_RIBBON: - retVal = substruct3->smartRibbon; + retVal = GetSubstruct3(boxMon)->smartRibbon; break; case MON_DATA_TOUGH_RIBBON: - retVal = substruct3->toughRibbon; + retVal = GetSubstruct3(boxMon)->toughRibbon; break; case MON_DATA_CHAMPION_RIBBON: - retVal = substruct3->championRibbon; + retVal = GetSubstruct3(boxMon)->championRibbon; break; case MON_DATA_WINNING_RIBBON: - retVal = substruct3->winningRibbon; + retVal = GetSubstruct3(boxMon)->winningRibbon; break; case MON_DATA_VICTORY_RIBBON: - retVal = substruct3->victoryRibbon; + retVal = GetSubstruct3(boxMon)->victoryRibbon; break; case MON_DATA_ARTIST_RIBBON: - retVal = substruct3->artistRibbon; + retVal = GetSubstruct3(boxMon)->artistRibbon; break; case MON_DATA_EFFORT_RIBBON: - retVal = substruct3->effortRibbon; + retVal = GetSubstruct3(boxMon)->effortRibbon; break; case MON_DATA_MARINE_RIBBON: - retVal = substruct3->marineRibbon; + retVal = GetSubstruct3(boxMon)->marineRibbon; break; case MON_DATA_LAND_RIBBON: - retVal = substruct3->landRibbon; + retVal = GetSubstruct3(boxMon)->landRibbon; break; case MON_DATA_SKY_RIBBON: - retVal = substruct3->skyRibbon; + retVal = GetSubstruct3(boxMon)->skyRibbon; break; case MON_DATA_COUNTRY_RIBBON: - retVal = substruct3->countryRibbon; + retVal = GetSubstruct3(boxMon)->countryRibbon; break; case MON_DATA_NATIONAL_RIBBON: - retVal = substruct3->nationalRibbon; + retVal = GetSubstruct3(boxMon)->nationalRibbon; break; case MON_DATA_EARTH_RIBBON: - retVal = substruct3->earthRibbon; + retVal = GetSubstruct3(boxMon)->earthRibbon; break; case MON_DATA_WORLD_RIBBON: - retVal = substruct3->worldRibbon; + retVal = GetSubstruct3(boxMon)->worldRibbon; break; case MON_DATA_MODERN_FATEFUL_ENCOUNTER: - retVal = substruct3->modernFatefulEncounter; + retVal = GetSubstruct3(boxMon)->modernFatefulEncounter; break; case MON_DATA_SPECIES_OR_EGG: - retVal = substruct0->species; - if (substruct0->species && (substruct3->isEgg || boxMon->isBadEgg)) + retVal = GetSubstruct0(boxMon)->species; + if (retVal && IsEggOrBadEgg(boxMon)) retVal = SPECIES_EGG; break; case MON_DATA_IVS: + { + struct PokemonSubstruct3 *substruct3 = GetSubstruct3(boxMon); retVal = substruct3->hpIV | (substruct3->attackIV << 5) | (substruct3->defenseIV << 10) @@ -2638,9 +2632,11 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) | (substruct3->spAttackIV << 20) | (substruct3->spDefenseIV << 25); break; + } case MON_DATA_KNOWN_MOVES: - if (substruct0->species && !substruct3->isEgg) + if (GetSubstruct0(boxMon)->species && !IsEggOrBadEgg(boxMon)) { + struct PokemonSubstruct1 *substruct1 = GetSubstruct1(boxMon); u16 *moves = (u16 *)data; s32 i = 0; @@ -2657,9 +2653,10 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } break; case MON_DATA_RIBBON_COUNT: - retVal = 0; - if (substruct0->species && !substruct3->isEgg) + if (GetSubstruct0(boxMon)->species && !IsEggOrBadEgg(boxMon)) { + struct PokemonSubstruct3 *substruct3 = GetSubstruct3(boxMon); + retVal = 0; retVal += substruct3->coolRibbon; retVal += substruct3->beautyRibbon; retVal += substruct3->cuteRibbon; @@ -2680,75 +2677,81 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) } break; case MON_DATA_RIBBONS: - retVal = 0; - if (substruct0->species && !substruct3->isEgg) + if (GetSubstruct0(boxMon)->species && !IsEggOrBadEgg(boxMon)) { + struct PokemonSubstruct3 *substruct3 = GetSubstruct3(boxMon); retVal = substruct3->championRibbon - | (substruct3->coolRibbon << 1) - | (substruct3->beautyRibbon << 4) - | (substruct3->cuteRibbon << 7) - | (substruct3->smartRibbon << 10) - | (substruct3->toughRibbon << 13) - | (substruct3->winningRibbon << 16) - | (substruct3->victoryRibbon << 17) - | (substruct3->artistRibbon << 18) - | (substruct3->effortRibbon << 19) - | (substruct3->marineRibbon << 20) - | (substruct3->landRibbon << 21) - | (substruct3->skyRibbon << 22) - | (substruct3->countryRibbon << 23) - | (substruct3->nationalRibbon << 24) - | (substruct3->earthRibbon << 25) - | (substruct3->worldRibbon << 26); + | (substruct3->coolRibbon << 1) + | (substruct3->beautyRibbon << 4) + | (substruct3->cuteRibbon << 7) + | (substruct3->smartRibbon << 10) + | (substruct3->toughRibbon << 13) + | (substruct3->winningRibbon << 16) + | (substruct3->victoryRibbon << 17) + | (substruct3->artistRibbon << 18) + | (substruct3->effortRibbon << 19) + | (substruct3->marineRibbon << 20) + | (substruct3->landRibbon << 21) + | (substruct3->skyRibbon << 22) + | (substruct3->countryRibbon << 23) + | (substruct3->nationalRibbon << 24) + | (substruct3->earthRibbon << 25) + | (substruct3->worldRibbon << 26); } break; case MON_DATA_HYPER_TRAINED_HP: - retVal = substruct1->hyperTrainedHP; + retVal = GetSubstruct1(boxMon)->hyperTrainedHP; break; case MON_DATA_HYPER_TRAINED_ATK: - retVal = substruct1->hyperTrainedAttack; + retVal = GetSubstruct1(boxMon)->hyperTrainedAttack; break; case MON_DATA_HYPER_TRAINED_DEF: - retVal = substruct1->hyperTrainedDefense; + retVal = GetSubstruct1(boxMon)->hyperTrainedDefense; break; case MON_DATA_HYPER_TRAINED_SPEED: - retVal = substruct1->hyperTrainedSpeed; + retVal = GetSubstruct1(boxMon)->hyperTrainedSpeed; break; case MON_DATA_HYPER_TRAINED_SPATK: - retVal = substruct1->hyperTrainedSpAttack; + retVal = GetSubstruct1(boxMon)->hyperTrainedSpAttack; break; case MON_DATA_HYPER_TRAINED_SPDEF: - retVal = substruct1->hyperTrainedSpDefense; + retVal = GetSubstruct1(boxMon)->hyperTrainedSpDefense; break; case MON_DATA_IS_SHADOW: - retVal = substruct3->isShadow; + retVal = GetSubstruct3(boxMon)->isShadow; break; case MON_DATA_DYNAMAX_LEVEL: - retVal = substruct3->dynamaxLevel; + retVal = GetSubstruct3(boxMon)->dynamaxLevel; break; case MON_DATA_GIGANTAMAX_FACTOR: - retVal = substruct3->gigantamaxFactor; + retVal = GetSubstruct3(boxMon)->gigantamaxFactor; break; case MON_DATA_TERA_TYPE: - if (gSpeciesInfo[substruct0->species].forceTeraType) { - retVal = gSpeciesInfo[substruct0->species].forceTeraType; - } - else if (substruct0->teraType == TYPE_NONE) // Tera Type hasn't been modified so we can just use the personality - { - const u8 *types = gSpeciesInfo[substruct0->species].types; - retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; - } - else - { - retVal = substruct0->teraType; + struct PokemonSubstruct0 *substruct0 = GetSubstruct0(boxMon); + if (gSpeciesInfo[substruct0->species].forceTeraType) + { + retVal = gSpeciesInfo[substruct0->species].forceTeraType; + } + else if (substruct0->teraType == TYPE_NONE) // Tera Type hasn't been modified so we can just use the personality + { + const u8 *types = gSpeciesInfo[substruct0->species].types; + retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; + } + else + { + retVal = substruct0->teraType; + } } break; case MON_DATA_EVOLUTION_TRACKER: - evoTracker.asField.a = substruct1->evolutionTracker1; - evoTracker.asField.b = substruct1->evolutionTracker2; - evoTracker.asField.unused = 0; - retVal = evoTracker.value; + { + struct PokemonSubstruct1 *substruct1 = GetSubstruct1(boxMon); + retVal = (union EvolutionTracker) { + .tracker1 = substruct1->evolutionTracker1, + .tracker2 = substruct1->evolutionTracker2, + }.combinedValue; + } break; default: break; @@ -2898,25 +2901,13 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) { const u8 *data = dataArg; - struct PokemonSubstruct0 *substruct0 = NULL; - struct PokemonSubstruct1 *substruct1 = NULL; - struct PokemonSubstruct2 *substruct2 = NULL; - struct PokemonSubstruct3 *substruct3 = NULL; - if (field > MON_DATA_ENCRYPT_SEPARATOR) { - substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0); - substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1); - substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2); - substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3); - - DecryptBoxMon(boxMon); - - if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum) + if (CalculateBoxMonChecksumDecrypt(boxMon) != boxMon->checksum) { boxMon->isBadEgg = TRUE; boxMon->isEgg = TRUE; - substruct3->isEgg = TRUE; + GetSubstruct3(boxMon)->isEgg = TRUE; EncryptBoxMon(boxMon); return; } @@ -2927,6 +2918,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) case MON_DATA_NICKNAME10: { s32 i; + struct PokemonSubstruct0 *substruct0 = GetSubstruct0(boxMon); for (i = 0; i < min(sizeof(boxMon->nickname), POKEMON_NAME_LENGTH); i++) boxMon->nickname[i] = data[i]; if (field != MON_DATA_NICKNAME10) @@ -2945,6 +2937,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) } case MON_DATA_SPECIES: { + struct PokemonSubstruct0 *substruct0 = GetSubstruct0(boxMon); SET16(substruct0->species); if (substruct0->species) boxMon->hasSpecies = TRUE; @@ -2953,180 +2946,178 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) break; } case MON_DATA_HELD_ITEM: - SET16(substruct0->heldItem); + SET16(GetSubstruct0(boxMon)->heldItem); break; case MON_DATA_EXP: - SET32(substruct0->experience); + SET32(GetSubstruct0(boxMon)->experience); break; case MON_DATA_PP_BONUSES: - SET8(substruct0->ppBonuses); + SET8(GetSubstruct0(boxMon)->ppBonuses); break; case MON_DATA_FRIENDSHIP: - SET8(substruct0->friendship); + SET8(GetSubstruct0(boxMon)->friendship); break; case MON_DATA_MOVE1: - SET16(substruct1->move1); + SET16(GetSubstruct1(boxMon)->move1); break; case MON_DATA_MOVE2: - SET16(substruct1->move2); + SET16(GetSubstruct1(boxMon)->move2); break; case MON_DATA_MOVE3: - SET16(substruct1->move3); + SET16(GetSubstruct1(boxMon)->move3); break; case MON_DATA_MOVE4: - SET16(substruct1->move4); + SET16(GetSubstruct1(boxMon)->move4); break; case MON_DATA_PP1: - SET8(substruct1->pp1); + SET8(GetSubstruct1(boxMon)->pp1); break; case MON_DATA_PP2: - SET8(substruct1->pp2); + SET8(GetSubstruct1(boxMon)->pp2); break; case MON_DATA_PP3: - SET8(substruct1->pp3); + SET8(GetSubstruct1(boxMon)->pp3); break; case MON_DATA_PP4: - SET8(substruct1->pp4); + SET8(GetSubstruct1(boxMon)->pp4); break; case MON_DATA_HP_EV: - SET8(substruct2->hpEV); + SET8(GetSubstruct2(boxMon)->hpEV); break; case MON_DATA_ATK_EV: - SET8(substruct2->attackEV); + SET8(GetSubstruct2(boxMon)->attackEV); break; case MON_DATA_DEF_EV: - SET8(substruct2->defenseEV); + SET8(GetSubstruct2(boxMon)->defenseEV); break; case MON_DATA_SPEED_EV: - SET8(substruct2->speedEV); + SET8(GetSubstruct2(boxMon)->speedEV); break; case MON_DATA_SPATK_EV: - SET8(substruct2->spAttackEV); + SET8(GetSubstruct2(boxMon)->spAttackEV); break; case MON_DATA_SPDEF_EV: - SET8(substruct2->spDefenseEV); + SET8(GetSubstruct2(boxMon)->spDefenseEV); break; case MON_DATA_COOL: - SET8(substruct2->cool); + SET8(GetSubstruct2(boxMon)->cool); break; case MON_DATA_BEAUTY: - SET8(substruct2->beauty); + SET8(GetSubstruct2(boxMon)->beauty); break; case MON_DATA_CUTE: - SET8(substruct2->cute); + SET8(GetSubstruct2(boxMon)->cute); break; case MON_DATA_SMART: - SET8(substruct2->smart); + SET8(GetSubstruct2(boxMon)->smart); break; case MON_DATA_TOUGH: - SET8(substruct2->tough); + SET8(GetSubstruct2(boxMon)->tough); break; case MON_DATA_SHEEN: - SET8(substruct2->sheen); + SET8(GetSubstruct2(boxMon)->sheen); break; case MON_DATA_POKERUS: - SET8(substruct3->pokerus); + SET8(GetSubstruct3(boxMon)->pokerus); break; case MON_DATA_MET_LOCATION: - SET8(substruct3->metLocation); + SET8(GetSubstruct3(boxMon)->metLocation); break; case MON_DATA_MET_LEVEL: - SET8(substruct3->metLevel); + SET8(GetSubstruct3(boxMon)->metLevel); break; case MON_DATA_MET_GAME: - SET8(substruct3->metGame); + SET8(GetSubstruct3(boxMon)->metGame); break; case MON_DATA_POKEBALL: - SET8(substruct0->pokeball); + SET8(GetSubstruct0(boxMon)->pokeball); break; case MON_DATA_OT_GENDER: - SET8(substruct3->otGender); + SET8(GetSubstruct3(boxMon)->otGender); break; case MON_DATA_HP_IV: - SET8(substruct3->hpIV); + SET8(GetSubstruct3(boxMon)->hpIV); break; case MON_DATA_ATK_IV: - SET8(substruct3->attackIV); + SET8(GetSubstruct3(boxMon)->attackIV); break; case MON_DATA_DEF_IV: - SET8(substruct3->defenseIV); + SET8(GetSubstruct3(boxMon)->defenseIV); break; case MON_DATA_SPEED_IV: - SET8(substruct3->speedIV); + SET8(GetSubstruct3(boxMon)->speedIV); break; case MON_DATA_SPATK_IV: - SET8(substruct3->spAttackIV); + SET8(GetSubstruct3(boxMon)->spAttackIV); break; case MON_DATA_SPDEF_IV: - SET8(substruct3->spDefenseIV); + SET8(GetSubstruct3(boxMon)->spDefenseIV); break; case MON_DATA_IS_EGG: - SET8(substruct3->isEgg); - if (substruct3->isEgg) - boxMon->isEgg = TRUE; - else - boxMon->isEgg = FALSE; + SET8(GetSubstruct3(boxMon)->isEgg); + SET8(boxMon->isEgg); break; case MON_DATA_ABILITY_NUM: - SET8(substruct3->abilityNum); + SET8(GetSubstruct3(boxMon)->abilityNum); break; case MON_DATA_COOL_RIBBON: - SET8(substruct3->coolRibbon); + SET8(GetSubstruct3(boxMon)->coolRibbon); break; case MON_DATA_BEAUTY_RIBBON: - SET8(substruct3->beautyRibbon); + SET8(GetSubstruct3(boxMon)->beautyRibbon); break; case MON_DATA_CUTE_RIBBON: - SET8(substruct3->cuteRibbon); + SET8(GetSubstruct3(boxMon)->cuteRibbon); break; case MON_DATA_SMART_RIBBON: - SET8(substruct3->smartRibbon); + SET8(GetSubstruct3(boxMon)->smartRibbon); break; case MON_DATA_TOUGH_RIBBON: - SET8(substruct3->toughRibbon); + SET8(GetSubstruct3(boxMon)->toughRibbon); break; case MON_DATA_CHAMPION_RIBBON: - SET8(substruct3->championRibbon); + SET8(GetSubstruct3(boxMon)->championRibbon); break; case MON_DATA_WINNING_RIBBON: - SET8(substruct3->winningRibbon); + SET8(GetSubstruct3(boxMon)->winningRibbon); break; case MON_DATA_VICTORY_RIBBON: - SET8(substruct3->victoryRibbon); + SET8(GetSubstruct3(boxMon)->victoryRibbon); break; case MON_DATA_ARTIST_RIBBON: - SET8(substruct3->artistRibbon); + SET8(GetSubstruct3(boxMon)->artistRibbon); break; case MON_DATA_EFFORT_RIBBON: - SET8(substruct3->effortRibbon); + SET8(GetSubstruct3(boxMon)->effortRibbon); break; case MON_DATA_MARINE_RIBBON: - SET8(substruct3->marineRibbon); + SET8(GetSubstruct3(boxMon)->marineRibbon); break; case MON_DATA_LAND_RIBBON: - SET8(substruct3->landRibbon); + SET8(GetSubstruct3(boxMon)->landRibbon); break; case MON_DATA_SKY_RIBBON: - SET8(substruct3->skyRibbon); + SET8(GetSubstruct3(boxMon)->skyRibbon); break; case MON_DATA_COUNTRY_RIBBON: - SET8(substruct3->countryRibbon); + SET8(GetSubstruct3(boxMon)->countryRibbon); break; case MON_DATA_NATIONAL_RIBBON: - SET8(substruct3->nationalRibbon); + SET8(GetSubstruct3(boxMon)->nationalRibbon); break; case MON_DATA_EARTH_RIBBON: - SET8(substruct3->earthRibbon); + SET8(GetSubstruct3(boxMon)->earthRibbon); break; case MON_DATA_WORLD_RIBBON: - SET8(substruct3->worldRibbon); + SET8(GetSubstruct3(boxMon)->worldRibbon); break; case MON_DATA_MODERN_FATEFUL_ENCOUNTER: - SET8(substruct3->modernFatefulEncounter); + SET8(GetSubstruct3(boxMon)->modernFatefulEncounter); break; case MON_DATA_IVS: { u32 ivs; + struct PokemonSubstruct3 *substruct3 = GetSubstruct3(boxMon); SET32(ivs); substruct3->hpIV = ivs & MAX_IV_MASK; substruct3->attackIV = (ivs >> 5) & MAX_IV_MASK; @@ -3137,43 +3128,42 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) break; } case MON_DATA_HYPER_TRAINED_HP: - SET8(substruct1->hyperTrainedHP); + SET8(GetSubstruct1(boxMon)->hyperTrainedHP); break; case MON_DATA_HYPER_TRAINED_ATK: - SET8(substruct1->hyperTrainedAttack); + SET8(GetSubstruct1(boxMon)->hyperTrainedAttack); break; case MON_DATA_HYPER_TRAINED_DEF: - SET8(substruct1->hyperTrainedDefense); + SET8(GetSubstruct1(boxMon)->hyperTrainedDefense); break; case MON_DATA_HYPER_TRAINED_SPEED: - SET8(substruct1->hyperTrainedSpeed); + SET8(GetSubstruct1(boxMon)->hyperTrainedSpeed); break; case MON_DATA_HYPER_TRAINED_SPATK: - SET8(substruct1->hyperTrainedSpAttack); + SET8(GetSubstruct1(boxMon)->hyperTrainedSpAttack); break; case MON_DATA_HYPER_TRAINED_SPDEF: - SET8(substruct1->hyperTrainedSpDefense); + SET8(GetSubstruct1(boxMon)->hyperTrainedSpDefense); break; case MON_DATA_IS_SHADOW: - SET8(substruct3->isShadow); + SET8(GetSubstruct3(boxMon)->isShadow); break; case MON_DATA_DYNAMAX_LEVEL: - SET8(substruct3->dynamaxLevel); + SET8(GetSubstruct3(boxMon)->dynamaxLevel); break; case MON_DATA_GIGANTAMAX_FACTOR: - SET8(substruct3->gigantamaxFactor); + SET8(GetSubstruct3(boxMon)->gigantamaxFactor); break; case MON_DATA_TERA_TYPE: - SET8(substruct0->teraType); + SET8(GetSubstruct0(boxMon)->teraType); break; case MON_DATA_EVOLUTION_TRACKER: { union EvolutionTracker evoTracker; - u32 evoTrackerValue; - SET32(evoTrackerValue); - evoTracker.value = evoTrackerValue; - substruct1->evolutionTracker1 = evoTracker.asField.a; - substruct1->evolutionTracker2 = evoTracker.asField.b; + struct PokemonSubstruct1 *substruct1 = GetSubstruct1(boxMon); + SET32(evoTracker.combinedValue); + substruct1->evolutionTracker1 = evoTracker.tracker1; + substruct1->evolutionTracker2 = evoTracker.tracker2; break; } default: @@ -3248,10 +3238,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) } if (field > MON_DATA_ENCRYPT_SEPARATOR) - { - boxMon->checksum = CalculateBoxMonChecksum(boxMon); - EncryptBoxMon(boxMon); - } + boxMon->checksum = CalculateBoxMonChecksumReencrypt(boxMon); } void CopyMon(void *dest, void *src, size_t size) @@ -6939,15 +6926,15 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) u32 teraType = GetBoxMonData(boxMon, MON_DATA_TERA_TYPE, NULL); old = *boxMon; - old0 = &(GetSubstruct(&old, old.personality, 0)->type0); - old1 = &(GetSubstruct(&old, old.personality, 1)->type1); - old2 = &(GetSubstruct(&old, old.personality, 2)->type2); - old3 = &(GetSubstruct(&old, old.personality, 3)->type3); + old0 = &(GetSubstruct(&old, old.personality, SUBSTRUCT_TYPE_0)->type0); + old1 = &(GetSubstruct(&old, old.personality, SUBSTRUCT_TYPE_1)->type1); + old2 = &(GetSubstruct(&old, old.personality, SUBSTRUCT_TYPE_2)->type2); + old3 = &(GetSubstruct(&old, old.personality, SUBSTRUCT_TYPE_3)->type3); - new0 = &(GetSubstruct(boxMon, personality, 0)->type0); - new1 = &(GetSubstruct(boxMon, personality, 1)->type1); - new2 = &(GetSubstruct(boxMon, personality, 2)->type2); - new3 = &(GetSubstruct(boxMon, personality, 3)->type3); + new0 = &(GetSubstruct(boxMon, personality, SUBSTRUCT_TYPE_0)->type0); + new1 = &(GetSubstruct(boxMon, personality, SUBSTRUCT_TYPE_1)->type1); + new2 = &(GetSubstruct(boxMon, personality, SUBSTRUCT_TYPE_2)->type2); + new3 = &(GetSubstruct(boxMon, personality, SUBSTRUCT_TYPE_3)->type3); DecryptBoxMon(&old); boxMon->personality = personality; @@ -6955,8 +6942,7 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) *new1 = *old1; *new2 = *old2; *new3 = *old3; - boxMon->checksum = CalculateBoxMonChecksum(boxMon); - EncryptBoxMon(boxMon); + boxMon->checksum = CalculateBoxMonChecksumReencrypt(boxMon); SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny); SetBoxMonData(boxMon, MON_DATA_HIDDEN_NATURE, &hiddenNature); diff --git a/test/pokemon.c b/test/pokemon.c index 00a4a4485b..c83b42e478 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -450,3 +450,28 @@ TEST("Pokémon level up learnsets fit within MAX_LEVEL_UP_MOVES and MAX_RELEARNE EXPECT_LT(count, MAX_LEVEL_UP_MOVES); EXPECT_LT(count, MAX_RELEARNER_MOVES - 1); // - 1 because at least one move is already known } + +TEST("Optimised GetMonData") +{ + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, 0, FALSE, 0, OT_ID_PRESET, 0x12345678); + u32 exp = 0x123456; + SetMonData(&gPlayerParty[0], MON_DATA_EXP, &exp); + struct Benchmark optimised, + vanilla = (struct Benchmark) { .ticks = 137 }; // From prior testing + u32 expGet = 0; + BENCHMARK(&optimised) { expGet = GetMonData(&gPlayerParty[0], MON_DATA_EXP); } + EXPECT_EQ(exp, expGet); + EXPECT_FASTER(optimised, vanilla); +} + +TEST("Optimised SetMonData") +{ + CreateMon(&gPlayerParty[0], SPECIES_WOBBUFFET, 5, 0, FALSE, 0, OT_ID_PRESET, 0x12345678); + u32 exp = 0x123456; + struct Benchmark optimised, + vanilla = (struct Benchmark) { .ticks = 205 }; // From prior testing + BENCHMARK(&optimised) { SetMonData(&gPlayerParty[0], MON_DATA_EXP, &exp); } + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_BAD_EGG), FALSE); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_EXP), exp); + EXPECT_FASTER(optimised, vanilla); +}