summaryrefslogtreecommitdiff
path: root/arm9/src
diff options
context:
space:
mode:
Diffstat (limited to 'arm9/src')
-rw-r--r--arm9/src/pokemon.c452
1 files changed, 452 insertions, 0 deletions
diff --git a/arm9/src/pokemon.c b/arm9/src/pokemon.c
new file mode 100644
index 00000000..7905046d
--- /dev/null
+++ b/arm9/src/pokemon.c
@@ -0,0 +1,452 @@
+#include "global.h"
+#define IN_POKEMON_C
+#include "pokemon.h"
+#include "heap.h"
+#include "MI_memory.h"
+#include "math_util.h"
+
+#pragma thumb on
+
+void MonEncryptSegment(void * datap, u32 size, u32 key);
+void MonDecryptSegment(void * datap, u32 size, u32 key);
+u16 MonEncryptionLCRNG(u32 * seed);
+u16 CalcMonChecksum(void * datap, u32 size);
+void InitBoxMonMoveset(struct BoxPokemon * boxmon);
+u32 GetMonDataInternal(struct Pokemon * pokemon, u32 attr, void * ptr);
+u32 GetBoxMonDataInternal(struct BoxPokemon * pokemon, u32 attr, void * ptr);
+void LoadMonBaseStats_HandleAlternateForme(u32 species, u32 forme, struct BaseStats * baseStats);
+int ApplyNatureModToStat(u8 nature, u16 statval, u32 statno);
+
+#define ENCRY_ARGS_PTY(mon) &(mon)->party, sizeof((mon)->party), (mon)->box.pid
+#define ENCRY_ARGS_BOX(boxmon) &(boxmon)->substructs, sizeof((boxmon)->substructs), (boxmon)->checksum
+#define ENCRYPT_PTY(mon) MonEncryptSegment(ENCRY_ARGS_PTY(mon))
+#define ENCRYPT_BOX(boxmon) MonEncryptSegment(ENCRY_ARGS_BOX(boxmon))
+#define DECRYPT_PTY(mon) MonDecryptSegment(ENCRY_ARGS_PTY(mon))
+#define DECRYPT_BOX(boxmon) MonDecryptSegment(ENCRY_ARGS_BOX(boxmon))
+#define SHINY_CHECK(otid, pid) (( \
+ ((((otid) & 0xFFFF0000u) >> 16u)) ^ \
+ (((otid) & 0xFFFFu)) ^ \
+ ((((pid) & 0xFFFF0000u) >> 16u))^ \
+ (((pid) & 0xFFFFu))) \
+ < 8u)
+#define CALC_UNOWN_LETTER(pid) ((u32)((((pid) & 0x3000000) >> 18) | (((pid) & 0x30000) >> 12) | (((pid) & 0x300) >> 6) | (((pid) & 0x3) >> 0)) % 28u)
+
+void ZeroMonData(struct Pokemon * pokemon)
+{
+ MIi_CpuClearFast(0, pokemon, sizeof(struct Pokemon));
+ ENCRYPT_BOX(&pokemon->box);
+ ENCRYPT_PTY(pokemon);
+}
+
+void ZeroBoxMonData(struct BoxPokemon * boxmon)
+{
+ MIi_CpuClearFast(0, boxmon, sizeof(struct BoxPokemon));
+ ENCRYPT_BOX(boxmon);
+}
+
+u32 SizeOfStructPokemon(void)
+{
+ return sizeof(struct Pokemon);
+}
+
+struct Pokemon * AllocMonZeroed(u32 heap_id)
+{
+ struct Pokemon * pokemon = (struct Pokemon *)AllocFromHeap(heap_id, sizeof(struct Pokemon));
+ ZeroMonData(pokemon);
+ return pokemon;
+}
+
+BOOL TryDecryptMon(struct Pokemon * mon)
+{
+ BOOL ret = FALSE;
+
+ if (!mon->box.party_lock)
+ {
+ ret = TRUE;
+ GF_ASSERT(!mon->box.box_lock);
+ mon->box.party_lock = TRUE;
+ mon->box.box_lock = TRUE;
+ DECRYPT_PTY(mon);
+ DECRYPT_BOX(&mon->box);
+ }
+ return ret;
+}
+
+BOOL TryEncryptMon(struct Pokemon * mon, BOOL decrypt_result)
+{
+ BOOL ret = FALSE;
+ if (mon->box.party_lock == TRUE && decrypt_result == TRUE)
+ {
+ ret = TRUE;
+ mon->box.party_lock = FALSE;
+ mon->box.box_lock = FALSE;
+ ENCRYPT_PTY(mon);
+ mon->box.checksum = CalcMonChecksum(&mon->box.substructs, sizeof(mon->box.substructs));
+ ENCRYPT_BOX(&mon->box);
+ }
+ return ret;
+}
+
+BOOL TryDecryptBoxMon(struct BoxPokemon * mon)
+{
+ BOOL ret = FALSE;
+
+ if (!mon->box_lock)
+ {
+ ret = TRUE;
+ mon->box_lock = TRUE;
+ DECRYPT_BOX(mon);
+ }
+ return ret;
+}
+
+BOOL TryEncryptBoxMon(struct BoxPokemon * mon, BOOL decrypt_result)
+{
+ BOOL ret = FALSE;
+ if (mon->box_lock == TRUE && decrypt_result == TRUE)
+ {
+ ret = TRUE;
+ mon->box_lock = FALSE;
+ mon->checksum = CalcMonChecksum(&mon->substructs, sizeof(mon->substructs));
+ ENCRYPT_BOX(mon);
+ }
+ return ret;
+}
+
+void CreateMon(struct Pokemon * pokemon, int species, int level, int fixedIV, int hasFixedPersonality, int fixedPersonality, int otIdType, int fixedOtId)
+{
+ struct SealStruct * seal;
+ u32 capsule;
+ u8 seal_coords[0x18];
+ ZeroMonData(pokemon);
+ CreateBoxMon(&pokemon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId);
+ // Not your average encryption call
+ MonEncryptSegment(&pokemon->party, sizeof(pokemon->party), 0);
+ ENCRYPT_PTY(pokemon);
+ SetMonData(pokemon, MON_DATA_LEVEL, &level);
+ seal = CreateNewSealsObject(0);
+ SetMonData(pokemon, MON_DATA_SEAL_STRUCT, seal);
+ FreeToHeap(seal);
+ capsule = 0;
+ SetMonData(pokemon, MON_DATA_CAPSULE, &capsule);
+ MIi_CpuClearFast(0, seal_coords, sizeof(seal_coords));
+ SetMonData(pokemon, MON_DATA_SEAL_COORDS, seal_coords);
+ CalcMonLevelAndStats(pokemon);
+}
+
+void CreateBoxMon(struct BoxPokemon * boxPokemon, int species, int level, int fixedIV, int hasFixedPersonality, int fixedPersonality, int otIdType, int fixedOtId)
+{
+ BOOL decry;
+ u32 exp;
+ u32 iv;
+ ZeroBoxMonData(boxPokemon);
+ decry = TryDecryptBoxMon(boxPokemon);
+ if (hasFixedPersonality == 0)
+ {
+ fixedPersonality = (rand_LC() | (rand_LC() << 16));
+ }
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_PERSONALITY, &fixedPersonality);
+ if (otIdType == 2)
+ {
+ do
+ {
+ fixedOtId = (rand_LC() | (rand_LC() << 16));
+ } while (SHINY_CHECK(fixedOtId, fixedPersonality));
+ }
+ else if (otIdType != 1)
+ fixedOtId = 0;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_OTID, &fixedOtId);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_GAME_LANGUAGE, &gGameLanguage);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPECIES, &species);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPECIES_NAME, NULL);
+ exp = GetMonExpBySpeciesAndLevel(species, level);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_EXPERIENCE, &exp);
+ exp = GetMonBaseStat(species, BASE_FRIENDSHIP);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_FRIENDSHIP, &exp);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_MET_LEVEL, &level);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_GAME_VERSION, &gGameVersion);
+ exp = 4;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_POKEBALL, &exp);
+ if (fixedIV < 0x20)
+ {
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_HP_IV, &fixedIV);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_ATK_IV, &fixedIV);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_DEF_IV, &fixedIV);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPEED_IV, &fixedIV);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPATK_IV, &fixedIV);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPDEF_IV, &fixedIV);
+ }
+ else
+ {
+ exp = rand_LC();
+ iv = exp & 0x1F;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_HP_IV, &iv);
+ iv = (exp & 0x3E0) >> 5;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_ATK_IV, &iv);
+ iv = (exp & 0x7C00) >> 10;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_DEF_IV, &iv);
+ exp = rand_LC();
+ iv = exp & 0x1F;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPEED_IV, &iv);
+ iv = (exp & 0x3E0) >> 5;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPATK_IV, &iv);
+ iv = (exp & 0x7C00) >> 10;
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_SPDEF_IV, &iv);
+ }
+ exp = GetMonBaseStat(species, BASE_ABILITY_1);
+ iv = GetMonBaseStat(species, BASE_ABILITY_2);
+ if (iv != 0)
+ {
+ if (fixedPersonality & 1)
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_ABILITY, &iv);
+ else
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_ABILITY, &exp);
+ }
+ else
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_ABILITY, &exp);
+ exp = GetBoxMonGenderEncrypted(boxPokemon);
+ SetBoxMonDataEncrypted(boxPokemon, MON_DATA_GENDER, &exp);
+ InitBoxMonMoveset(boxPokemon);
+ TryEncryptBoxMon(boxPokemon, decry);
+}
+
+void CreateMonWithNature(struct Pokemon * pokemon, u16 species, u8 level, u8 fixedIv, u8 nature)
+{
+ u32 personality;
+ do
+ {
+ personality = (u32)(rand_LC() | (rand_LC() << 16));
+ } while (nature != GetNatureFromPersonality(personality));
+ CreateMon(pokemon, (int)species, (int)level, (int)fixedIv, 1, (int)personality, (int)0, (int)0);
+}
+
+// FIXME: stack storage of pokemon, fixedIv swapped
+void CreateMonWithGenderNatureLetter(struct Pokemon * pokemon, u16 species, u8 level, u8 fixedIv, u8 gender, u8 nature, u8 letter)
+{
+ u32 pid = 0;
+ u16 test = 0;
+ if (letter != 0 && letter < 29)
+ {
+ do {
+ pid = (u32)(rand_LC() | (rand_LC() << 16));
+ test = (u16)CALC_UNOWN_LETTER(pid);
+ } while (nature != GetNatureFromPersonality(pid) || gender != GetGenderBySpeciesAndPersonality(species, pid) || test != letter - 1);
+ }
+ else
+ {
+ pid = GenPersonalityByGenderAndNature(species, gender, nature);
+ }
+ CreateMon(pokemon, (int)species, (int)level, (int)fixedIv, 1, (int)pid, 0, 0);
+}
+
+u32 GenPersonalityByGenderAndNature(u16 species, u8 gender, u8 nature)
+{
+ int pid = nature;
+ u8 ratio = (u8)GetMonBaseStat(species, BASE_GENDER_RATIO);
+ switch (ratio)
+ {
+ case MON_RATIO_MALE:
+ case MON_RATIO_FEMALE:
+ case MON_RATIO_UNKNOWN:
+ break;
+ default:
+ if (gender == MON_MALE)
+ {
+ // Smallest increment that forces the low byte to exceed the
+ // gender ratio, thus making the mon male
+ pid = 25 * ((ratio / 25) + 1);
+ pid += nature;
+ }
+ break;
+ }
+ return (u32)pid;
+}
+
+void CreateMonWithFixedIVs(struct Pokemon * pokemon, int species, int level, int ivs, int personality)
+{
+ CreateMon(pokemon, species, level, 0, 1, personality, 0, 0);
+ SetMonData(pokemon, MON_DATA_IVS_WORD, &ivs);
+ CalcMonLevelAndStats(pokemon);
+}
+
+void CalcMonLevelAndStats(struct Pokemon * pokemon)
+{
+ BOOL decry = TryDecryptMon(pokemon);
+ u32 level = CalcMonLevelEncrypted(pokemon);
+ SetMonData(pokemon, MON_DATA_LEVEL, &level);
+ CalcMonStats(pokemon);
+ TryEncryptMon(pokemon, decry);
+}
+
+void CalcMonStats(struct Pokemon * pokemon)
+{
+ struct BaseStats * baseStats;
+ int level;
+ int maxHp;
+ int hpIv;
+ int hpEv;
+ int atkIv;
+ int defIv;
+ int speedIv;
+ int spatkIv;
+ int spdefIv;
+ int atkEv;
+ int defEv;
+ int speedEv;
+ int spatkEv;
+ int spdefEv;
+ int forme;
+ int hp;
+ int species;
+ int newMaxHp;
+ int newAtk;
+ int newDef;
+ int newSpeed;
+ int newSpatk;
+ int newSpdef;
+ BOOL decry = TryDecryptMon(pokemon);
+ level = (int)GetMonData(pokemon, MON_DATA_LEVEL, NULL);
+ maxHp = (int)GetMonData(pokemon, MON_DATA_MAXHP, NULL);
+ hp = (int)GetMonData(pokemon, MON_DATA_HP, NULL);
+ hpIv = (int)GetMonData(pokemon, MON_DATA_HP_IV, NULL);
+ hpEv = (int)GetMonData(pokemon, MON_DATA_HP_EV, NULL);
+ atkIv = (int)GetMonData(pokemon, MON_DATA_ATK_IV, NULL);
+ atkEv = (int)GetMonData(pokemon, MON_DATA_ATK_EV, NULL);
+ defIv = (int)GetMonData(pokemon, MON_DATA_DEF_IV, NULL);
+ defEv = (int)GetMonData(pokemon, MON_DATA_DEF_EV, NULL);
+ speedIv = (int)GetMonData(pokemon, MON_DATA_SPEED_IV, NULL);
+ speedEv = (int)GetMonData(pokemon, MON_DATA_SPEED_EV, NULL);
+ spatkIv = (int)GetMonData(pokemon, MON_DATA_SPATK_IV, NULL);
+ spatkEv = (int)GetMonData(pokemon, MON_DATA_SPATK_EV, NULL);
+ spdefIv = (int)GetMonData(pokemon, MON_DATA_SPDEF_IV, NULL);
+ spdefEv = (int)GetMonData(pokemon, MON_DATA_SPDEF_EV, NULL);
+ forme = (int)GetMonData(pokemon, MON_DATA_FORME, NULL);
+ species = (int)GetMonData(pokemon, MON_DATA_SPECIES, NULL);
+
+ baseStats = (struct BaseStats *)AllocFromHeap(0, sizeof(struct BaseStats));
+ LoadMonBaseStats_HandleAlternateForme(species, forme, baseStats);
+
+ if (species == SPECIES_SHEDINJA)
+ newMaxHp = 1;
+ else
+ {
+ newMaxHp = (baseStats->hp * 2 + hpIv + hpEv / 4) * level / 100 + level + 10;
+ }
+ SetMonData(pokemon, MON_DATA_MAXHP, &newMaxHp);
+
+ newAtk = (baseStats->atk * 2 + atkIv + atkEv / 4) * level / 100 + 5;
+ newAtk = ApplyNatureModToStat(GetMonNature(pokemon), newAtk, 1);
+ SetMonData(pokemon, MON_DATA_ATK, &newAtk);
+
+ newDef = (baseStats->def * 2 + defIv + defEv / 4) * level / 100 + 5;
+ newDef = ApplyNatureModToStat(GetMonNature(pokemon), newDef, 2);
+ SetMonData(pokemon, MON_DATA_DEF, &newDef);
+
+ newSpeed = (baseStats->speed * 2 + speedIv + speedEv / 4) * level / 100 + 5;
+ newSpeed = ApplyNatureModToStat(GetMonNature(pokemon), newSpeed, 3);
+ SetMonData(pokemon, MON_DATA_SPEED, &newSpeed);
+
+ newSpatk = (baseStats->spatk * 2 + spatkIv + spatkEv / 4) * level / 100 + 5;
+ newSpatk = ApplyNatureModToStat(GetMonNature(pokemon), newSpatk, 4);
+ SetMonData(pokemon, MON_DATA_SPATK, &newSpatk);
+
+ newSpdef = (baseStats->spdef * 2 + spdefIv + spdefEv / 4) * level / 100 + 5;
+ newSpdef = ApplyNatureModToStat(GetMonNature(pokemon), newSpdef, 5);
+ SetMonData(pokemon, MON_DATA_SPDEF, &newSpdef);
+
+ FreeToHeap(baseStats);
+
+ if (hp != 0 || maxHp == 0)
+ {
+ if (species == SPECIES_SHEDINJA)
+ hp = 1;
+ else if (hp == 0)
+ hp = newMaxHp;
+ else
+ hp += newMaxHp - maxHp;
+ }
+ if (hp != 0)
+ SetMonData(pokemon, MON_DATA_HP, &hp);
+ TryEncryptMon(pokemon, decry);
+}
+
+u32 GetMonData(struct Pokemon * pokemon, u32 attr, void * dest)
+{
+ u32 ret;
+ u32 checksum;
+ if (!pokemon->box.party_lock)
+ {
+ DECRYPT_PTY(pokemon);
+ DECRYPT_BOX(&pokemon->box);
+ checksum = CalcMonChecksum(&pokemon->box.substructs, sizeof(pokemon->box.substructs));
+ if (checksum != pokemon->box.checksum)
+ {
+ GF_ASSERT(checksum == pokemon->box.checksum);
+ pokemon->box.checksum_fail = TRUE;
+ }
+ }
+ ret = GetMonDataInternal(pokemon, attr, dest);
+ if (!pokemon->box.party_lock)
+ {
+ ENCRYPT_PTY(pokemon);
+ ENCRYPT_BOX(&pokemon->box);
+ }
+ return ret;
+}
+
+u32 GetMonDataInternal(struct Pokemon * pokemon, u32 attr, void * dest)
+{
+ switch (attr)
+ {
+ case MON_DATA_STATUS:
+ return pokemon->party.status;
+ case MON_DATA_LEVEL:
+ return pokemon->party.level;
+ case MON_DATA_CAPSULE:
+ return pokemon->party.capsule;
+ case MON_DATA_HP:
+ return pokemon->party.hp;
+ case MON_DATA_MAXHP:
+ return pokemon->party.maxHp;
+ case MON_DATA_ATK:
+ return pokemon->party.atk;
+ case MON_DATA_DEF:
+ return pokemon->party.def;
+ case MON_DATA_SPEED:
+ return pokemon->party.speed;
+ case MON_DATA_SPATK:
+ return pokemon->party.spatk;
+ case MON_DATA_SPDEF:
+ return pokemon->party.spdef;
+ case MON_DATA_SEAL_STRUCT:
+ CopySealsObject(&pokemon->party.seal_something, dest);
+ return 1;
+ case MON_DATA_SEAL_COORDS:
+ FUN_02029C74(pokemon->party.sealCoords, dest);
+ return 1;
+ default:
+ return GetBoxMonDataInternal(&pokemon->box, attr, dest);
+ }
+}
+
+u32 GetBoxMonData(struct BoxPokemon * boxmon, u32 attr, void * dest)
+{
+ u32 ret;
+ u32 checksum;
+ if (!boxmon->box_lock)
+ {
+ DECRYPT_BOX(boxmon);
+ checksum = CalcMonChecksum(&boxmon->substructs, sizeof(boxmon->substructs));
+ if (checksum != boxmon->checksum)
+ {
+ GF_ASSERT(checksum == boxmon->checksum);
+ boxmon->checksum_fail = TRUE;
+ }
+ }
+ ret = GetBoxMonDataInternal(boxmon, attr, dest);
+ if (!boxmon->box_lock)
+ {
+ ENCRYPT_BOX(boxmon);
+ }
+ return ret;
+}