summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/battle_ai.c2838
-rw-r--r--src/dma3_manager.c528
-rw-r--r--src/main.c418
-rw-r--r--src/multiboot.c470
-rw-r--r--src/rng.c35
-rw-r--r--src/siirtc.c432
-rw-r--r--src/string_util.c780
-rw-r--r--src/task.c111
8 files changed, 5511 insertions, 101 deletions
diff --git a/src/battle_ai.c b/src/battle_ai.c
new file mode 100644
index 000000000..8f3b7cf1e
--- /dev/null
+++ b/src/battle_ai.c
@@ -0,0 +1,2838 @@
+#include "global.h"
+#include "pokemon.h"
+#include "battle.h"
+#include "species.h"
+#include "abilities.h"
+
+#define AIScriptRead32(ptr) ((ptr)[0] | (ptr)[1] << 8 | (ptr)[2] << 16 | (ptr)[3] << 24)
+#define AIScriptRead16(ptr) ((ptr)[0] | (ptr)[1] << 8)
+#define AIScriptRead8(ptr) ((ptr)[0])
+#define AIScriptReadPtr(ptr) (u8*) AIScriptRead32(ptr)
+
+#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gUnknown_020244A8->ai))
+#define UNK_2016A00_STRUCT ((struct UnknownStruct2 *)(gUnknown_020244A8->unk18))
+#define UNK_2016C00_STRUCT ((struct UnknownStruct4 *)(gUnknown_020244A8->unk1C))
+#define UNK_BATTLE_STRUCT ((struct UnknownStruct1 *)(gUnknown_020244A8))
+
+#define AI_ACTION_UNK1 0x0001
+#define AI_ACTION_UNK2 0x0002
+#define AI_ACTION_UNK3 0x0004
+#define AI_ACTION_UNK4 0x0008
+#define AI_ACTION_UNK5 0x0010
+#define AI_ACTION_UNK6 0x0020
+#define AI_ACTION_UNK7 0x0040
+#define AI_ACTION_UNK8 0x0080
+
+enum
+{
+ TARGET,
+ USER
+};
+
+// AI states
+enum
+{
+ AIState_SettingUp,
+ AIState_Processing,
+ AIState_FinishedProcessing,
+ AIState_DoNotProcess
+};
+
+//Copied from pokeruby -- hopefully the same
+struct Trainer
+{
+ /*0x00*/ u8 partyFlags;
+ /*0x01*/ u8 trainerClass;
+ /*0x02*/ u8 encounterMusic:7;
+ /*0x02*/ u8 gender:1;
+ /*0x03*/ u8 trainerPic;
+ /*0x04*/ u8 trainerName[12];
+ /*0x10*/ u16 items[4];
+ /*0x18*/ bool8 doubleBattle;
+ /*0x1C*/ u32 aiFlags;
+ /*0x20*/ u8 partySize;
+ /*0x24*/ void *party;
+};
+
+#define POKEMON_NAME_LENGTH 10
+
+struct BattlePokemon
+{
+ /* 0x00 */ u16 species;
+ /* 0x02 */ u16 attack;
+ /* 0x04 */ u16 defense;
+ /* 0x06 */ u16 speed;
+ /* 0x08 */ u16 spAttack;
+ /* 0x0A */ u16 spDefense;
+ /* 0x0C */ u16 moves[4];
+ /* 0x14 */ u32 hpIV:5;
+ /* 0x14 */ u32 attackIV:5;
+ /* 0x15 */ u32 defenseIV:5;
+ /* 0x15 */ u32 speedIV:5;
+ /* 0x16 */ u32 spAttackIV:5;
+ /* 0x17 */ u32 spDefenseIV:5;
+ /* 0x17 */ u32 isEgg:1;
+ /* 0x17 */ u32 altAbility:1;
+ /* 0x18 */ s8 statStages[8];
+ /* 0x20 */ u8 ability;
+ /* 0x21 */ u8 type1;
+ /* 0x22 */ u8 type2;
+ /* 0x23 */ u8 unknown;
+ /* 0x24 */ u8 pp[4];
+ /* 0x28 */ u16 hp;
+ /* 0x2A */ u8 level;
+ /* 0x2B */ u8 friendship;
+ /* 0x2C */ u16 maxHP;
+ /* 0x2E */ u16 item;
+ /* 0x30 */ u8 nickname[POKEMON_NAME_LENGTH + 1];
+ /* 0x3B */ u8 ppBonuses;
+ /* 0x3C */ u8 otName[8];
+ /* 0x44 */ u32 experience;
+ /* 0x48 */ u32 personality;
+ /* 0x4C */ u32 status1;
+ /* 0x50 */ u32 status2;
+ /* 0x54 */ u32 otId;
+};
+
+//size should be 0x1C
+struct AI_ThinkingStruct
+{
+ u8 aiState;
+ u8 movesetIndex;
+ u16 moveConsidered;
+ s8 score[4];
+ u32 funcResult;
+ u32 aiFlags;
+ u8 aiAction;
+ u8 aiLogicId;
+ u8 filler12[6];
+ u8 unk18[4];
+};
+
+//size should be 0x54
+struct UnknownStruct2
+{
+ u16 unk0[2][8];
+ u8 unk20[2];
+ u8 filler20[0x1E];
+ u8 unk40[4];
+ u8 unk44[4];
+ u16 unk48[4];
+ u8 unk50;
+};
+
+struct UnknownStruct4
+{
+ u8 *ptr[8];
+ u8 unk20;
+};
+
+struct SimpleUnknownStruct
+{
+ u32 unkArray[4]; // unknown size
+};
+
+struct UnknownStruct1
+{
+ u8 unk0;
+ u8 filler1[0x3];
+ struct SimpleUnknownStruct *unk4;
+ u8 filler8[0xC];
+ struct AI_ThinkingStruct *ai;
+ struct UnknownStruct2 *unk18;
+ struct UnknownStruct4 *unk1C;
+};
+
+struct UnknownStruct5
+{
+ u8 filler0[0x3];
+ u16 unk4;
+ u16 unk6;
+ u8 unk8;
+ u8 unk9;
+ u8 fillerA[0x9];
+ u8 taunt:4;
+ u8 unkC:4;
+ u8 fillerD[0x2];
+ u8 unk16;
+ u8 filler17[0x4];
+};
+
+extern struct UnknownStruct5 gUnknown_020242BC[];
+
+/*
+gAIScriptPtr is a pointer to the next battle AI cmd command to read.
+when a command finishes processing, gAIScriptPtr is incremented by
+the number of bytes that the current command had reserved for arguments
+in order to read the next command correctly. refer to battle_ai_scripts.s for the
+AI scripts.
+*/
+extern u8 *gAIScriptPtr;
+
+extern u32 gBattleTypeFlags;
+extern u8 gUnknown_02024064;
+extern struct BattlePokemon gBattleMons[];
+extern u16 gUnknown_020241EA;
+extern u8 gEnemyMonIndex;
+extern u8 gUnknown_02024210;
+extern u16 gUnknown_02024248[];
+extern u8 *gUnknown_0202449C;
+extern struct UnknownStruct1 *gUnknown_020244A8;
+extern u16 gUnknown_02038BCA;
+extern u16 gUnknown_02038BCC;
+extern u8 gPlayerMonIndex;
+extern struct Trainer gTrainers[];
+extern const u32 gBitTable[];
+extern u8 *gUnknown_082DBEF8[];
+extern u32 gUnknown_020242AC[];
+extern u16 gUnknown_0202428E[];
+extern struct BattleMove gBattleMoves[];
+extern u8 gUnknown_03005D10[];
+extern u8 gUnknown_0202406E[][2];
+extern struct BaseStats gBaseStats[];
+extern u16 gUnknown_02024400;
+extern u8 gUnknown_02024474[];
+extern u8 gBattleMoveFlags;
+extern int gBattleMoveDamage;
+extern u8 gCritMultiplier;
+extern u16 gBattleWeather;
+
+extern u8 battle_get_per_side_status(u8);
+extern u8 b_first_side(u8, u8, u8);
+extern u8 battle_get_side_with_given_state(u8);
+extern void move_effectiveness_something(u16, u8, u8);
+extern u8 itemid_get_x12();
+extern void b_mc_stack_push(u8 *);
+extern bool8 b_mc_stack_pop_cursor(void);
+extern void sub_8046E7C(u8, u8);
+
+typedef void (*BattleAICmdFunc)(void);
+
+extern const BattleAICmdFunc gUnknown_085B083C[];
+
+extern u8 sub_803FECC();
+extern u16 Random();
+extern u8 battle_side_get_owner();
+extern u32 sub_8186438();
+extern u32 sub_81A6FB4();
+
+void BattleAI_SetupAIData(u8 a);
+u8 BattleAI_GetAIActionToUse(void);
+u8 sub_8130CF4(void);
+void sub_8131074(void);
+void BattleAI_DoAIProcessing(void);
+
+void BattleAI_HandleItemUseBeforeAISetup(u8 a)
+{
+ s32 i;
+ u8 *data = (u8 *)gUnknown_020244A8->unk18;
+
+ for (i = 0; (u32)i < 0x54; i++)
+ data[i] = 0;
+ if ((gBattleTypeFlags & 0x0A7F098A) == 8)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ if (gTrainers[gUnknown_02038BCA].items[i] != 0)
+ {
+ gUnknown_020244A8->unk18->unk48[gUnknown_020244A8->unk18->unk50] = gTrainers[gUnknown_02038BCA].items[i];
+ gUnknown_020244A8->unk18->unk50++;
+ }
+ }
+ }
+
+ BattleAI_SetupAIData(a);
+}
+
+void BattleAI_SetupAIData(u8 a)
+{
+ s32 i;
+ u8 *data = (u8 *)AI_THINKING_STRUCT;
+ u8 r6;
+
+ // clear AI data.
+ for (i = 0; (u32)i < sizeof(struct AI_ThinkingStruct); i++)
+ data[i] = 0;
+
+ // conditional score reset, unlike Ruby.
+ for (i = 0; i < 4; i++)
+ {
+ if (a & 1)
+ AI_THINKING_STRUCT->score[i] = 100;
+ else
+ AI_THINKING_STRUCT->score[i] = 0;
+ a >>= 1;
+ }
+
+ r6 = sub_803FECC(gUnknown_02024064, 0, 0xFF);
+
+ for (i = 0; i < 4; i++)
+ {
+ if (gBitTable[i] & r6)
+ AI_THINKING_STRUCT->score[i] = 0;
+
+ AI_THINKING_STRUCT->unk18[i] = 100 - (Random() % 16);
+ }
+ gUnknown_020244A8->unk1C->unk20 = 0;
+ gPlayerMonIndex = gUnknown_02024064;
+ if (gBattleTypeFlags & 1)
+ {
+ gEnemyMonIndex = (Random() & 2) + ((u32)battle_side_get_owner(gUnknown_02024064) ^ 1);
+ if (gUnknown_02024210 & gBitTable[gEnemyMonIndex])
+ gEnemyMonIndex ^= 2;
+ }
+ else
+ {
+ //_08130A60
+ gEnemyMonIndex = gPlayerMonIndex ^ 1;
+ }
+ //_08130A68
+ if (gBattleTypeFlags & 0x1000000)
+ AI_THINKING_STRUCT->aiFlags = sub_8186438();
+ else if (gBattleTypeFlags & 0x80)
+ AI_THINKING_STRUCT->aiFlags = 0x40000000;
+ else if (gBattleTypeFlags & 0x400)
+ AI_THINKING_STRUCT->aiFlags = 0x20000000;
+ else if (gBattleTypeFlags & 0x10)
+ AI_THINKING_STRUCT->aiFlags = 0x80000000;
+ else if (gBattleTypeFlags & 0x80000)
+ AI_THINKING_STRUCT->aiFlags = sub_81A6FB4();
+ else if (gBattleTypeFlags & 0x0C3F0900)
+ AI_THINKING_STRUCT->aiFlags = 7;
+ else if (gBattleTypeFlags & 0x8000)
+ AI_THINKING_STRUCT->aiFlags = gTrainers[gUnknown_02038BCA].aiFlags | gTrainers[gUnknown_02038BCC].aiFlags;
+ else
+ AI_THINKING_STRUCT->aiFlags = gTrainers[gUnknown_02038BCA].aiFlags;
+ if (gBattleTypeFlags & 1)
+ AI_THINKING_STRUCT->aiFlags |= 0x80;
+}
+
+u8 sub_8130BA4(void)
+{
+ u16 r4 = gUnknown_020241EA;
+ u8 ret;
+
+ if (!(gBattleTypeFlags & 1))
+ ret = BattleAI_GetAIActionToUse();
+ else
+ ret = sub_8130CF4();
+
+ gUnknown_020241EA = r4;
+ return ret;
+}
+
+u8 BattleAI_GetAIActionToUse(void)
+{
+ u8 currentMoveArray[4];
+ u8 consideredMoveArray[4];
+ u8 numOfBestMoves;
+ s32 i;
+
+ sub_8131074();
+
+ while (AI_THINKING_STRUCT->aiFlags != 0)
+ {
+ if (AI_THINKING_STRUCT->aiFlags & 1)
+ {
+ AI_THINKING_STRUCT->aiState = AIState_SettingUp;
+ BattleAI_DoAIProcessing();
+ }
+ AI_THINKING_STRUCT->aiFlags >>= 1;
+ AI_THINKING_STRUCT->aiLogicId++;
+ AI_THINKING_STRUCT->movesetIndex = 0;
+ }
+
+ // special flags for safari watch/flee.
+ if (AI_THINKING_STRUCT->aiAction & 2)
+ return 4;
+ if (AI_THINKING_STRUCT->aiAction & 4)
+ return 5;
+
+ numOfBestMoves = 1;
+ currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
+ consideredMoveArray[0] = 0;
+
+ for (i = 1; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] != 0) // emerald adds an extra move ID check for some reason.
+ {
+ // in ruby, the order of these if statements are reversed.
+ if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i])
+ {
+ currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->score[i];
+ consideredMoveArray[numOfBestMoves++] = i;
+ }
+ if (currentMoveArray[0] < AI_THINKING_STRUCT->score[i])
+ {
+ numOfBestMoves = 1;
+ currentMoveArray[0] = AI_THINKING_STRUCT->score[i];
+ consideredMoveArray[0] = i;
+ }
+ }
+ }
+ return consideredMoveArray[Random() % numOfBestMoves];
+}
+
+#ifdef NONMATCHING
+u8 sub_8130CF4(void)
+{
+ s32 i;
+ s32 j;
+ //s32 r4_2;
+ #define r4_2 r4
+ s32 r5;
+ s16 r5_2;
+ s32 r4;
+ s16 sp0[4];
+ s8 sp8[4];
+ s8 spC[4];
+ u8 sp10[4]; // definitely unsigned
+ u8 sp14[4];
+ //u8 *sp1C = spC;
+ //u8 *sp18 = sp8;
+ //u8 *sp20 = spC;
+
+ for (i = 0; i < 4; i++) //_08130D14
+ {
+ if (i == gPlayerMonIndex || gBattleMons[i].hp == 0)
+ {
+ //_08130D2E
+ spC[i] = -1;
+ sp0[i] = -1;
+ }
+ //_08130D48
+ else
+ {
+ if (gBattleTypeFlags & 0x20000)
+ BattleAI_SetupAIData(gUnknown_0202449C[0x92] >> 4);
+ else
+ BattleAI_SetupAIData(0xF);
+ //_08130D76
+ gEnemyMonIndex = i;
+ if ((i & 1) != (gPlayerMonIndex & 1))
+ sub_8131074();
+ //_08130D90
+ AI_THINKING_STRUCT->unk11 = 0;
+ AI_THINKING_STRUCT->unk1 = 0;
+ r4 = AI_THINKING_STRUCT->aiFlags;
+ while (r4 != 0)
+ {
+ if (r4 & 1)
+ {
+ AI_THINKING_STRUCT->aiState = AIState_SettingUp;
+ BattleAI_DoAIProcessing();
+ }
+ r4 >>= 1;
+ AI_THINKING_STRUCT->unk11++;
+ AI_THINKING_STRUCT->unk1 = 0;
+ }
+ //_08130DD8
+ if (AI_THINKING_STRUCT->unk10 & 2)
+ spC[i] = 4;
+ else if (AI_THINKING_STRUCT->unk10 & 4)
+ spC[i] = 5;
+ else
+ {
+ //_08130E10
+ sp10[0] = AI_THINKING_STRUCT->score[0];
+ sp14[0] = 0;
+ r5 = 1;
+ for (j = 1; j < 4; j++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[j] != 0)
+ {
+ if (sp10[0] == AI_THINKING_STRUCT->score[j])
+ {
+ sp10[r5] = AI_THINKING_STRUCT->score[j];
+ sp14[r5] = j;
+ r5++;
+ }
+ if (sp10[0] < AI_THINKING_STRUCT->score[j])
+ {
+ sp10[0] = AI_THINKING_STRUCT->score[j];
+ sp14[0] = j;
+ r5 = 1;
+ }
+ }
+ //_08130E72
+ }
+ spC[i] = sp14[Random() % r5];
+ //asm("":::"r3");
+ sp0[i] = sp10[0];
+ if (i == (gPlayerMonIndex ^ 2) && sp0[i] < 100)
+ sp0[i] = -1;
+ }
+ }
+ //_08130EAE
+ }
+
+ //#define i r5
+
+ //_08130EC4
+ r5_2 = sp0[0];
+ sp8[0] = 0;
+ r4_2 = 1;
+ for (i = 1; i < 4; i++)
+ {
+ //_08130EDA
+ if (r5_2 == sp0[i])
+ {
+ sp8[r4_2] = i;
+ r4_2++;
+ }
+ //_08130EEE
+ if (r5_2 < sp0[i])
+ {
+ r5_2 = sp0[i];
+ sp8[0] = i;
+ r4_2 = 1;
+ }
+ }
+ gEnemyMonIndex = sp8[Random() % r4_2];
+ return spC[gEnemyMonIndex];
+}
+#else
+__attribute__((naked))
+u8 sub_8130CF4(void)
+{
+ asm(".syntax unified\n\
+ push {r4-r7,lr}\n\
+ mov r7, r10\n\
+ mov r6, r9\n\
+ mov r5, r8\n\
+ push {r5-r7}\n\
+ sub sp, 0x24\n\
+ movs r0, 0\n\
+ mov r8, r0\n\
+ mov r1, sp\n\
+ adds r1, 0xC\n\
+ str r1, [sp, 0x1C]\n\
+ mov r2, sp\n\
+ adds r2, 0x8\n\
+ str r2, [sp, 0x18]\n\
+ str r1, [sp, 0x20]\n\
+ mov r10, sp\n\
+_08130D14:\n\
+ ldr r0, =gPlayerMonIndex\n\
+ ldrb r0, [r0]\n\
+ cmp r8, r0\n\
+ beq _08130D2E\n\
+ movs r0, 0x58\n\
+ mov r7, r8\n\
+ muls r7, r0\n\
+ adds r0, r7, 0\n\
+ ldr r1, =gBattleMons\n\
+ adds r0, r1\n\
+ ldrh r0, [r0, 0x28]\n\
+ cmp r0, 0\n\
+ bne _08130D48\n\
+_08130D2E:\n\
+ movs r0, 0xFF\n\
+ ldr r2, [sp, 0x20]\n\
+ strb r0, [r2]\n\
+ ldr r0, =0x0000ffff\n\
+ mov r7, r10\n\
+ strh r0, [r7]\n\
+ b _08130EAE\n\
+ .pool\n\
+_08130D48:\n\
+ ldr r0, =gBattleTypeFlags\n\
+ ldr r0, [r0]\n\
+ movs r1, 0x80\n\
+ lsls r1, 10\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08130D70\n\
+ ldr r0, =gUnknown_0202449C\n\
+ ldr r0, [r0]\n\
+ adds r0, 0x92\n\
+ ldrb r0, [r0]\n\
+ lsrs r0, 4\n\
+ bl BattleAI_SetupAIData\n\
+ b _08130D76\n\
+ .pool\n\
+_08130D70:\n\
+ movs r0, 0xF\n\
+ bl BattleAI_SetupAIData\n\
+_08130D76:\n\
+ ldr r0, =gEnemyMonIndex\n\
+ mov r1, r8\n\
+ strb r1, [r0]\n\
+ movs r1, 0x1\n\
+ mov r2, r8\n\
+ ands r2, r1\n\
+ ldr r0, =gPlayerMonIndex\n\
+ ldrb r0, [r0]\n\
+ ands r1, r0\n\
+ cmp r2, r1\n\
+ beq _08130D90\n\
+ bl sub_8131074\n\
+_08130D90:\n\
+ ldr r2, =gUnknown_020244A8\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ movs r1, 0\n\
+ strb r1, [r0, 0x11]\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ strb r1, [r0, 0x1]\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ ldr r4, [r0, 0xC]\n\
+ mov r9, r2\n\
+ cmp r4, 0\n\
+ beq _08130DD8\n\
+ mov r5, r9\n\
+ movs r6, 0\n\
+_08130DB0:\n\
+ movs r0, 0x1\n\
+ ands r0, r4\n\
+ cmp r0, 0\n\
+ beq _08130DC2\n\
+ ldr r0, [r5]\n\
+ ldr r0, [r0, 0x14]\n\
+ strb r6, [r0]\n\
+ bl BattleAI_DoAIProcessing\n\
+_08130DC2:\n\
+ asrs r4, 1\n\
+ ldr r0, [r5]\n\
+ ldr r1, [r0, 0x14]\n\
+ ldrb r0, [r1, 0x11]\n\
+ adds r0, 0x1\n\
+ strb r0, [r1, 0x11]\n\
+ ldr r0, [r5]\n\
+ ldr r0, [r0, 0x14]\n\
+ strb r6, [r0, 0x1]\n\
+ cmp r4, 0\n\
+ bne _08130DB0\n\
+_08130DD8:\n\
+ mov r2, r9\n\
+ ldr r0, [r2]\n\
+ ldr r3, [r0, 0x14]\n\
+ ldrb r1, [r3, 0x10]\n\
+ movs r0, 0x2\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _08130DFC\n\
+ movs r0, 0x4\n\
+ ldr r7, [sp, 0x20]\n\
+ strb r0, [r7]\n\
+ b _08130EAE\n\
+ .pool\n\
+_08130DFC:\n\
+ movs r0, 0x4\n\
+ ands r0, r1\n\
+ lsls r0, 24\n\
+ lsrs r2, r0, 24\n\
+ cmp r2, 0\n\
+ beq _08130E10\n\
+ movs r0, 0x5\n\
+ ldr r1, [sp, 0x20]\n\
+ strb r0, [r1]\n\
+ b _08130EAE\n\
+_08130E10:\n\
+ add r1, sp, 0x10\n\
+ ldrb r0, [r3, 0x4]\n\
+ strb r0, [r1]\n\
+ add r0, sp, 0x14\n\
+ strb r2, [r0]\n\
+ movs r5, 0x1\n\
+ movs r3, 0x1\n\
+ adds r6, r1, 0\n\
+ ldr r0, =gPlayerMonIndex\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ ldr r2, =gUnknown_02024090\n\
+ adds r0, r2\n\
+ adds r4, r0, 0x2\n\
+ add r7, sp, 0x14\n\
+_08130E30:\n\
+ ldrh r0, [r4]\n\
+ cmp r0, 0\n\
+ beq _08130E72\n\
+ ldrb r1, [r6]\n\
+ mov r2, r9\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ adds r0, 0x4\n\
+ adds r2, r0, r3\n\
+ movs r0, 0\n\
+ ldrsb r0, [r2, r0]\n\
+ cmp r1, r0\n\
+ bne _08130E56\n\
+ adds r0, r6, r5\n\
+ ldrb r1, [r2]\n\
+ strb r1, [r0]\n\
+ adds r0, r7, r5\n\
+ strb r3, [r0]\n\
+ adds r5, 0x1\n\
+_08130E56:\n\
+ ldrb r1, [r6]\n\
+ mov r2, r9\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ adds r0, 0x4\n\
+ adds r2, r0, r3\n\
+ movs r0, 0\n\
+ ldrsb r0, [r2, r0]\n\
+ cmp r1, r0\n\
+ bge _08130E72\n\
+ ldrb r0, [r2]\n\
+ strb r0, [r6]\n\
+ strb r3, [r7]\n\
+ movs r5, 0x1\n\
+_08130E72:\n\
+ adds r4, 0x2\n\
+ adds r3, 0x1\n\
+ cmp r3, 0x3\n\
+ ble _08130E30\n\
+ bl Random\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ adds r1, r5, 0\n\
+ bl __modsi3\n\
+ add r0, sp\n\
+ adds r0, 0x14\n\
+ ldrb r0, [r0]\n\
+ ldr r7, [sp, 0x20]\n\
+ strb r0, [r7]\n\
+ ldrb r2, [r6]\n\
+ mov r0, r10\n\
+ strh r2, [r0]\n\
+ ldr r0, =gPlayerMonIndex\n\
+ ldrb r1, [r0]\n\
+ movs r0, 0x2\n\
+ eors r0, r1\n\
+ cmp r8, r0\n\
+ bne _08130EAE\n\
+ cmp r2, 0x63\n\
+ bgt _08130EAE\n\
+ ldr r0, =0x0000ffff\n\
+ mov r1, r10\n\
+ strh r0, [r1]\n\
+_08130EAE:\n\
+ ldr r2, [sp, 0x20]\n\
+ adds r2, 0x1\n\
+ str r2, [sp, 0x20]\n\
+ movs r7, 0x2\n\
+ add r10, r7\n\
+ movs r0, 0x1\n\
+ add r8, r0\n\
+ mov r1, r8\n\
+ cmp r1, 0x3\n\
+ bgt _08130EC4\n\
+ b _08130D14\n\
+_08130EC4:\n\
+ mov r0, sp\n\
+ ldrh r5, [r0]\n\
+ movs r0, 0\n\
+ ldr r2, [sp, 0x18]\n\
+ strb r0, [r2]\n\
+ movs r4, 0x1\n\
+ mov r8, r4\n\
+ ldr r6, =gEnemyMonIndex\n\
+ ldr r3, [sp, 0x18]\n\
+ mov r1, sp\n\
+ adds r1, 0x2\n\
+_08130EDA:\n\
+ lsls r0, r5, 16\n\
+ asrs r2, r0, 16\n\
+ movs r7, 0\n\
+ ldrsh r0, [r1, r7]\n\
+ cmp r2, r0\n\
+ bne _08130EEE\n\
+ adds r0, r3, r4\n\
+ mov r7, r8\n\
+ strb r7, [r0]\n\
+ adds r4, 0x1\n\
+_08130EEE:\n\
+ movs r7, 0\n\
+ ldrsh r0, [r1, r7]\n\
+ cmp r2, r0\n\
+ bge _08130EFE\n\
+ ldrh r5, [r1]\n\
+ mov r0, r8\n\
+ strb r0, [r3]\n\
+ movs r4, 0x1\n\
+_08130EFE:\n\
+ adds r1, 0x2\n\
+ movs r2, 0x1\n\
+ add r8, r2\n\
+ mov r7, r8\n\
+ cmp r7, 0x3\n\
+ ble _08130EDA\n\
+ bl Random\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ adds r1, r4, 0\n\
+ bl __modsi3\n\
+ ldr r1, [sp, 0x18]\n\
+ adds r0, r1, r0\n\
+ ldrb r0, [r0]\n\
+ strb r0, [r6]\n\
+ ldrb r0, [r6]\n\
+ ldr r2, [sp, 0x1C]\n\
+ adds r0, r2, r0\n\
+ ldrb r0, [r0]\n\
+ add sp, 0x24\n\
+ pop {r3-r5}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ mov r10, r5\n\
+ pop {r4-r7}\n\
+ pop {r1}\n\
+ bx r1\n\
+ .pool\n\
+ .syntax divided\n");
+}
+#endif
+
+void BattleAI_DoAIProcessing(void)
+{
+ while (AI_THINKING_STRUCT->aiState != AIState_FinishedProcessing)
+ {
+ switch (AI_THINKING_STRUCT->aiState)
+ {
+ case AIState_DoNotProcess: //Needed to match.
+ break;
+ case AIState_SettingUp:
+ gAIScriptPtr = gUnknown_082DBEF8[AI_THINKING_STRUCT->aiLogicId]; // set AI ptr to logic ID.
+ if (gBattleMons[gPlayerMonIndex].pp[AI_THINKING_STRUCT->movesetIndex] == 0)
+ {
+ AI_THINKING_STRUCT->moveConsidered = 0;
+ }
+ else
+ {
+ AI_THINKING_STRUCT->moveConsidered = gBattleMons[gPlayerMonIndex].moves[AI_THINKING_STRUCT->movesetIndex];
+ }
+ AI_THINKING_STRUCT->aiState++;
+ break;
+ case AIState_Processing:
+ if (AI_THINKING_STRUCT->moveConsidered != 0)
+ gUnknown_085B083C[*gAIScriptPtr](); // run AI command.
+ else
+ {
+ AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0;
+ AI_THINKING_STRUCT->aiAction |= 1;
+ }
+ if (AI_THINKING_STRUCT->aiAction & 1)
+ {
+ AI_THINKING_STRUCT->movesetIndex++;
+
+ if (AI_THINKING_STRUCT->movesetIndex < 4 && !(AI_THINKING_STRUCT->aiAction & 8))
+ AI_THINKING_STRUCT->aiState = AIState_SettingUp;
+ else
+ AI_THINKING_STRUCT->aiState++;
+
+ AI_THINKING_STRUCT->aiAction &= 0xFE;
+ }
+ break;
+ }
+ }
+}
+
+void sub_8131074(void)
+{
+ s32 i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] == gUnknown_02024248[gEnemyMonIndex])
+ break;
+ if (gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] != gUnknown_02024248[gEnemyMonIndex] //HACK: This redundant condition is a hack to make the asm match.
+ && gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] == 0)
+ {
+ gUnknown_020244A8->unk18->unk0[gEnemyMonIndex][i] = gUnknown_02024248[gEnemyMonIndex];
+ break;
+ }
+ }
+}
+
+void sub_81310F0(u8 a)
+{
+ s32 i;
+
+ for (i = 0; i < 4; i++)
+ gUnknown_020244A8->unk18->unk0[a][i] = 0;
+}
+
+void b_history__record_ability_usage_of_player(u8 a, u8 b)
+{
+ gUnknown_020244A8->unk18->unk40[a] = b;
+}
+
+void sub_8131130(u8 a)
+{
+ gUnknown_020244A8->unk18->unk40[a] = 0;
+}
+
+void b_history__record_item_x12_of_player(u8 a, u8 b)
+{
+ gUnknown_020244A8->unk18->unk44[a] = b;
+}
+
+void sub_8131160(u8 a)
+{
+ gUnknown_020244A8->unk18->unk44[a] = 0;
+}
+
+static void BattleAICmd_if_random_less_than(void)
+{
+ u16 random = Random();
+
+ if (random % 256 < gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_random_greater_than(void)
+{
+ u16 random = Random();
+
+ if (random % 256 > gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_random_equal(void)
+{
+ u16 random = Random();
+
+ if (random % 256 == gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_random_not_equal(void)
+{
+ u16 random = Random();
+
+ if (random % 256 != gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_score(void)
+{
+ AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] += gAIScriptPtr[1]; // add the result to the array of the move consider's score.
+
+ if (AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] < 0) // if the score is negative, flatten it to 0.
+ AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0;
+
+ gAIScriptPtr += 2; // AI return.
+}
+
+static void BattleAICmd_if_hp_less_than(void)
+{
+ u16 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) < gAIScriptPtr[2])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_hp_more_than(void)
+{
+ u16 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) > gAIScriptPtr[2])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_hp_equal(void)
+{
+ u16 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) == gAIScriptPtr[2])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_hp_not_equal(void)
+{
+ u16 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) != gAIScriptPtr[2])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_status(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gBattleMons[index].status1 & arg) != 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_not_status(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gBattleMons[index].status1 & arg) == 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_status2(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gBattleMons[index].status2 & arg) != 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_not_status2(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gBattleMons[index].status2 & arg) == 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_status3(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gUnknown_020242AC[index] & arg) != 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_not_status3(void)
+{
+ u16 index;
+ u32 arg;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gUnknown_020242AC[index] & arg) == 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_status4(void)
+{
+ u16 index;
+ u32 arg1, arg2;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg1 = battle_get_per_side_status(index) & 1;
+ arg2 = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gUnknown_0202428E[arg1] & arg2) != 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_not_status4(void)
+{
+ u16 index;
+ u32 arg1, arg2;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ arg1 = battle_get_per_side_status(index) & 1;
+ arg2 = AIScriptRead32(gAIScriptPtr + 2);
+
+ if ((gUnknown_0202428E[arg1] & arg2) == 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ else
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_less_than(void)
+{
+ if (AI_THINKING_STRUCT->funcResult < gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_more_than(void)
+{
+ if (AI_THINKING_STRUCT->funcResult > gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_equal(void)
+{
+ if (AI_THINKING_STRUCT->funcResult == gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_not_equal(void)
+{
+ if (AI_THINKING_STRUCT->funcResult != gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_less_than_32(void)
+{
+ u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->funcResult < *temp)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ else
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_more_than_32(void)
+{
+ u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->funcResult > *temp)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ else
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_equal_32(void)
+{
+ u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->funcResult == *temp)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ else
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_not_equal_32(void)
+{
+ u8 *temp = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->funcResult != *temp)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ else
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_move(void)
+{
+ u16 move = AIScriptRead16(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->moveConsidered == move)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_not_move(void)
+{
+ u16 move = AIScriptRead16(gAIScriptPtr + 1);
+
+ if (AI_THINKING_STRUCT->moveConsidered != move)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ else
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_in_bytes(void)
+{
+ u8 *ptr = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ while (*ptr != 0xFF)
+ {
+ if (AI_THINKING_STRUCT->funcResult == *ptr)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ return;
+ }
+ ptr++;
+ }
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_not_in_bytes(void)
+{
+ u8 *ptr = AIScriptReadPtr(gAIScriptPtr + 1);
+
+ while (*ptr != 0xFF)
+ {
+ if (AI_THINKING_STRUCT->funcResult == *ptr)
+ {
+ gAIScriptPtr += 9;
+ return;
+ }
+ ptr++;
+ }
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+}
+
+static void BattleAICmd_if_in_words(void)
+{
+ u16 *ptr = (u16 *)AIScriptReadPtr(gAIScriptPtr + 1);
+
+ while (*ptr != 0xFFFF)
+ {
+ if (AI_THINKING_STRUCT->funcResult == *ptr)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+ return;
+ }
+ ptr++;
+ }
+ gAIScriptPtr += 9;
+}
+
+static void BattleAICmd_if_not_in_words(void)
+{
+ u16 *ptr = (u16 *)AIScriptReadPtr(gAIScriptPtr + 1);
+
+ while (*ptr != 0xFFFF)
+ {
+ if (AI_THINKING_STRUCT->funcResult == *ptr)
+ {
+ gAIScriptPtr += 9;
+ return;
+ }
+ ptr++;
+ }
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 5);
+}
+
+static void BattleAICmd_if_user_can_damage(void)
+{
+ s32 i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] != 0
+ && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].power != 0)
+ break;
+ }
+ if (i == 4)
+ gAIScriptPtr += 5;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+}
+
+static void BattleAICmd_if_user_cant_damage(void)
+{
+ s32 i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] != 0
+ && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].power != 0)
+ break;
+ }
+ if (i != 4)
+ gAIScriptPtr += 5;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+}
+
+static void BattleAICmd_get_turn_count(void)
+{
+ AI_THINKING_STRUCT->funcResult = gUnknown_03005D10[19];
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_type(void)
+{
+ u8 typeVar = gAIScriptPtr[1];
+
+ switch (typeVar)
+ {
+ case 1: // player primary type
+ AI_THINKING_STRUCT->funcResult = gBattleMons[gPlayerMonIndex].type1;
+ break;
+ case 0: // enemy primary type
+ AI_THINKING_STRUCT->funcResult = gBattleMons[gEnemyMonIndex].type1;
+ break;
+ case 3: // player secondary type
+ AI_THINKING_STRUCT->funcResult = gBattleMons[gPlayerMonIndex].type2;
+ break;
+ case 2: // enemy secondary type
+ AI_THINKING_STRUCT->funcResult = gBattleMons[gEnemyMonIndex].type2;
+ break;
+ case 4: // type of move being pointed to
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].type;
+ break;
+ }
+ gAIScriptPtr += 2;
+}
+
+// util for double battles? whats this doing in the middle of the battle AI macros?
+u8 sub_8131E70(u8 index)
+{
+ switch (index)
+ {
+ case 1:
+ return gPlayerMonIndex;
+ case 0:
+ default:
+ return gEnemyMonIndex;
+ case 3:
+ return gPlayerMonIndex ^ 2;
+ case 2:
+ return gEnemyMonIndex ^ 2;
+ }
+}
+
+static void BattleAICmd_unk_5F(void)
+{
+ u8 index = sub_8131E70(gAIScriptPtr[1]);
+
+ if(gBattleMons[index].type1 == gAIScriptPtr[2] || gBattleMons[index].type2 == gAIScriptPtr[2])
+ {
+ AI_THINKING_STRUCT->funcResult = 1;
+ }
+ else
+ {
+ AI_THINKING_STRUCT->funcResult = 0;
+ }
+
+ gAIScriptPtr += 3;
+}
+
+static void BattleAICmd_get_move_power(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power;
+ gAIScriptPtr += 1;
+}
+
+__attribute__((naked)) // not even going to try. if it doesnt match in ruby, it wont match in emerald (yet).
+static void BattleAICmd_is_most_powerful_move(void)
+{
+ asm(".syntax unified\n\
+ push {r4-r7,lr}\n\
+ mov r7, r10\n\
+ mov r6, r9\n\
+ mov r5, r8\n\
+ push {r5-r7}\n\
+ sub sp, 0x14\n\
+ movs r3, 0\n\
+ ldr r0, =gUnknown_085B09C8\n\
+ ldrh r1, [r0]\n\
+ ldr r5, =0x0000ffff\n\
+ ldr r6, =gBattleMoves\n\
+ ldr r2, =gUnknown_020244A8\n\
+ cmp r1, r5\n\
+ beq _08131F86\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ ldrh r1, [r0, 0x2]\n\
+ lsls r0, r1, 1\n\
+ adds r0, r1\n\
+ lsls r0, 2\n\
+ adds r0, r6\n\
+ ldrb r4, [r0]\n\
+ ldr r1, =gUnknown_085B09C8\n\
+_08131F76:\n\
+ ldrh r0, [r1]\n\
+ cmp r4, r0\n\
+ beq _08131F86\n\
+ adds r1, 0x2\n\
+ adds r3, 0x1\n\
+ ldrh r0, [r1]\n\
+ cmp r0, r5\n\
+ bne _08131F76\n\
+_08131F86:\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ ldrh r1, [r0, 0x2]\n\
+ lsls r0, r1, 1\n\
+ adds r0, r1\n\
+ lsls r0, 2\n\
+ adds r0, r6\n\
+ ldrb r0, [r0, 0x1]\n\
+ cmp r0, 0x1\n\
+ bhi _08131F9C\n\
+ b _08132126\n\
+_08131F9C:\n\
+ lsls r0, r3, 1\n\
+ ldr r1, =gUnknown_085B09C8\n\
+ adds r0, r1\n\
+ ldrh r3, [r0]\n\
+ ldr r0, =0x0000ffff\n\
+ cmp r3, r0\n\
+ beq _08131FAC\n\
+ b _08132126\n\
+_08131FAC:\n\
+ ldr r0, =gUnknown_02024400\n\
+ movs r1, 0\n\
+ strh r1, [r0]\n\
+ ldr r0, =gUnknown_0202449C\n\
+ ldr r0, [r0]\n\
+ strb r1, [r0, 0x13]\n\
+ ldr r0, =gUnknown_02024474\n\
+ movs r2, 0x1\n\
+ strb r2, [r0, 0xE]\n\
+ ldr r0, =gBattleMoveFlags\n\
+ strb r1, [r0]\n\
+ ldr r0, =gCritMultiplier\n\
+ strb r2, [r0]\n\
+ movs r6, 0\n\
+ mov r9, r3\n\
+ ldr r2, =gUnknown_085B09C8\n\
+ ldrh r2, [r2]\n\
+ str r2, [sp, 0x10]\n\
+_08131FD0:\n\
+ movs r3, 0\n\
+ ldr r5, =gBattleMons\n\
+ lsls r4, r6, 1\n\
+ ldr r7, =gPlayerMonIndex\n\
+ lsls r0, r6, 2\n\
+ mov r8, r0\n\
+ adds r1, r6, 0x1\n\
+ mov r10, r1\n\
+ ldr r2, [sp, 0x10]\n\
+ cmp r2, r9\n\
+ beq _08132014\n\
+ ldr r2, =gBattleMoves\n\
+ ldrb r1, [r7]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r4, r0\n\
+ adds r1, r5, 0\n\
+ adds r1, 0xC\n\
+ adds r0, r1\n\
+ ldrh r1, [r0]\n\
+ lsls r0, r1, 1\n\
+ adds r0, r1\n\
+ lsls r0, 2\n\
+ adds r0, r2\n\
+ ldrb r2, [r0]\n\
+ ldr r1, =gUnknown_085B09C8\n\
+_08132004:\n\
+ ldrh r0, [r1]\n\
+ cmp r2, r0\n\
+ beq _08132014\n\
+ adds r1, 0x2\n\
+ adds r3, 0x1\n\
+ ldrh r0, [r1]\n\
+ cmp r0, r9\n\
+ bne _08132004\n\
+_08132014:\n\
+ ldrb r1, [r7]\n\
+ movs r0, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r4, r0\n\
+ adds r1, r5, 0\n\
+ adds r1, 0xC\n\
+ adds r1, r0, r1\n\
+ ldrh r0, [r1]\n\
+ cmp r0, 0\n\
+ beq _081320C0\n\
+ lsls r0, r3, 1\n\
+ ldr r2, =gUnknown_085B09C8\n\
+ adds r0, r2\n\
+ ldrh r0, [r0]\n\
+ cmp r0, r9\n\
+ bne _081320C0\n\
+ ldr r0, =gBattleMoves\n\
+ ldrh r2, [r1]\n\
+ lsls r1, r2, 1\n\
+ adds r1, r2\n\
+ lsls r1, 2\n\
+ adds r1, r0\n\
+ ldrb r0, [r1, 0x1]\n\
+ cmp r0, 0x1\n\
+ bls _081320C0\n\
+ ldr r5, =gUnknown_020241EA\n\
+ strh r2, [r5]\n\
+ ldrb r0, [r7]\n\
+ ldr r4, =gEnemyMonIndex\n\
+ ldrb r1, [r4]\n\
+ bl sub_8046E7C\n\
+ ldrh r0, [r5]\n\
+ ldrb r1, [r7]\n\
+ ldrb r2, [r4]\n\
+ bl move_effectiveness_something\n\
+ mov r4, sp\n\
+ add r4, r8\n\
+ ldr r2, =gBattleMoveDamage\n\
+ ldr r0, =gUnknown_020244A8\n\
+ ldr r0, [r0]\n\
+ ldr r0, [r0, 0x14]\n\
+ adds r0, 0x18\n\
+ adds r0, r6\n\
+ ldrb r1, [r0]\n\
+ ldr r0, [r2]\n\
+ muls r0, r1\n\
+ movs r1, 0x64\n\
+ bl __divsi3\n\
+ str r0, [r4]\n\
+ cmp r0, 0\n\
+ bne _081320C8\n\
+ movs r0, 0x1\n\
+ str r0, [r4]\n\
+ b _081320C8\n\
+ .pool\n\
+_081320C0:\n\
+ mov r1, sp\n\
+ add r1, r8\n\
+ movs r0, 0\n\
+ str r0, [r1]\n\
+_081320C8:\n\
+ mov r6, r10\n\
+ cmp r6, 0x3\n\
+ bgt _081320D0\n\
+ b _08131FD0\n\
+_081320D0:\n\
+ movs r6, 0\n\
+ ldr r2, =gUnknown_020244A8\n\
+ ldr r0, [r2]\n\
+ ldr r0, [r0, 0x14]\n\
+ ldrb r0, [r0, 0x1]\n\
+ lsls r0, 2\n\
+ add r0, sp\n\
+ ldr r1, [sp]\n\
+ ldr r0, [r0]\n\
+ ldr r5, =gAIScriptPtr\n\
+ cmp r1, r0\n\
+ bgt _08132106\n\
+ adds r4, r2, 0\n\
+ mov r3, sp\n\
+_081320EC:\n\
+ adds r3, 0x4\n\
+ adds r6, 0x1\n\
+ cmp r6, 0x3\n\
+ bgt _08132106\n\
+ ldr r0, [r4]\n\
+ ldr r0, [r0, 0x14]\n\
+ ldrb r0, [r0, 0x1]\n\
+ lsls r0, 2\n\
+ add r0, sp\n\
+ ldr r1, [r3]\n\
+ ldr r0, [r0]\n\
+ cmp r1, r0\n\
+ ble _081320EC\n\
+_08132106:\n\
+ cmp r6, 0x4\n\
+ bne _0813211C\n\
+ ldr r0, [r2]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0x2\n\
+ str r0, [r1, 0x8]\n\
+ b _08132130\n\
+ .pool\n\
+_0813211C:\n\
+ ldr r0, [r2]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0x1\n\
+ str r0, [r1, 0x8]\n\
+ b _08132130\n\
+_08132126:\n\
+ ldr r0, [r2]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0\n\
+ str r0, [r1, 0x8]\n\
+ ldr r5, =gAIScriptPtr\n\
+_08132130:\n\
+ ldr r0, [r5]\n\
+ adds r0, 0x1\n\
+ str r0, [r5]\n\
+ add sp, 0x14\n\
+ pop {r3-r5}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ mov r10, r5\n\
+ pop {r4-r7}\n\
+ pop {r0}\n\
+ bx r0\n\
+ .pool\n\
+ .syntax divided");
+}
+
+static void BattleAICmd_get_move(void)
+{
+ if (gAIScriptPtr[1] == USER)
+ AI_THINKING_STRUCT->funcResult = gUnknown_02024248[gPlayerMonIndex];
+ else
+ AI_THINKING_STRUCT->funcResult = gUnknown_02024248[gEnemyMonIndex];
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_if_arg_equal(void)
+{
+ if (gAIScriptPtr[1] == AI_THINKING_STRUCT->funcResult)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_arg_not_equal(void)
+{
+ if (gAIScriptPtr[1] != AI_THINKING_STRUCT->funcResult)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_would_go_first(void)
+{
+ if (b_first_side(gPlayerMonIndex, gEnemyMonIndex, 1) == gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_would_not_go_first(void)
+{
+ if (b_first_side(gPlayerMonIndex, gEnemyMonIndex, 1) != gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_nullsub_2A(void)
+{
+}
+
+static void BattleAICmd_nullsub_2B(void)
+{
+}
+
+static void BattleAICmd_count_alive_pokemon(void)
+{
+ u8 index;
+ u8 var, var2;
+ struct Pokemon *party;
+ int i;
+
+ AI_THINKING_STRUCT->funcResult = 0;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if (battle_side_get_owner(index) == 0)
+ party = gPlayerParty;
+ else
+ party = gEnemyParty;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ u32 status;
+ var = gUnknown_0202406E[index][0];
+ status = battle_get_per_side_status(index) ^ 2;
+ var2 = gUnknown_0202406E[battle_get_side_with_given_state(status)][0];
+ }
+ else
+ {
+ var = gUnknown_0202406E[index][0];
+ var2 = gUnknown_0202406E[index][0];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ if (i != var && i != var2
+ && GetMonData(&party[i], MON_DATA_HP) != 0
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG)
+ {
+ AI_THINKING_STRUCT->funcResult++;
+ }
+ }
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_get_considered_move(void)
+{
+ AI_THINKING_STRUCT->funcResult = AI_THINKING_STRUCT->moveConsidered;
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_considered_move_effect(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect;
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_ability(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if(gUnknown_02024064 != index)
+ {
+ if(UNK_2016A00_STRUCT->unk40[index] != 0)
+ {
+ AI_THINKING_STRUCT->funcResult = UNK_2016A00_STRUCT->unk40[index];
+ gAIScriptPtr += 2;
+ return;
+ }
+
+ // abilities that prevent fleeing.
+ if (gBattleMons[index].ability == ABILITY_SHADOW_TAG
+ || gBattleMons[index].ability == ABILITY_MAGNET_PULL
+ || gBattleMons[index].ability == ABILITY_ARENA_TRAP)
+ {
+ AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability;
+ gAIScriptPtr += 2;
+ return;
+ }
+
+ if (gBaseStats[gBattleMons[index].species].ability1 != ABILITY_NONE)
+ {
+ if (gBaseStats[gBattleMons[index].species].ability2 != ABILITY_NONE)
+ {
+ // AI has no knowledge of opponent, so it guesses which ability.
+ if(Random() & 1)
+ {
+ AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1;
+ }
+ else
+ {
+ AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2;
+ }
+ }
+ else
+ {
+ AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1; // it's definitely ability 1.
+ }
+ }
+ else
+ {
+ AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2; // AI cant actually reach this part since every mon has at least 1 ability.
+ }
+ }
+ else
+ {
+ // The AI knows its own ability.
+ AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability;
+ }
+ gAIScriptPtr += 2;
+}
+
+#ifdef NONMATCHING
+static void tai60_unk(void)
+{
+ u8 index = sub_8131E70(gAIScriptPtr[1]);
+ u8 arg2 = gAIScriptPtr[2];
+ u8 var;
+
+ if(gAIScriptPtr[1] == 0 || gAIScriptPtr[1] == 2)
+ {
+ // _0813253A
+ if(UNK_2016A00_STRUCT->unk40[index] != 0)
+ {
+ var = UNK_2016A00_STRUCT->unk40[index];
+ AI_THINKING_STRUCT->funcResult = var;
+ }
+ else
+ {
+ // _0813255C
+ if (gBattleMons[index].ability == ABILITY_SHADOW_TAG
+ || gBattleMons[index].ability == ABILITY_MAGNET_PULL
+ || gBattleMons[index].ability == ABILITY_ARENA_TRAP)
+ {
+ var = gBattleMons[index].ability;
+ }
+ else
+ {
+ // _08132588
+ if (gBaseStats[gBattleMons[index].species].ability1 != ABILITY_NONE)
+ {
+ if (gBaseStats[gBattleMons[index].species].ability2 != ABILITY_NONE)
+ {
+ if(gBaseStats[gBattleMons[index].species].ability1 != arg2 && gBaseStats[gBattleMons[index].species].ability2 != arg2)
+ {
+ var = 2;
+ }
+ else
+ {
+ var = gBaseStats[gBattleMons[index].species].ability1;
+ }
+ }
+ else
+ {
+ // _081325B4
+ var = gBaseStats[gBattleMons[index].species].ability1;
+ }
+ }
+ else
+ {
+ // _081325B8
+ var = gBaseStats[gBattleMons[index].species].ability2;
+ }
+ }
+ }
+ }
+ else
+ {
+ // _081325BC
+ var = gBattleMons[index].ability;
+ }
+
+ // _081325CA
+ if(var == ABILITY_NONE)
+ {
+ AI_THINKING_STRUCT->funcResult = 2;
+ }
+ else if(var == arg2)
+ {
+ AI_THINKING_STRUCT->funcResult = 1;
+ }
+ else
+ {
+ AI_THINKING_STRUCT->funcResult = 0;
+ }
+ gAIScriptPtr += 3;
+}
+#else
+__attribute__((naked))
+static void tai60_unk(void)
+{
+ asm(".syntax unified\n\
+ push {r4-r6,lr}\n\
+ ldr r4, =gAIScriptPtr\n\
+ ldr r0, [r4]\n\
+ ldrb r0, [r0, 0x1]\n\
+ bl sub_8131E70\n\
+ lsls r0, 24\n\
+ lsrs r5, r0, 24\n\
+ ldr r0, [r4]\n\
+ ldrb r3, [r0, 0x2]\n\
+ ldrb r0, [r0, 0x1]\n\
+ cmp r0, 0\n\
+ beq _0813253A\n\
+ cmp r0, 0x2\n\
+ bne _081325BC\n\
+_0813253A:\n\
+ ldr r0, =gUnknown_020244A8\n\
+ ldr r4, [r0]\n\
+ ldr r1, [r4, 0x18]\n\
+ adds r1, 0x40\n\
+ adds r2, r1, r5\n\
+ ldrb r1, [r2]\n\
+ adds r6, r0, 0\n\
+ cmp r1, 0\n\
+ beq _0813255C\n\
+ adds r3, r1, 0\n\
+ ldr r0, [r4, 0x14]\n\
+ str r3, [r0, 0x8]\n\
+ b _081325CA\n\
+ .pool\n\
+_0813255C:\n\
+ ldr r1, =gBattleMons\n\
+ movs r0, 0x58\n\
+ muls r0, r5\n\
+ adds r4, r0, r1\n\
+ adds r0, r4, 0\n\
+ adds r0, 0x20\n\
+ ldrb r0, [r0]\n\
+ cmp r0, 0x17\n\
+ beq _08132576\n\
+ cmp r0, 0x2A\n\
+ beq _08132576\n\
+ cmp r0, 0x47\n\
+ bne _08132588\n\
+_08132576:\n\
+ movs r0, 0x58\n\
+ muls r0, r5\n\
+ adds r0, r1\n\
+ adds r0, 0x20\n\
+ ldrb r3, [r0]\n\
+ b _081325CA\n\
+ .pool\n\
+_08132588:\n\
+ ldr r2, =gBaseStats\n\
+ ldrh r1, [r4]\n\
+ lsls r0, r1, 3\n\
+ subs r0, r1\n\
+ lsls r0, 2\n\
+ adds r1, r0, r2\n\
+ ldrb r4, [r1, 0x16]\n\
+ cmp r4, 0\n\
+ beq _081325B8\n\
+ ldrb r2, [r1, 0x17]\n\
+ cmp r2, 0\n\
+ beq _081325B4\n\
+ adds r0, r3, 0\n\
+ cmp r4, r0\n\
+ beq _081325CE\n\
+ cmp r2, r0\n\
+ beq _081325CE\n\
+ adds r3, r4, 0\n\
+ b _081325CA\n\
+ .pool\n\
+_081325B4:\n\
+ ldrb r3, [r1, 0x16]\n\
+ b _081325CA\n\
+_081325B8:\n\
+ ldrb r3, [r1, 0x17]\n\
+ b _081325CA\n\
+_081325BC:\n\
+ ldr r1, =gBattleMons\n\
+ movs r0, 0x58\n\
+ muls r0, r5\n\
+ adds r0, r1\n\
+ adds r0, 0x20\n\
+ ldrb r3, [r0]\n\
+ ldr r6, =gUnknown_020244A8\n\
+_081325CA:\n\
+ cmp r3, 0\n\
+ bne _081325E8\n\
+_081325CE:\n\
+ ldr r0, [r6]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0x2\n\
+ str r0, [r1, 0x8]\n\
+ ldr r2, =gAIScriptPtr\n\
+ b _08132608\n\
+ .pool\n\
+_081325E8:\n\
+ ldr r0, =gAIScriptPtr\n\
+ ldr r1, [r0]\n\
+ adds r2, r0, 0\n\
+ ldrb r1, [r1, 0x2]\n\
+ cmp r3, r1\n\
+ bne _08132600\n\
+ ldr r0, [r6]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0x1\n\
+ b _08132606\n\
+ .pool\n\
+_08132600:\n\
+ ldr r0, [r6]\n\
+ ldr r1, [r0, 0x14]\n\
+ movs r0, 0\n\
+_08132606:\n\
+ str r0, [r1, 0x8]\n\
+_08132608:\n\
+ ldr r0, [r2]\n\
+ adds r0, 0x3\n\
+ str r0, [r2]\n\
+ pop {r4-r6}\n\
+ pop {r0}\n\
+ bx r0\n\
+ .pool\n\
+ .syntax divided");
+}
+#endif
+
+static void BattleAICmd_get_highest_possible_damage(void)
+{
+ s32 i;
+
+ gUnknown_02024400 = 0;
+ gUnknown_0202449C[0x13] = 0;
+ gUnknown_02024474[0xE] = 1;
+ gBattleMoveFlags = 0;
+ gCritMultiplier = 1;
+ AI_THINKING_STRUCT->funcResult = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ gBattleMoveDamage = 40;
+ gUnknown_020241EA = gBattleMons[gPlayerMonIndex].moves[i];
+
+ if (gUnknown_020241EA)
+ {
+ move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex);
+
+ // reduce by 1/3.
+ if (gBattleMoveDamage == 120)
+ gBattleMoveDamage = 80;
+ if (gBattleMoveDamage == 240)
+ gBattleMoveDamage = 160;
+ if (gBattleMoveDamage == 30)
+ gBattleMoveDamage = 20;
+ if (gBattleMoveDamage == 15)
+ gBattleMoveDamage = 10;
+
+ if (gBattleMoveFlags & 8) // if it's a status move, it wont do anything.
+ gBattleMoveDamage = 0;
+
+ if (AI_THINKING_STRUCT->funcResult < gBattleMoveDamage)
+ AI_THINKING_STRUCT->funcResult = gBattleMoveDamage;
+ }
+ }
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_if_damage_bonus(void)
+{
+ u8 damageVar;
+
+ gUnknown_02024400 = 0;
+ gUnknown_0202449C[0x13] = 0;
+ gUnknown_02024474[0xE] = 1;
+ gBattleMoveFlags = 0;
+ gCritMultiplier = 1;
+
+ gBattleMoveDamage = 40;
+ gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered;
+
+ move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex);
+
+ if (gBattleMoveDamage == 120)
+ gBattleMoveDamage = 80;
+ if (gBattleMoveDamage == 240)
+ gBattleMoveDamage = 160;
+ if (gBattleMoveDamage == 30)
+ gBattleMoveDamage = 20;
+ if (gBattleMoveDamage == 15)
+ gBattleMoveDamage = 10;
+
+ if (gBattleMoveFlags & 8)
+ gBattleMoveDamage = 0;
+
+ // store gBattleMoveDamage in a u8 variable because gAIScriptPtr[1] is a u8.
+ damageVar = gBattleMoveDamage;
+
+ if (damageVar == gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_nullsub_32(void)
+{
+}
+
+static void BattleAICmd_nullsub_33(void)
+{
+}
+
+static void BattleAICmd_if_status_in_party(void)
+{
+ struct Pokemon *party;
+ int i;
+ u32 statusToCompareTo;
+ u8 index;
+
+ switch(gAIScriptPtr[1])
+ {
+ case 1:
+ index = gPlayerMonIndex;
+ break;
+ default:
+ index = gEnemyMonIndex;
+ break;
+ }
+
+ party = (battle_side_get_owner(index) == 0) ? gPlayerParty : gEnemyParty;
+
+ statusToCompareTo = AIScriptRead32(gAIScriptPtr + 2);
+
+ for (i = 0; i < 6; i++)
+ {
+ u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
+ u16 hp = GetMonData(&party[i], MON_DATA_HP);
+ u32 status = GetMonData(&party[i], MON_DATA_STATUS);
+
+ if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+ return;
+ }
+ }
+
+ gAIScriptPtr += 10;
+}
+
+static void BattleAICmd_if_status_not_in_party(void)
+{
+ struct Pokemon *party;
+ int i;
+ u32 statusToCompareTo;
+ u8 index;
+
+ switch(gAIScriptPtr[1])
+ {
+ case 1:
+ index = gPlayerMonIndex;
+ break;
+ default:
+ index = gEnemyMonIndex;
+ break;
+ }
+
+ party = (battle_side_get_owner(index) == 0) ? gPlayerParty : gEnemyParty;
+
+ statusToCompareTo = AIScriptRead32(gAIScriptPtr + 2);
+
+ for (i = 0; i < 6; i++)
+ {
+ u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
+ u16 hp = GetMonData(&party[i], MON_DATA_HP);
+ u32 status = GetMonData(&party[i], MON_DATA_STATUS);
+
+ if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo)
+ {
+ gAIScriptPtr += 10; // still bugged in Emerald
+ }
+ }
+
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 6);
+}
+
+static void BattleAICmd_get_weather(void)
+{
+ if (gBattleWeather & 7)
+ AI_THINKING_STRUCT->funcResult = 1;
+ if (gBattleWeather & 0x18)
+ AI_THINKING_STRUCT->funcResult = 2;
+ if (gBattleWeather & 0x60)
+ AI_THINKING_STRUCT->funcResult = 0;
+ if (gBattleWeather & 0x80)
+ AI_THINKING_STRUCT->funcResult = 3;
+
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_if_effect(void)
+{
+ if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect == gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_not_effect(void)
+{
+ if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect != gAIScriptPtr[1])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+static void BattleAICmd_if_stat_level_less_than(void)
+{
+ u32 party;
+
+ if (gAIScriptPtr[1] == USER)
+ party = gPlayerMonIndex;
+ else
+ party = gEnemyMonIndex;
+
+ if (gBattleMons[party].statStages[gAIScriptPtr[2]] < gAIScriptPtr[3])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ else
+ gAIScriptPtr += 8;
+}
+
+static void BattleAICmd_if_stat_level_more_than(void)
+{
+ u32 party;
+
+ if (gAIScriptPtr[1] == USER)
+ party = gPlayerMonIndex;
+ else
+ party = gEnemyMonIndex;
+
+ if (gBattleMons[party].statStages[gAIScriptPtr[2]] > gAIScriptPtr[3])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ else
+ gAIScriptPtr += 8;
+}
+
+static void BattleAICmd_if_stat_level_equal(void)
+{
+ u32 party;
+
+ if (gAIScriptPtr[1] == USER)
+ party = gPlayerMonIndex;
+ else
+ party = gEnemyMonIndex;
+
+ if (gBattleMons[party].statStages[gAIScriptPtr[2]] == gAIScriptPtr[3])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ else
+ gAIScriptPtr += 8;
+}
+
+static void BattleAICmd_if_stat_level_not_equal(void)
+{
+ u32 party;
+
+ if (gAIScriptPtr[1] == USER)
+ party = gPlayerMonIndex;
+ else
+ party = gEnemyMonIndex;
+
+ if (gBattleMons[party].statStages[gAIScriptPtr[2]] != gAIScriptPtr[3])
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ else
+ gAIScriptPtr += 8;
+}
+
+static void BattleAICmd_if_can_faint(void)
+{
+ if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2)
+ {
+ gAIScriptPtr += 5;
+ return;
+ }
+
+ gUnknown_02024400 = 0;
+ gUnknown_0202449C[0x13] = 0;
+ gUnknown_02024474[0xE] = 1;
+ gBattleMoveFlags = 0;
+ gCritMultiplier = 1;
+ gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered;
+ sub_8046E7C(gPlayerMonIndex, gEnemyMonIndex);
+ move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex);
+
+ gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->unk18[AI_THINKING_STRUCT->movesetIndex] / 100;
+
+ // moves always do at least 1 damage.
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+
+ if (gBattleMons[gEnemyMonIndex].hp <= gBattleMoveDamage)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void BattleAICmd_if_cant_faint(void)
+{
+ if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2)
+ {
+ gAIScriptPtr += 5;
+ return;
+ }
+
+ gUnknown_02024400 = 0;
+ gUnknown_0202449C[0x13] = 0;
+ gUnknown_02024474[0xE] = 1;
+ gBattleMoveFlags = 0;
+ gCritMultiplier = 1;
+ gUnknown_020241EA = AI_THINKING_STRUCT->moveConsidered;
+ sub_8046E7C(gPlayerMonIndex, gEnemyMonIndex);
+ move_effectiveness_something(gUnknown_020241EA, gPlayerMonIndex, gEnemyMonIndex);
+
+ gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->unk18[AI_THINKING_STRUCT->movesetIndex] / 100;
+
+ // this macro is missing the damage 0 = 1 assumption.
+
+ if (gBattleMons[gEnemyMonIndex].hp > gBattleMoveDamage)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void BattleAICmd_if_has_move(void)
+{
+ int i;
+ u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2);
+
+ switch(gAIScriptPtr[1])
+ {
+ case 1:
+ // _08132E42
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] == *temp_ptr)
+ break;
+ }
+ if (i == 4)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ return;
+ }
+ case 3: // new to Emerald
+ if(gBattleMons[gPlayerMonIndex ^ 2].hp == 0)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex ^ 2].moves[i] == *temp_ptr)
+ break;
+ }
+ }
+ if (i == 4)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ return;
+ }
+ case 0:
+ case 2:
+ for (i = 0; i < 4; i++)
+ {
+ if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] == *temp_ptr)
+ break;
+ }
+ if (i == 4)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ return;
+ }
+ }
+}
+
+static void BattleAICmd_if_dont_have_move(void)
+{
+ int i;
+ u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2);
+
+ switch(gAIScriptPtr[1])
+ {
+ case 1:
+ case 3: // if_dont_have_move does not have the seperate 3 case check in Emerald unlike if_has_move.
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] == *temp_ptr)
+ break;
+ }
+ if (i != 4)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ return;
+ }
+ case 0:
+ case 2:
+ for (i = 0; i < 4; i++)
+ {
+ if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] == *temp_ptr)
+ break;
+ }
+ if (i != 4)
+ {
+ gAIScriptPtr += 8;
+ return;
+ }
+ else
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ return;
+ }
+ }
+}
+
+static void BattleAICmd_if_move_effect(void)
+{
+ int i;
+
+ switch (gAIScriptPtr[1])
+ {
+ case 1:
+ case 3: // _08133044
+ for(i = 0; i < 4; i++)
+ {
+ if(gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].effect == gAIScriptPtr[2])
+ break;
+ }
+ if (i == 4)
+ gAIScriptPtr += 7;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ break;
+ case 0:
+ case 2: // _08133090
+ for (i = 0; i < 4; i++)
+ {
+ if (gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i]].effect == gAIScriptPtr[2])
+ break;
+ }
+ if (i == 4)
+ gAIScriptPtr += 7;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ break;
+ }
+}
+
+static void BattleAICmd_if_not_move_effect(void)
+{
+ int i;
+
+ switch (gAIScriptPtr[1])
+ {
+ case 1:
+ case 3: // _0813313C
+ for(i = 0; i < 4; i++)
+ {
+ if(gBattleMons[gPlayerMonIndex].moves[i] != 0 && gBattleMoves[gBattleMons[gPlayerMonIndex].moves[i]].effect == gAIScriptPtr[2])
+ break;
+ }
+ if (i != 4)
+ gAIScriptPtr += 7;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ break;
+ case 0:
+ case 2: // _08133188
+ for (i = 0; i < 4; i++)
+ {
+ if (UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i] && gBattleMoves[UNK_2016A00_STRUCT->unk0[gEnemyMonIndex][i]].effect == gAIScriptPtr[2])
+ break;
+ }
+ if (i != 4)
+ gAIScriptPtr += 7;
+ else
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ break;
+ }
+}
+
+static void BattleAICmd_if_last_move_did_damage(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if (gAIScriptPtr[2] == 0)
+ {
+ if (gUnknown_020242BC[index].unk4 == 0)
+ {
+ gAIScriptPtr += 7;
+ return;
+ }
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ return;
+ }
+ else if (gAIScriptPtr[2] != 1) // ignore the macro if its not 0 or 1.
+ {
+ gAIScriptPtr += 7;
+ return;
+ }
+ else if (gUnknown_020242BC[index].unk6 != 0)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 3);
+ return;
+ }
+ gAIScriptPtr += 7;
+}
+
+static void BattleAICmd_if_encored(void)
+{
+ switch (gAIScriptPtr[1])
+ {
+ case 0: // _08109348
+ if (gUnknown_020242BC[gUnknown_02024064].unk4 == AI_THINKING_STRUCT->moveConsidered)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ return;
+ }
+ gAIScriptPtr += 6;
+ return;
+ case 1: // _08109370
+ if (gUnknown_020242BC[gUnknown_02024064].unk6 == AI_THINKING_STRUCT->moveConsidered)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ return;
+ }
+ gAIScriptPtr += 6;
+ return;
+ default:
+ gAIScriptPtr += 6;
+ return;
+ }
+}
+
+static void BattleAICmd_flee(void)
+{
+ AI_THINKING_STRUCT->aiAction |= (AI_ACTION_UNK1 | AI_ACTION_UNK2 | AI_ACTION_UNK4); // what matters is UNK2 being enabled.
+}
+
+static void BattleAICmd_if_random_100(void)
+{
+ u8 safariFleeRate = gUnknown_0202449C[0x7B] * 5; // safari flee rate, from 0-20
+
+ if ((u8)(Random() % 100) < safariFleeRate)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void BattleAICmd_watch(void)
+{
+ AI_THINKING_STRUCT->aiAction |= (AI_ACTION_UNK1 | AI_ACTION_UNK3 | AI_ACTION_UNK4); // what matters is UNK3 being enabled.
+}
+
+static void BattleAICmd_get_hold_effect(void)
+{
+ u8 index;
+ u16 status;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ if (gUnknown_02024064 != index)
+ {
+ AI_THINKING_STRUCT->funcResult = itemid_get_x12(UNK_2016A00_STRUCT->unk44[index]);
+ }
+ else
+ AI_THINKING_STRUCT->funcResult = itemid_get_x12(gBattleMons[index].item);
+
+ gAIScriptPtr += 2;
+}
+
+static void tai62_unk(void)
+{
+ u8 index = sub_8131E70(gAIScriptPtr[1]);
+ u16 item;
+ u8 var1, var2;
+
+ if((index & 1) == (gPlayerMonIndex & 1))
+ item = gBattleMons[index].item;
+ else
+ item = UNK_2016A00_STRUCT->unk44[index];
+
+ // strange way of loading a 16-bit argument from the AI command.
+ var2 = gAIScriptPtr[2];
+ var1 = gAIScriptPtr[3];
+
+ if((var1 | var2) == item)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 4);
+ else
+ gAIScriptPtr += 8;
+}
+
+static void BattleAICmd_get_gender(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ AI_THINKING_STRUCT->funcResult = pokemon_species_get_gender_info(gBattleMons[index].species, gBattleMons[index].personality);
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_is_first_turn(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk16;
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_get_stockpile_count(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk9;
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_is_double_battle(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleTypeFlags & BATTLE_TYPE_DOUBLE;
+
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_item(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ // this hack and a half matches. whatever. i dont care. someone else fix this mess later. PS: still cant fix this.
+ AI_THINKING_STRUCT->funcResult = gUnknown_0202449C[0xB8 + (index * 2)];
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_get_move_type_from_result(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].type;
+
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_move_power_from_result(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].power;
+
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_move_effect_from_result(void)
+{
+ AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].effect;
+
+ gAIScriptPtr += 1;
+}
+
+static void BattleAICmd_get_protect_count(void)
+{
+ u8 index;
+
+ if (gAIScriptPtr[1] == USER)
+ index = gPlayerMonIndex;
+ else
+ index = gEnemyMonIndex;
+
+ AI_THINKING_STRUCT->funcResult = gUnknown_020242BC[index].unk8;
+
+ gAIScriptPtr += 2;
+}
+
+static void BattleAICmd_nullsub_52(void)
+{
+}
+
+static void BattleAICmd_nullsub_53(void)
+{
+}
+
+static void BattleAICmd_nullsub_54(void)
+{
+}
+
+static void BattleAICmd_nullsub_55(void)
+{
+}
+
+static void BattleAICmd_nullsub_56(void)
+{
+}
+
+static void BattleAICmd_nullsub_57(void)
+{
+}
+
+static void BattleAICmd_call(void)
+{
+ b_mc_stack_push(gAIScriptPtr + 5);
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+}
+
+static void BattleAICmd_jump(void)
+{
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+}
+
+static void BattleAICmd_end(void)
+{
+ if (b_mc_stack_pop_cursor() == 0)
+ AI_THINKING_STRUCT->aiAction |= AI_ACTION_UNK1;
+}
+
+static void BattleAICmd_if_level_cond(void)
+{
+ switch (gAIScriptPtr[1])
+ {
+ case 0: // greater than
+ if (gBattleMons[gPlayerMonIndex].level > gBattleMons[gEnemyMonIndex].level)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ return;
+ }
+ gAIScriptPtr += 6;
+ return;
+ case 1: // less than
+ if (gBattleMons[gPlayerMonIndex].level < gBattleMons[gEnemyMonIndex].level)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ return;
+ }
+ gAIScriptPtr += 6;
+ return;
+ case 2: // equal
+ if (gBattleMons[gPlayerMonIndex].level == gBattleMons[gEnemyMonIndex].level)
+ {
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ return;
+ }
+ gAIScriptPtr += 6;
+ return;
+ }
+}
+
+static void BattleAICmd_if_taunted(void)
+{
+ if (gUnknown_020242BC[gEnemyMonIndex].taunt != 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void BattleAICmd_if_not_taunted(void)
+{
+ if (gUnknown_020242BC[gEnemyMonIndex].taunt == 0)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void tai5E_unk(void)
+{
+ if((gPlayerMonIndex & 1) == (gEnemyMonIndex & 1))
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 1);
+ else
+ gAIScriptPtr += 5;
+}
+
+static void tai61_unk(void)
+{
+ u8 index = sub_8131E70(gAIScriptPtr[1]);
+
+ if(UNK_BATTLE_STRUCT->unk4->unkArray[index] & 1)
+ gAIScriptPtr = AIScriptReadPtr(gAIScriptPtr + 2);
+ else
+ gAIScriptPtr += 6;
+}
+
+void b_mc_stack_push(u8 *var)
+{
+ UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20++] = var;
+}
+
+void b_mc_stack_push_cursor(void)
+{
+ UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20++] = gAIScriptPtr;
+}
+
+bool8 b_mc_stack_pop_cursor(void)
+{
+ if (UNK_2016C00_STRUCT->unk20 != 0)
+ {
+ --UNK_2016C00_STRUCT->unk20;
+ gAIScriptPtr = UNK_2016C00_STRUCT->ptr[UNK_2016C00_STRUCT->unk20];
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/src/dma3_manager.c b/src/dma3_manager.c
new file mode 100644
index 000000000..cd080a172
--- /dev/null
+++ b/src/dma3_manager.c
@@ -0,0 +1,528 @@
+#include "global.h"
+
+extern u8 gDma3ManagerLocked;
+extern u8 gDma3RequestCursor;
+
+// size is 0x10
+struct DmaRequestsStruct
+{
+ /* 0x00 */ u8 *src;
+ /* 0x04 */ u8 *dest;
+ /* 0x08 */ u16 size;
+ /* 0x0A */ u16 mode;
+ /* 0x0C */ u32 value;
+};
+
+extern struct DmaRequestsStruct gDma3Requests[128];
+
+void ClearDma3Requests(void)
+{
+ int i;
+
+ gDma3ManagerLocked = TRUE;
+ gDma3RequestCursor = FALSE;
+
+ for(i = 0; i < (u8)ARRAY_COUNT(gDma3Requests); i++)
+ {
+ gDma3Requests[i].size = 0;
+ gDma3Requests[i].src = 0;
+ gDma3Requests[i].dest = 0;
+ }
+
+ gDma3ManagerLocked = FALSE;
+}
+
+#ifdef NONMATCHING
+void ProcessDma3Requests(void)
+{
+ // NOTE: the fillerA member of the DMA struct is actually u32 value;
+ // NOTE: gUnknown_0300001C is just a pointer inside the gDma3Requests structure, not a true symbol; feel free to remove
+ u16 total_size;
+
+ if (gDma3ManagerLocked)
+ return;
+
+ total_size = 0;
+
+ // as long as there are DMA requests to process (unless size or vblank is an issue), do not exit
+ while (gDma3Requests[gDma3RequestCursor].size)
+ {
+ total_size += gDma3Requests[gDma3RequestCursor].size;
+
+ if (total_size > 0xA000)
+ return; // don't do too much at once
+
+ if (REG_VCOUNT > 224)
+ return;// we're about to leave vblank, stop
+
+ switch (gDma3Requests[gDma3RequestCursor].mode)
+ {
+ case 1: // regular 32-bit copy
+ // _08000C8C
+ if(gDma3Requests[gDma3RequestCursor].size <= 0x1000)
+ {
+ DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size);
+ break;
+ }
+ while (gDma3Requests[gDma3RequestCursor].size > 0x1000)
+ {
+ DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, 0x1000);
+ gDma3Requests[gDma3RequestCursor].src += 0x1000;
+ gDma3Requests[gDma3RequestCursor].dest += 0x1000;
+ gDma3Requests[gDma3RequestCursor].size -= 0x1000;
+ }
+ DmaCopy32(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size);
+ break;
+ case 2: // repeat a single 32-bit value across RAM
+ // _08000CD0
+ while (gDma3Requests[gDma3RequestCursor].size > 0x1000)
+ {
+ DmaFill32(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, 0x1000);
+ gDma3Requests[gDma3RequestCursor].dest += 0x1000;
+ gDma3Requests[gDma3RequestCursor].size -= 0x1000;
+ }
+ DmaFill32(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size);
+ break;
+ case 3: // regular 16-bit copy
+ // _08000D3C
+ while (gDma3Requests[gDma3RequestCursor].size > 0x1000)
+ {
+ DmaCopy16(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, 0x1000);
+ gDma3Requests[gDma3RequestCursor].src += 0x1000;
+ gDma3Requests[gDma3RequestCursor].dest += 0x1000;
+ gDma3Requests[gDma3RequestCursor].size -= 0x1000;
+ }
+ DmaCopy16(3, gDma3Requests[gDma3RequestCursor].src, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size);
+ break;
+ case 4: // repeat a single 16-bit value across RAM
+ // _08000D88
+ while (gDma3Requests[gDma3RequestCursor].size > 0x1000)
+ {
+ DmaFill16(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, 0x1000);
+ gDma3Requests[gDma3RequestCursor].dest += 0x1000;
+ gDma3Requests[gDma3RequestCursor].size -= 0x1000;
+ }
+ DmaFill16(3, gDma3Requests[gDma3RequestCursor].value, gDma3Requests[gDma3RequestCursor].dest, gDma3Requests[gDma3RequestCursor].size);
+ break;
+ }
+ gDma3Requests[gDma3RequestCursor].src = 0;
+ gDma3Requests[gDma3RequestCursor].dest = 0;
+ gDma3Requests[gDma3RequestCursor].size = 0;
+ gDma3Requests[gDma3RequestCursor].mode = 0;
+ gDma3Requests[gDma3RequestCursor].value = 0;
+ gDma3RequestCursor++;
+
+ if (gDma3RequestCursor >= 128) // loop back to the first DMA request
+ gDma3RequestCursor = 0;
+ }
+}
+#else
+__attribute__((naked))
+void ProcessDma3Requests(void)
+{
+ asm(".syntax unified\n\
+ push {r4-r7,lr}\n\
+ mov r7, r10\n\
+ mov r6, r9\n\
+ mov r5, r8\n\
+ push {r5-r7}\n\
+ sub sp, 0xC\n\
+ ldr r0, =gDma3ManagerLocked\n\
+ ldrb r0, [r0]\n\
+ cmp r0, 0\n\
+ beq _08000C06\n\
+ b _08000E46\n\
+_08000C06:\n\
+ movs r0, 0\n\
+ str r0, [sp, 0x8]\n\
+ ldr r1, =gDma3Requests\n\
+ ldr r2, =gDma3RequestCursor\n\
+ ldrb r0, [r2]\n\
+ lsls r0, 4\n\
+ adds r0, r1\n\
+ ldrh r0, [r0, 0x8]\n\
+ mov r12, r2\n\
+ cmp r0, 0\n\
+ bne _08000C1E\n\
+ b _08000E46\n\
+_08000C1E:\n\
+ mov r8, r1\n\
+ adds r1, 0x4\n\
+ mov r10, r1\n\
+ movs r6, 0x80\n\
+ lsls r6, 5\n\
+ ldr r7, =0x040000D4 @REG_DMA3\n\
+ movs r2, 0\n\
+ mov r9, r2\n\
+_08000C2E:\n\
+ mov r3, r12 @ gDma3RequestCursor\n\
+ ldrb r0, [r3]\n\
+ lsls r5, r0, 4\n\
+ mov r0, r8 @ gDma3Requests\n\
+ adds r1, r5, r0 @ gDma3Requests[gDma3RequestCursor]\n\
+ ldrh r0, [r1, 0x8] @ gDma3Requests[gDma3RequestCursor].size\n\
+ ldr r2, [sp, 0x8]\n\
+ adds r0, r2, r0\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ str r0, [sp, 0x8]\n\
+ movs r0, 0xA0\n\
+ lsls r0, 8\n\
+ ldr r3, [sp, 0x8]\n\
+ cmp r3, r0\n\
+ bls _08000C50\n\
+ b _08000E46\n\
+_08000C50:\n\
+ ldr r0, =0x04000006 @REG_VCOUNT\n\
+ ldrb r0, [r0]\n\
+ cmp r0, 0xE0\n\
+ bls _08000C5A\n\
+ b _08000E46\n\
+_08000C5A:\n\
+ ldrh r0, [r1, 0xA]\n\
+ cmp r0, 0x2\n\
+ beq _08000CD0\n\
+ cmp r0, 0x2\n\
+ bgt _08000C80\n\
+ cmp r0, 0x1\n\
+ beq _08000C8C\n\
+ b _08000DF0\n\
+ .pool\n\
+_08000C80:\n\
+ cmp r0, 0x3\n\
+ beq _08000D3C\n\
+ cmp r0, 0x4\n\
+ bne _08000C8A\n\
+ b _08000D88\n\
+_08000C8A:\n\
+ b _08000DF0\n\
+_08000C8C:\n\
+ ldr r3, [r1]\n\
+ mov r2, r10\n\
+ adds r0, r5, r2\n\
+ ldr r2, [r0]\n\
+ ldrh r1, [r1, 0x8]\n\
+ cmp r1, r6\n\
+ bhi _08000CA6\n\
+ str r3, [r7]\n\
+ str r2, [r7, 0x4]\n\
+ lsrs r0, r1, 2\n\
+ movs r1, 0x84\n\
+ lsls r1, 24\n\
+ b _08000DAA\n\
+_08000CA6:\n\
+ ldr r4, =0x040000D4 @REG_DMA3\n\
+ str r3, [r4]\n\
+ str r2, [r4, 0x4]\n\
+ ldr r0, =0x84000400\n\
+ str r0, [r4, 0x8]\n\
+ ldr r0, [r4, 0x8]\n\
+ adds r3, r6\n\
+ adds r2, r6\n\
+ subs r1, r6\n\
+ cmp r1, r6\n\
+ bhi _08000CA6\n\
+ str r3, [r4]\n\
+ str r2, [r4, 0x4]\n\
+ lsrs r0, r1, 2\n\
+ movs r1, 0x84\n\
+ lsls r1, 24\n\
+ b _08000D76\n\
+ .pool\n\
+_08000CD0:\n\
+ mov r3, r10\n\
+ adds r0, r5, r3\n\
+ ldr r4, [r0]\n\
+ ldrh r1, [r1, 0x8]\n\
+ cmp r1, r6\n\
+ bhi _08000CF4\n\
+ mov r0, r8\n\
+ adds r0, 0xC\n\
+ adds r0, r5, r0\n\
+ ldr r0, [r0]\n\
+ str r0, [sp]\n\
+ mov r5, sp\n\
+ str r5, [r7]\n\
+ str r4, [r7, 0x4]\n\
+ lsrs r0, r1, 2\n\
+ movs r1, 0x85\n\
+ lsls r1, 24\n\
+ b _08000DAA\n\
+_08000CF4:\n\
+ mov r2, r12\n\
+ ldrb r0, [r2]\n\
+ lsls r0, 4\n\
+ mov r5, r8\n\
+ adds r5, 0xC\n\
+ adds r0, r5\n\
+ ldr r0, [r0]\n\
+ str r0, [sp]\n\
+ ldr r3, =0x040000D4 @REG_DMA3\n\
+ mov r0, sp\n\
+ str r0, [r3]\n\
+ str r4, [r3, 0x4]\n\
+ ldr r0, =0x85000400\n\
+ str r0, [r3, 0x8]\n\
+ ldr r0, [r3, 0x8]\n\
+ adds r4, r6\n\
+ subs r1, r6\n\
+ cmp r1, r6\n\
+ bhi _08000CF4\n\
+ ldrb r0, [r2]\n\
+ lsls r0, 4\n\
+ adds r0, r5\n\
+ ldr r0, [r0]\n\
+ str r0, [sp]\n\
+ mov r2, sp\n\
+ str r2, [r3]\n\
+ str r4, [r3, 0x4]\n\
+ lsrs r0, r1, 2\n\
+ movs r1, 0x85\n\
+ lsls r1, 24\n\
+ b _08000DEA\n\
+ .pool\n\
+_08000D3C:\n\
+ ldr r3, [r1]\n\
+ mov r2, r10\n\
+ adds r0, r5, r2\n\
+ ldr r2, [r0]\n\
+ ldrh r1, [r1, 0x8]\n\
+ cmp r1, r6\n\
+ bhi _08000D56\n\
+ str r3, [r7]\n\
+ str r2, [r7, 0x4]\n\
+ lsrs r0, r1, 1\n\
+ movs r1, 0x80\n\
+ lsls r1, 24\n\
+ b _08000DAA\n\
+_08000D56:\n\
+ ldr r4, =0x040000D4 @REG_DMA3\n\
+ str r3, [r4]\n\
+ str r2, [r4, 0x4]\n\
+ ldr r0, =0x80000800\n\
+ str r0, [r4, 0x8]\n\
+ ldr r0, [r4, 0x8]\n\
+ adds r3, r6\n\
+ adds r2, r6\n\
+ subs r1, r6\n\
+ cmp r1, r6\n\
+ bhi _08000D56\n\
+ str r3, [r4]\n\
+ str r2, [r4, 0x4]\n\
+ lsrs r0, r1, 1\n\
+ movs r1, 0x80\n\
+ lsls r1, 24\n\
+_08000D76:\n\
+ orrs r0, r1\n\
+ str r0, [r4, 0x8]\n\
+ ldr r0, [r4, 0x8]\n\
+ b _08000DF0\n\
+ .pool\n\
+_08000D88:\n\
+ mov r3, r10\n\
+ adds r0, r5, r3\n\
+ ldr r2, [r0]\n\
+ ldrh r4, [r1, 0x8]\n\
+ add r1, sp, 0x4\n\
+ cmp r4, r6\n\
+ bhi _08000DB2\n\
+ mov r0, r8\n\
+ adds r0, 0xC\n\
+ adds r0, r5, r0\n\
+ ldr r0, [r0]\n\
+ strh r0, [r1]\n\
+ str r1, [r7]\n\
+ str r2, [r7, 0x4]\n\
+ lsrs r0, r4, 1\n\
+ movs r1, 0x81\n\
+ lsls r1, 24\n\
+_08000DAA:\n\
+ orrs r0, r1\n\
+ str r0, [r7, 0x8]\n\
+ ldr r0, [r7, 0x8]\n\
+ b _08000DF0\n\
+_08000DB2:\n\
+ mov r5, r12\n\
+ ldrb r0, [r5]\n\
+ lsls r0, 4\n\
+ ldr r3, =gUnknown_0300001C\n\
+ adds r0, r3\n\
+ ldr r0, [r0]\n\
+ strh r0, [r1]\n\
+ ldr r3, =0x040000D4 @REG_DMA3\n\
+ str r1, [r3]\n\
+ str r2, [r3, 0x4]\n\
+ ldr r0, =0x81000800\n\
+ str r0, [r3, 0x8]\n\
+ ldr r0, [r3, 0x8]\n\
+ adds r2, r6\n\
+ subs r4, r6\n\
+ cmp r4, r6\n\
+ bhi _08000DB2\n\
+ ldrb r0, [r5]\n\
+ lsls r0, 4\n\
+ ldr r5, =gUnknown_0300001C\n\
+ adds r0, r5\n\
+ ldr r0, [r0]\n\
+ strh r0, [r1]\n\
+ str r1, [r3]\n\
+ str r2, [r3, 0x4]\n\
+ lsrs r0, r4, 1\n\
+ movs r1, 0x81\n\
+ lsls r1, 24\n\
+_08000DEA:\n\
+ orrs r0, r1\n\
+ str r0, [r3, 0x8]\n\
+ ldr r0, [r3, 0x8]\n\
+_08000DF0:\n\
+ ldr r1, =gDma3Requests\n\
+ mov r3, r12\n\
+ ldrb r0, [r3]\n\
+ lsls r0, 4\n\
+ adds r0, r1\n\
+ mov r2, r9\n\
+ str r2, [r0]\n\
+ ldrb r0, [r3]\n\
+ lsls r0, 4\n\
+ add r0, r10\n\
+ str r2, [r0]\n\
+ ldrb r0, [r3]\n\
+ lsls r0, 4\n\
+ adds r0, r1\n\
+ movs r4, 0\n\
+ strh r2, [r0, 0x8]\n\
+ ldrb r0, [r3]\n\
+ lsls r0, 4\n\
+ adds r0, r1\n\
+ mov r5, r9\n\
+ strh r5, [r0, 0xA]\n\
+ ldrb r0, [r3]\n\
+ lsls r0, 4\n\
+ adds r1, 0xC\n\
+ adds r0, r1\n\
+ mov r1, r9\n\
+ str r1, [r0]\n\
+ ldrb r0, [r3]\n\
+ adds r0, 0x1\n\
+ strb r0, [r3]\n\
+ lsls r0, 24\n\
+ cmp r0, 0\n\
+ bge _08000E34\n\
+ strb r4, [r3]\n\
+_08000E34:\n\
+ mov r2, r12\n\
+ ldrb r0, [r2]\n\
+ lsls r0, 4\n\
+ ldr r3, =gDma3Requests\n\
+ adds r0, r3\n\
+ ldrh r0, [r0, 0x8]\n\
+ cmp r0, 0\n\
+ beq _08000E46\n\
+ b _08000C2E\n\
+_08000E46:\n\
+ add sp, 0xC\n\
+ pop {r3-r5}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ mov r10, r5\n\
+ pop {r4-r7}\n\
+ pop {r0}\n\
+ bx r0\n\
+ .pool\n\
+ .syntax divided");
+}
+#endif
+
+int RequestDma3Copy(void *src, void *dest, u16 size, u8 mode)
+{
+ int cursor;
+ int var = 0;
+
+ gDma3ManagerLocked = 1;
+
+ cursor = gDma3RequestCursor;
+ while(1)
+ {
+ if(!gDma3Requests[cursor].size) // an empty copy was found and the current cursor will be returned.
+ {
+ gDma3Requests[cursor].src = src;
+ gDma3Requests[cursor].dest = dest;
+ gDma3Requests[cursor].size = size;
+
+ if(mode == 1)
+ gDma3Requests[cursor].mode = mode;
+ else
+ gDma3Requests[cursor].mode = 3;
+
+ gDma3ManagerLocked = FALSE;
+ return (s16)cursor;
+ }
+ if(++cursor >= 0x80) // loop back to start.
+ {
+ cursor = 0;
+ }
+ if(++var >= 0x80) // max checks were made. all resulted in failure.
+ {
+ break;
+ }
+ }
+ gDma3ManagerLocked = FALSE;
+ return -1;
+}
+
+int RequestDma3Fill(s32 value, void *dest, u16 size, u8 mode)
+{
+ int cursor;
+ int var = 0;
+
+ cursor = gDma3RequestCursor;
+ gDma3ManagerLocked = 1;
+
+ while(1)
+ {
+ if(!gDma3Requests[cursor].size)
+ {
+ gDma3Requests[cursor].dest = dest;
+ gDma3Requests[cursor].size = size;
+ gDma3Requests[cursor].mode = mode;
+ gDma3Requests[cursor].value = value;
+
+ if(mode == 1)
+ gDma3Requests[cursor].mode = 2;
+ else
+ gDma3Requests[cursor].mode = 4;
+
+ gDma3ManagerLocked = FALSE;
+ return (s16)cursor;
+ }
+ if(++cursor >= 0x80) // loop back to start.
+ {
+ cursor = 0;
+ }
+ if(++var >= 0x80) // max checks were made. all resulted in failure.
+ {
+ break;
+ }
+ }
+ gDma3ManagerLocked = FALSE;
+ return -1;
+}
+
+int CheckForSpaceForDma3Request(s16 index)
+{
+ int current = 0;
+
+ if (index == -1)
+ {
+ for (; current < 0x80; current ++)
+ if (gDma3Requests[current].size)
+ return -1;
+
+ return 0;
+ }
+
+ if (gDma3Requests[index].size)
+ return -1;
+
+ return 0;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 000000000..a88c378eb
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,418 @@
+#include "global.h"
+#include "main.h"
+#include "gba/flash_internal.h"
+#include "gba/m4a_internal.h"
+
+extern u16 GetGpuReg(u8);
+extern void SetGpuReg(u8, u16);
+extern void LinkVSync(void);
+extern void sub_800E174(void);
+extern void sub_800B9B8(void);
+
+extern struct SoundInfo gSoundInfo;
+extern u32 gFlashMemoryPresent;
+extern u32 IntrMain[];
+extern u8 gHeap[];
+extern struct SaveBlock2 gUnknown_02024A54;
+extern char *gUnknown_03005D94;
+extern char gUnknown_02029808[];
+extern u32 gBattleTypeFlags;
+extern u8 gUnknown_03002748;
+extern u32 *gUnknown_0203CF5C;
+
+void Timer3Intr(void);
+bool8 HandleLinkConnection(void);
+void c2_copyright_1(void);
+
+static void VBlankIntr(void);
+static void HBlankIntr(void);
+static void VCountIntr(void);
+static void SerialIntr(void);
+static void IntrDummy(void);
+
+const u8 gGameVersion = VERSION_EMERALD;
+
+const u8 gGameLanguage = GAME_LANGUAGE; // English
+
+const char BuildDateTime[] = "2005 02 21 11:10";
+
+const IntrFunc gIntrTableTemplate[] =
+{
+ VCountIntr, // V-count interrupt
+ SerialIntr, // Serial interrupt
+ Timer3Intr, // Timer 3 interrupt
+ HBlankIntr, // H-blank interrupt
+ VBlankIntr, // V-blank interrupt
+ IntrDummy, // Timer 0 interrupt
+ IntrDummy, // Timer 1 interrupt
+ IntrDummy, // Timer 2 interrupt
+ IntrDummy, // DMA 0 interrupt
+ IntrDummy, // DMA 1 interrupt
+ IntrDummy, // DMA 2 interrupt
+ IntrDummy, // DMA 3 interrupt
+ IntrDummy, // Key interrupt
+ IntrDummy, // Game Pak interrupt
+};
+
+#define INTR_COUNT ((int)(sizeof(gIntrTableTemplate)/sizeof(IntrFunc)))
+
+extern u16 gUnknown_03000000;
+
+extern u16 gKeyRepeatStartDelay;
+extern u8 gUnknown_030022B4;
+extern struct Main gMain;
+extern u16 gKeyRepeatContinueDelay;
+extern u8 gSoftResetDisabled;
+extern IntrFunc gIntrTable[INTR_COUNT];
+extern bool8 gLinkVSyncDisabled;
+extern u32 IntrMain_Buffer[0x200];
+extern u8 gPcmDmaCounter;
+
+extern u16 gTrainerId;
+
+EWRAM_DATA void (**gFlashTimerIntrFunc)(void) = NULL;
+
+static void UpdateLinkAndCallCallbacks(void);
+static void InitMainCallbacks(void);
+static void CallCallbacks(void);
+static void SeedRngWithRtc(void);
+static void ReadKeys(void);
+void InitIntrHandlers(void);
+static void WaitForVBlank(void);
+
+#define B_START_SELECT (B_BUTTON | START_BUTTON | SELECT_BUTTON)
+
+void AgbMain()
+{
+ RegisterRamReset(RESET_ALL);
+ *(vu16 *)BG_PLTT = 0x7FFF;
+ InitGpuRegManager();
+ REG_WAITCNT = WAITCNT_PREFETCH_ENABLE | WAITCNT_WS0_S_1 | WAITCNT_WS0_N_3;
+ InitKeys();
+ InitIntrHandlers();
+ m4aSoundInit();
+ EnableVCountIntrAtLine150();
+ sub_800E6D0();
+ RtcInit();
+ CheckForFlashMemory();
+ InitMainCallbacks();
+ InitMapMusic();
+ ClearDma3Requests();
+ ResetBgs();
+ SetDefaultFontsPointer();
+ InitHeap(gHeap, 0x1C000);
+
+ gSoftResetDisabled = FALSE;
+
+ if (gFlashMemoryPresent != TRUE)
+ SetMainCallback2(NULL);
+
+ gUnknown_030022B4 = 0;
+ gUnknown_03000000 = 0xFC0;
+
+ for (;;)
+ {
+ ReadKeys();
+
+ if (gSoftResetDisabled == FALSE
+ && (gMain.heldKeysRaw & A_BUTTON)
+ && (gMain.heldKeysRaw & B_START_SELECT) == B_START_SELECT)
+ {
+ rfu_REQ_stopMode();
+ rfu_waitREQComplete();
+ DoSoftReset();
+ }
+
+ if (sub_8087634() == 1)
+ {
+ gUnknown_030022B4 = 1;
+ UpdateLinkAndCallCallbacks();
+ gUnknown_030022B4 = 0;
+ }
+ else
+ {
+ gUnknown_030022B4 = 0;
+ UpdateLinkAndCallCallbacks();
+
+ if (sub_80875C8() == 1)
+ {
+ gMain.newKeys = 0;
+ ClearObjectCopyRequests();
+ gUnknown_030022B4 = 1;
+ UpdateLinkAndCallCallbacks();
+ gUnknown_030022B4 = 0;
+ }
+ }
+
+ PlayTimeCounter_Update();
+ MapMusicMain();
+ WaitForVBlank();
+ }
+}
+
+static void UpdateLinkAndCallCallbacks(void)
+{
+ if (!HandleLinkConnection())
+ CallCallbacks();
+}
+
+static void InitMainCallbacks(void)
+{
+ gMain.vblankCounter1 = 0;
+ gUnknown_0203CF5C = NULL;
+ gMain.vblankCounter2 = 0;
+ gMain.callback1 = NULL;
+ SetMainCallback2(c2_copyright_1);
+ gSaveBlock2Ptr = &gUnknown_02024A54;
+ gUnknown_03005D94 = gUnknown_02029808;
+}
+
+static void CallCallbacks(void)
+{
+ if (gMain.callback1)
+ gMain.callback1();
+
+ if (gMain.callback2)
+ gMain.callback2();
+}
+
+void SetMainCallback2(MainCallback callback)
+{
+ gMain.callback2 = callback;
+ gMain.state = 0;
+}
+
+void StartTimer1(void)
+{
+ REG_TM1CNT_H = 0x80;
+}
+
+void SeedRngAndSetTrainerId(void)
+{
+ u16 val = REG_TM1CNT_L;
+ SeedRng(val);
+ REG_TM1CNT_H = 0;
+ gTrainerId = val;
+}
+
+u16 GetTrainerId(void)
+{
+ return gTrainerId;
+}
+
+void EnableVCountIntrAtLine150(void)
+{
+ u16 gpuReg = (GetGpuReg(REG_OFFSET_DISPSTAT) & 0xFF) | (150 << 8);
+ SetGpuReg(REG_OFFSET_DISPSTAT, gpuReg | DISPSTAT_VCOUNT_INTR);
+ EnableInterrupts(INTR_FLAG_VCOUNT);
+}
+
+void InitKeys(void)
+{
+ gKeyRepeatContinueDelay = 5;
+ gKeyRepeatStartDelay = 40;
+
+ gMain.heldKeys = 0;
+ gMain.newKeys = 0;
+ gMain.newAndRepeatedKeys = 0;
+ gMain.heldKeysRaw = 0;
+ gMain.newKeysRaw = 0;
+}
+
+static void ReadKeys(void)
+{
+ u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
+ gMain.newKeysRaw = keyInput & ~gMain.heldKeysRaw;
+ gMain.newKeys = gMain.newKeysRaw;
+ gMain.newAndRepeatedKeys = gMain.newKeysRaw;
+
+ // BUG: Key repeat won't work when pressing L using L=A button mode
+ // because it compares the raw key input with the remapped held keys.
+ // Note that newAndRepeatedKeys is never remapped either.
+
+ if (keyInput != 0 && gMain.heldKeys == keyInput)
+ {
+ gMain.keyRepeatCounter--;
+
+ if (gMain.keyRepeatCounter == 0)
+ {
+ gMain.newAndRepeatedKeys = keyInput;
+ gMain.keyRepeatCounter = gKeyRepeatContinueDelay;
+ }
+ }
+ else
+ {
+ // If there is no input or the input has changed, reset the counter.
+ gMain.keyRepeatCounter = gKeyRepeatStartDelay;
+ }
+
+ gMain.heldKeysRaw = keyInput;
+ gMain.heldKeys = gMain.heldKeysRaw;
+
+ // Remap L to A if the L=A option is enabled.
+ if (gSaveBlock2Ptr->optionsButtonMode == 2)
+ {
+ if (gMain.newKeys & L_BUTTON)
+ gMain.newKeys |= A_BUTTON;
+
+ if (gMain.heldKeys & L_BUTTON)
+ gMain.heldKeys |= A_BUTTON;
+ }
+
+ if (gMain.newKeys & gMain.watchedKeysMask)
+ gMain.watchedKeysPressed = TRUE;
+}
+
+void InitIntrHandlers(void)
+{
+ int i;
+
+ for (i = 0; i < INTR_COUNT; i++)
+ gIntrTable[i] = gIntrTableTemplate[i];
+
+ DmaCopy32(3, IntrMain, IntrMain_Buffer, sizeof(IntrMain_Buffer));
+
+ INTR_VECTOR = IntrMain_Buffer;
+
+ SetVBlankCallback(NULL);
+ SetHBlankCallback(NULL);
+ SetSerialCallback(NULL);
+
+ REG_IME = 1;
+
+ EnableInterrupts(0x1);
+}
+
+void SetVBlankCallback(IntrCallback callback)
+{
+ gMain.vblankCallback = callback;
+}
+
+void SetHBlankCallback(IntrCallback callback)
+{
+ gMain.hblankCallback = callback;
+}
+
+void SetVCountCallback(IntrCallback callback)
+{
+ gMain.vcountCallback = callback;
+}
+
+void RestoreSerialTimer3IntrHandlers(void)
+{
+ gIntrTable[1] = SerialIntr;
+ gIntrTable[2] = Timer3Intr;
+}
+
+void SetSerialCallback(IntrCallback callback)
+{
+ gMain.serialCallback = callback;
+}
+
+extern void CopyBufferedValuesToGpuRegs(void);
+extern void ProcessDma3Requests(void);
+
+static void VBlankIntr(void)
+{
+ if (gLinkVSyncDisabled != FALSE)
+ LinkVSync();
+ else if (gUnknown_03002748 == FALSE)
+ sub_800B9B8();
+
+ gMain.vblankCounter1++;
+
+ if (gUnknown_0203CF5C && *gUnknown_0203CF5C < 0xFFFFFFFF)
+ (*gUnknown_0203CF5C)++;
+
+ if (gMain.vblankCallback)
+ gMain.vblankCallback();
+
+ gMain.vblankCounter2++;
+
+ CopyBufferedValuesToGpuRegs();
+ ProcessDma3Requests();
+
+ gPcmDmaCounter = gSoundInfo.pcmDmaCounter;
+
+ m4aSoundMain();
+ sub_8033648();
+
+ if (!gMain.inBattle || (gBattleTypeFlags & 0x013F0102) == 0)
+ Random();
+
+ sub_800E174();
+
+ INTR_CHECK |= INTR_FLAG_VBLANK;
+ gMain.intrCheck |= INTR_FLAG_VBLANK;
+}
+
+void StartFlashMemoryTimer(void)
+{
+ SetFlashTimerIntr(2, gIntrTable + 0x7);
+}
+
+static void HBlankIntr(void)
+{
+ if (gMain.hblankCallback)
+ gMain.hblankCallback();
+
+ INTR_CHECK |= INTR_FLAG_HBLANK;
+ gMain.intrCheck |= INTR_FLAG_HBLANK;
+}
+
+static void VCountIntr(void)
+{
+ if (gMain.vcountCallback)
+ gMain.vcountCallback();
+
+ m4aSoundVSync();
+ INTR_CHECK |= INTR_FLAG_VCOUNT;
+ gMain.intrCheck |= INTR_FLAG_VCOUNT;
+}
+
+static void SerialIntr(void)
+{
+ if (gMain.serialCallback)
+ gMain.serialCallback();
+
+ INTR_CHECK |= INTR_FLAG_SERIAL;
+ gMain.intrCheck |= INTR_FLAG_SERIAL;
+}
+
+static void IntrDummy(void)
+{}
+
+static void WaitForVBlank(void)
+{
+ gMain.intrCheck &= ~INTR_FLAG_VBLANK;
+
+ while (!(gMain.intrCheck & INTR_FLAG_VBLANK))
+ ;
+}
+
+void sub_80008DC(u32 *var)
+{
+ gUnknown_0203CF5C = var;
+}
+
+void sub_80008E8(void)
+{
+ gUnknown_0203CF5C = NULL;
+}
+
+void DoSoftReset(void)
+{
+ REG_IME = 0;
+ m4aSoundVSyncOff();
+ remove_some_task();
+ DmaStop(1);
+ DmaStop(2);
+ DmaStop(3);
+ SiiRtcProtect();
+ SoftReset(RESET_ALL);
+}
+
+void ClearPokemonCrySongs(void)
+{
+ CpuFill16(0, gPokemonCrySongs, MAX_POKEMON_CRIES * sizeof(struct PokemonCrySong));
+}
diff --git a/src/multiboot.c b/src/multiboot.c
new file mode 100644
index 000000000..80291ff46
--- /dev/null
+++ b/src/multiboot.c
@@ -0,0 +1,470 @@
+#include "gba/gba.h"
+#include "multiboot.h"
+
+static u16 MultiBoot_required_data[MULTIBOOT_NCHILD];
+
+static int MultiBootSend(struct MultiBootParam *mp, u16 data);
+static int MultiBootHandShake(struct MultiBootParam *mp);
+static void MultiBootWaitCycles(u32 cycles);
+static void MultiBootWaitSendDone(void);
+
+void MultiBootInit(struct MultiBootParam *mp)
+{
+ mp->client_bit = 0;
+ mp->probe_count = 0;
+ mp->response_bit = 0;
+
+ mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
+ mp->sendflag = 0;
+
+ mp->handshake_timeout = 0;
+
+ REG_RCNT = 0;
+ REG_SIOCNT = SIO_MULTI_MODE | SIO_115200_BPS;
+ REG_SIODATA8 = 0;
+}
+
+int MultiBootMain(struct MultiBootParam *mp)
+{
+ int i;
+ int j;
+ int k;
+
+ if (MultiBootCheckComplete(mp))
+ {
+ return 0;
+ }
+
+ if (mp->check_wait > MULTIBOOT_CONNECTION_CHECK_WAIT)
+ {
+ mp->check_wait--;
+ return 0;
+ }
+
+output_burst:
+ if (mp->sendflag)
+ {
+ mp->sendflag = 0;
+
+ i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_ERROR | SIO_ID | SIO_MULTI_SD | SIO_MULTI_SI);
+ if (i != SIO_MULTI_SD)
+ {
+ MultiBootInit(mp);
+ return i ^ SIO_MULTI_SD;
+ }
+ }
+
+ if (mp->probe_count >= 0xe0)
+ {
+ i = MultiBootHandShake(mp);
+ if (i)
+ {
+ return i;
+ }
+
+ if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK
+ && mp->probe_count > 0xe1
+ && MultiBootCheckComplete(mp) == 0)
+ {
+ MultiBootWaitSendDone();
+ goto output_burst;
+ }
+
+ if (MultiBootCheckComplete(mp) == 0)
+ {
+ if (mp->handshake_timeout == 0)
+ {
+ MultiBootInit(mp);
+ return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
+ }
+ mp->handshake_timeout--;
+ }
+
+ return 0;
+ }
+
+ switch (mp->probe_count)
+ {
+ case 0:
+ k = 0x0e;
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ if (*(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2) != 0xffff)
+ {
+ break;
+ }
+ k >>= 1;
+ }
+
+ k &= 0x0e;
+ mp->response_bit = k;
+
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if (mp->client_bit & (1 << i))
+ {
+ if (j != ((MULTIBOOT_CLIENT_INFO << 8) | (1 << i)))
+ {
+ k = 0;
+ break;
+ }
+ }
+ }
+
+ mp->client_bit &= k;
+
+ if (k == 0)
+ {
+ mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT;
+ }
+
+ if (mp->check_wait)
+ {
+ mp->check_wait--;
+ }
+ else
+ {
+ if (mp->response_bit != mp->client_bit)
+ {
+ MultiBootStartProbe(mp);
+ goto case_1;
+ }
+ }
+
+ output_master_info:
+ return MultiBootSend(mp, (MULTIBOOT_MASTER_INFO << 8) | mp->client_bit);
+
+ case_1:
+ case 1:
+ mp->probe_target_bit = 0;
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if ((j >> 8) == MULTIBOOT_CLIENT_INFO)
+ {
+ MultiBoot_required_data[i - 1] = j;
+ j &= 0xff;
+ if (j == (1 << i))
+ {
+ mp->probe_target_bit |= j;
+ }
+ }
+ }
+
+ if (mp->response_bit != mp->probe_target_bit)
+ {
+ goto output_master_info;
+ }
+
+ mp->probe_count = 2;
+ return MultiBootSend(mp, (MULTIBOOT_MASTER_START_PROBE << 8) | mp->probe_target_bit);
+
+ case 2:
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ if (mp->probe_target_bit & (1 << i))
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if (j != MultiBoot_required_data[i - 1])
+ {
+ mp->probe_target_bit ^= 1 << i;
+ }
+ }
+ }
+ goto output_header;
+
+ case 0xd0:
+ k = 1;
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ mp->client_data[i - 1] = j;
+ if (mp->probe_target_bit & (1 << i))
+ {
+ if ((j >> 8) != MULTIBOOT_CLIENT_INFO
+ && (j >> 8) != MULTIBOOT_CLIENT_DLREADY)
+ {
+ MultiBootInit(mp);
+ return MULTIBOOT_ERROR_NO_DLREADY;
+ }
+ if (j == MultiBoot_required_data[i - 1])
+ {
+ k = 0;
+ }
+ }
+ }
+
+ if (k == 0)
+ {
+ return MultiBootSend(mp, (MULTIBOOT_MASTER_REQUEST_DLREADY << 8) | mp->palette_data);
+ }
+
+ mp->probe_count = 0xd1;
+
+ k = 0x11;
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ k += mp->client_data[i - 1];
+ }
+ mp->handshake_data = k;
+ return MultiBootSend(mp, (MULTIBOOT_MASTER_START_DL << 8) | (k & 0xff));
+
+ case 0xd1:
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if (mp->probe_target_bit & (1 << i))
+ {
+ if ((j >> 8) != MULTIBOOT_CLIENT_DLREADY)
+ {
+ MultiBootInit(mp);
+ return MULTIBOOT_ERROR_NO_DLREADY;
+ }
+ }
+ }
+
+ i = MultiBoot(mp);
+
+ if (i == 0)
+ {
+ mp->probe_count = 0xe0;
+ mp->handshake_timeout = MULTIBOOT_HANDSHAKE_TIMEOUT;
+ return 0;
+ }
+ MultiBootInit(mp);
+ mp->check_wait = MULTIBOOT_CONNECTION_CHECK_WAIT * 2;
+ return MULTIBOOT_ERROR_BOOT_FAILURE;
+
+ default:
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ if (mp->probe_target_bit & (1 << i))
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if ((j >> 8) != (MULTIBOOT_MASTER_START_PROBE + 1 - (mp->probe_count >> 1))
+ || ((j & 0xff) != (1 << i)))
+ {
+ mp->probe_target_bit ^= 1 << i;
+ }
+ }
+ }
+
+ if (mp->probe_count == 0xc4)
+ {
+ mp->client_bit = mp->probe_target_bit & 0x0e;
+ mp->probe_count = 0;
+ goto output_master_info;
+ }
+
+ output_header:
+ if (mp->probe_target_bit == 0)
+ {
+ MultiBootInit(mp);
+ return MULTIBOOT_ERROR_NO_PROBE_TARGET;
+ }
+
+ mp->probe_count += 2;
+ if (mp->probe_count == 0xc4)
+ {
+ goto output_master_info;
+ }
+ i = MultiBootSend(mp,
+ (mp->masterp[mp->probe_count - 4 + 1] << 8)
+ | mp->masterp[mp->probe_count - 4]);
+
+ if (i)
+ {
+ return i;
+ }
+ if (mp->server_type == MULTIBOOT_SERVER_TYPE_QUICK)
+ {
+ MultiBootWaitSendDone();
+ goto output_burst;
+ }
+ return 0;
+ }
+}
+
+static int MultiBootSend(struct MultiBootParam *mp, u16 data)
+{
+ int i;
+
+ i = REG_SIOCNT & (SIO_MULTI_BUSY | SIO_MULTI_SD | SIO_MULTI_SI);
+ if (i != SIO_MULTI_SD)
+ {
+ MultiBootInit(mp);
+ return i ^ SIO_MULTI_SD;
+ }
+
+ REG_SIODATA8 = data;
+ REG_SIOCNT = SIO_MULTI_MODE | SIO_START | SIO_115200_BPS;
+
+ mp->sendflag = 1;
+ return 0;
+}
+
+void MultiBootStartProbe(struct MultiBootParam *mp)
+{
+ if (mp->probe_count != 0)
+ {
+ MultiBootInit(mp);
+ return;
+ }
+ mp->check_wait = 0;
+ mp->client_bit = 0;
+ mp->probe_count = 1;
+}
+
+void MultiBootStartMaster(struct MultiBootParam *mp, u8 *srcp, int length, u8 palette_color, s8 palette_speed)
+{
+ int i = 0;
+
+ if (mp->probe_count != 0
+ || mp->client_bit == 0
+ || mp->check_wait != 0)
+ {
+ MultiBootInit(mp);
+ return;
+ }
+
+ mp->boot_srcp = srcp;
+ length = (length + 15) & ~15;
+ if (length < MULTIBOOT_SEND_SIZE_MIN || length > MULTIBOOT_SEND_SIZE_MAX)
+ {
+ MultiBootInit(mp);
+ return;
+ }
+
+ mp->boot_endp = srcp + length;
+
+ switch (palette_speed)
+ {
+ case -4:
+ case -3:
+ case -2:
+ case -1:
+ i = (palette_color << 3) | (3 - palette_speed);
+ break;
+ case 0:
+ i = 0x38 | palette_color;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ i = (palette_color << 3) | (palette_speed - 1);
+ break;
+ }
+
+ mp->palette_data = ((i & 0x3f) << 1) | 0x81;
+ mp->probe_count = 0xd0;
+}
+
+int MultiBootCheckComplete(struct MultiBootParam *mp)
+{
+ if (mp->probe_count == 0xe9)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int MultiBootHandShake(struct MultiBootParam *mp)
+{
+ int i, j;
+
+#define send_data (mp->system_work[0])
+#define must_data (mp->system_work[1])
+
+ switch (mp->probe_count)
+ {
+ case_0xe0:
+ case 0xe0:
+ mp->probe_count = 0xe1;
+ must_data = 0x0000;
+ send_data = 0x100000;
+ return MultiBootSend(mp, 0x0000);
+
+ default:
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if ((mp->client_bit & (1 << i))
+ && j != must_data)
+ {
+ goto case_0xe0;
+ }
+ }
+ mp->probe_count++;
+ must_data = send_data & 0xffff;
+ if (send_data == 0x0000)
+ {
+ must_data = mp->masterp[0xac] | (mp->masterp[0xad] << 8);
+ send_data = must_data << 5;
+ }
+ send_data >>= 5;
+ output_common:
+ return MultiBootSend(mp, send_data);
+
+ case 0xe7:
+ case 0xe8:
+ for (i = MULTIBOOT_NCHILD; i != 0; i--)
+ {
+ j = *(vu16 *)(REG_ADDR_SIOMULTI0 + i * 2);
+ if ((mp->client_bit & (1 << i)) && j != must_data)
+ {
+ MultiBootInit(mp);
+ return MULTIBOOT_ERROR_HANDSHAKE_FAILURE;
+ }
+ }
+
+ mp->probe_count++;
+ if (mp->probe_count == 0xe9)
+ {
+ return 0;
+ }
+
+ send_data = mp->masterp[0xae] | (mp->masterp[0xaf] << 8);
+ must_data = send_data;
+ goto output_common;
+ }
+
+#undef send_data
+#undef must_data
+}
+
+static void MultiBootWaitCycles(u32 cycles)
+{
+ asm("mov r2, pc");
+ asm("lsr r2, #24");
+ asm("mov r1, #12");
+ asm("cmp r2, #0x02");
+ asm("beq MultiBootWaitCyclesLoop");
+
+ asm("mov r1, #13");
+ asm("cmp r2, #0x08");
+ asm("beq MultiBootWaitCyclesLoop");
+
+ asm("mov r1, #4");
+
+ asm("MultiBootWaitCyclesLoop:");
+ asm("sub r0, r1");
+ asm("bgt MultiBootWaitCyclesLoop");
+}
+
+static void MultiBootWaitSendDone(void)
+{
+ int i;
+
+ for (i = 0; i < 31069; i++)
+ {
+ if ((REG_SIOCNT & SIO_START) == 0)
+ {
+ break;
+ }
+ }
+
+ MultiBootWaitCycles(600);
+}
diff --git a/src/rng.c b/src/rng.c
new file mode 100644
index 000000000..6f4f2ce55
--- /dev/null
+++ b/src/rng.c
@@ -0,0 +1,35 @@
+#include "global.h"
+#include "rng.h"
+
+// The number 1103515245 comes from the example implementation of rand and srand
+// in the ISO C standard.
+
+extern u32 gRngValue;
+extern u32 gRng2Value;
+
+EWRAM_DATA static u8 sUnknown = 0;
+EWRAM_DATA static u32 sRandCount = 0;
+
+u16 Random()
+{
+ gRngValue = 1103515245 * gRngValue + 24691;
+ sRandCount++;
+ return gRngValue >> 16;
+}
+
+void SeedRng(u16 seed)
+{
+ gRngValue = seed;
+ sUnknown = 0;
+}
+
+void SeedRng2(u16 seed)
+{
+ gRng2Value = seed;
+}
+
+u16 Random2(void)
+{
+ gRng2Value = 1103515245 * gRng2Value + 24691;
+ return gRng2Value >> 16;
+}
diff --git a/src/siirtc.c b/src/siirtc.c
new file mode 100644
index 000000000..965a068f1
--- /dev/null
+++ b/src/siirtc.c
@@ -0,0 +1,432 @@
+// Ruby/Sapphire/Emerald cartridges contain a Seiko Instruments Inc. (SII)
+// S-3511A real-time clock (RTC). This library ("SIIRTC_V001") is for
+// communicating with the RTC.
+
+#include "gba/gba.h"
+#include "siirtc.h"
+
+#define STATUS_INTFE 0x02 // frequency interrupt enable
+#define STATUS_INTME 0x08 // per-minute interrupt enable
+#define STATUS_INTAE 0x20 // alarm interrupt enable
+#define STATUS_24HOUR 0x40 // 0: 12-hour mode, 1: 24-hour mode
+#define STATUS_POWER 0x80 // power on or power failure occurred
+
+#define TEST_MODE 0x80 // flag in the "second" byte
+
+#define ALARM_AM 0x00
+#define ALARM_PM 0x80
+
+#define OFFSET_YEAR offsetof(struct SiiRtcInfo, year)
+#define OFFSET_MONTH offsetof(struct SiiRtcInfo, month)
+#define OFFSET_DAY offsetof(struct SiiRtcInfo, day)
+#define OFFSET_DAY_OF_WEEK offsetof(struct SiiRtcInfo, dayOfWeek)
+#define OFFSET_HOUR offsetof(struct SiiRtcInfo, hour)
+#define OFFSET_MINUTE offsetof(struct SiiRtcInfo, minute)
+#define OFFSET_SECOND offsetof(struct SiiRtcInfo, second)
+#define OFFSET_STATUS offsetof(struct SiiRtcInfo, status)
+#define OFFSET_ALARM_HOUR offsetof(struct SiiRtcInfo, alarmHour)
+#define OFFSET_ALARM_MINUTE offsetof(struct SiiRtcInfo, alarmMinute)
+
+#define INFO_BUF(info, index) (*((u8 *)(info) + (index)))
+
+#define DATETIME_BUF(info, index) INFO_BUF(info, OFFSET_YEAR + index)
+#define DATETIME_BUF_LEN (OFFSET_SECOND - OFFSET_YEAR + 1)
+
+#define TIME_BUF(info, index) INFO_BUF(info, OFFSET_HOUR + index)
+#define TIME_BUF_LEN (OFFSET_SECOND - OFFSET_HOUR + 1)
+
+#define WR 0 // command for writing data
+#define RD 1 // command for reading data
+
+#define CMD(n) (0x60 | (n << 1))
+
+#define CMD_RESET CMD(0)
+#define CMD_STATUS CMD(1)
+#define CMD_DATETIME CMD(2)
+#define CMD_TIME CMD(3)
+#define CMD_ALARM CMD(4)
+
+#define GPIO_PORT_DATA (*(vu16 *)0x80000C4)
+#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6)
+#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8)
+
+extern vu16 GPIOPortDirection;
+
+static u16 sDummy; // unused variable
+static bool8 sLocked;
+
+static int WriteCommand(u8 value);
+static int WriteData(u8 value);
+static u8 ReadData();
+static void EnableGpioPortRead();
+static void DisableGpioPortRead();
+
+static const char AgbLibRtcVersion[] = "SIIRTC_V001";
+
+void SiiRtcUnprotect()
+{
+ EnableGpioPortRead();
+ sLocked = FALSE;
+}
+
+void SiiRtcProtect()
+{
+ DisableGpioPortRead();
+ sLocked = TRUE;
+}
+
+u8 SiiRtcProbe()
+{
+ u8 errorCode;
+ struct SiiRtcInfo rtc;
+
+ if (!SiiRtcGetStatus(&rtc))
+ return 0;
+
+ errorCode = 0;
+
+ if ((rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == SIIRTCINFO_POWER
+ || (rtc.status & (SIIRTCINFO_POWER | SIIRTCINFO_24HOUR)) == 0)
+ {
+ // The RTC is in 12-hour mode. Reset it and switch to 24-hour mode.
+
+ // Note that the conditions are redundant and equivalent to simply
+ // "(rtc.status & SIIRTCINFO_24HOUR) == 0". It's possible that this
+ // was also intended to handle resetting the clock after power failure
+ // but a mistake was made.
+
+ if (!SiiRtcReset())
+ return 0;
+
+ errorCode++;
+ }
+
+ SiiRtcGetTime(&rtc);
+
+ if (rtc.second & TEST_MODE)
+ {
+ // The RTC is in test mode. Reset it to leave test mode.
+
+ if (!SiiRtcReset())
+ return (errorCode << 4) & 0xF0;
+
+ errorCode++;
+ }
+
+ return (errorCode << 4) | 1;
+}
+
+bool8 SiiRtcReset()
+{
+ u8 result;
+ struct SiiRtcInfo rtc;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_RESET | WR);
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ rtc.status = SIIRTCINFO_24HOUR;
+
+ result = SiiRtcSetStatus(&rtc);
+
+ return result;
+}
+
+bool8 SiiRtcGetStatus(struct SiiRtcInfo *rtc)
+{
+ u8 statusData;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_STATUS | RD);
+
+ GPIO_PORT_DIRECTION = 5;
+
+ statusData = ReadData();
+
+ rtc->status = (statusData & (STATUS_POWER | STATUS_24HOUR))
+ | ((statusData & STATUS_INTAE) >> 3)
+ | ((statusData & STATUS_INTME) >> 2)
+ | ((statusData & STATUS_INTFE) >> 1);
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcSetStatus(struct SiiRtcInfo *rtc)
+{
+ u8 statusData;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ statusData = STATUS_24HOUR
+ | ((rtc->status & SIIRTCINFO_INTAE) << 3)
+ | ((rtc->status & SIIRTCINFO_INTME) << 2)
+ | ((rtc->status & SIIRTCINFO_INTFE) << 1);
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_STATUS | WR);
+
+ WriteData(statusData);
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcGetDateTime(struct SiiRtcInfo *rtc)
+{
+ u8 i;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_DATETIME | RD);
+
+ GPIO_PORT_DIRECTION = 5;
+
+ for (i = 0; i < DATETIME_BUF_LEN; i++)
+ DATETIME_BUF(rtc, i) = ReadData();
+
+ INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcSetDateTime(struct SiiRtcInfo *rtc)
+{
+ u8 i;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_DATETIME | WR);
+
+ for (i = 0; i < DATETIME_BUF_LEN; i++)
+ WriteData(DATETIME_BUF(rtc, i));
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcGetTime(struct SiiRtcInfo *rtc)
+{
+ u8 i;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_TIME | RD);
+
+ GPIO_PORT_DIRECTION = 5;
+
+ for (i = 0; i < TIME_BUF_LEN; i++)
+ TIME_BUF(rtc, i) = ReadData();
+
+ INFO_BUF(rtc, OFFSET_HOUR) &= 0x7F;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcSetTime(struct SiiRtcInfo *rtc)
+{
+ u8 i;
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIO_PORT_DIRECTION = 7;
+
+ WriteCommand(CMD_TIME | WR);
+
+ for (i = 0; i < TIME_BUF_LEN; i++)
+ WriteData(TIME_BUF(rtc, i));
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+bool8 SiiRtcSetAlarm(struct SiiRtcInfo *rtc)
+{
+ u8 i;
+ u8 alarmData[2];
+
+ if (sLocked == TRUE)
+ return FALSE;
+
+ sLocked = TRUE;
+
+ // Decode BCD.
+ alarmData[0] = (rtc->alarmHour & 0xF) + 10 * ((rtc->alarmHour >> 4) & 0xF);
+
+ // The AM/PM flag must be set correctly even in 24-hour mode.
+
+ if (alarmData[0] < 12)
+ alarmData[0] = rtc->alarmHour | ALARM_AM;
+ else
+ alarmData[0] = rtc->alarmHour | ALARM_PM;
+
+ alarmData[1] = rtc->alarmMinute;
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 5;
+
+ GPIOPortDirection = 7; // Why is this the only instance that uses a symbol?
+
+ WriteCommand(CMD_ALARM | WR);
+
+ for (i = 0; i < 2; i++)
+ WriteData(alarmData[i]);
+
+ GPIO_PORT_DATA = 1;
+ GPIO_PORT_DATA = 1;
+
+ sLocked = FALSE;
+
+ return TRUE;
+}
+
+static int WriteCommand(u8 value)
+{
+ u8 i;
+ u8 temp;
+
+ for (i = 0; i < 8; i++)
+ {
+ temp = ((value >> (7 - i)) & 1);
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 5;
+ }
+
+ // control reaches end of non-void function
+}
+
+static int WriteData(u8 value)
+{
+ u8 i;
+ u8 temp;
+
+ for (i = 0; i < 8; i++)
+ {
+ temp = ((value >> i) & 1);
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 4;
+ GPIO_PORT_DATA = (temp << 1) | 5;
+ }
+
+ // control reaches end of non-void function
+}
+
+static u8 ReadData()
+{
+ u8 i;
+ u8 temp;
+ u8 value;
+
+ for (i = 0; i < 8; i++)
+ {
+ GPIO_PORT_DATA = 4;
+ GPIO_PORT_DATA = 4;
+ GPIO_PORT_DATA = 4;
+ GPIO_PORT_DATA = 4;
+ GPIO_PORT_DATA = 4;
+ GPIO_PORT_DATA = 5;
+
+ temp = ((GPIO_PORT_DATA & 2) >> 1);
+ value = (value >> 1) | (temp << 7); // UB: accessing uninitialized var
+ }
+
+ return value;
+}
+
+static void EnableGpioPortRead()
+{
+ GPIO_PORT_READ_ENABLE = 1;
+}
+
+static void DisableGpioPortRead()
+{
+ GPIO_PORT_READ_ENABLE = 0;
+}
diff --git a/src/string_util.c b/src/string_util.c
new file mode 100644
index 000000000..009072dca
--- /dev/null
+++ b/src/string_util.c
@@ -0,0 +1,780 @@
+#include "global.h"
+#include "string_util.h"
+#include "text.h"
+
+EWRAM_DATA u8 gUnknownStringVar[16] = {0};
+
+static const u8 sDigits[] = __("0123456789ABCDEF");
+
+static const s32 sPowersOfTen[] =
+{
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+};
+
+extern u8 gExpandedPlaceholder_Empty[];
+extern u8 gExpandedPlaceholder_Kun[];
+extern u8 gExpandedPlaceholder_Chan[];
+extern u8 gExpandedPlaceholder_Sapphire[];
+extern u8 gExpandedPlaceholder_Ruby[];
+extern u8 gExpandedPlaceholder_Emerald[];
+extern u8 gExpandedPlaceholder_Aqua[];
+extern u8 gExpandedPlaceholder_Magma[];
+extern u8 gExpandedPlaceholder_Archie[];
+extern u8 gExpandedPlaceholder_Maxie[];
+extern u8 gExpandedPlaceholder_Kyogre[];
+extern u8 gExpandedPlaceholder_Groudon[];
+extern u8 gExpandedPlaceholder_Brendan[];
+extern u8 gExpandedPlaceholder_May[];
+
+u8 *StringCopy10(u8 *dest, const u8 *src)
+{
+ u8 i;
+ u32 limit = 10;
+
+ for (i = 0; i < limit; i++)
+ {
+ dest[i] = src[i];
+
+ if (dest[i] == EOS)
+ return &dest[i];
+ }
+
+ dest[i] = EOS;
+ return &dest[i];
+}
+
+u8 *StringGetEnd10(u8 *str)
+{
+ u8 i;
+ u32 limit = 10;
+
+ for (i = 0; i < limit; i++)
+ if (str[i] == EOS)
+ return &str[i];
+
+ str[i] = EOS;
+ return &str[i];
+}
+
+u8 *StringCopy7(u8 *dest, const u8 *src)
+{
+ s32 i;
+ s32 limit = 7;
+
+ for (i = 0; i < limit; i++)
+ {
+ dest[i] = src[i];
+
+ if (dest[i] == EOS)
+ return &dest[i];
+ }
+
+ dest[i] = EOS;
+ return &dest[i];
+}
+
+u8 *StringCopy(u8 *dest, const u8 *src)
+{
+ while (*src != EOS)
+ {
+ *dest = *src;
+ dest++;
+ src++;
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *StringAppend(u8 *dest, const u8 *src)
+{
+ while (*dest != EOS)
+ dest++;
+
+ return StringCopy(dest, src);
+}
+
+u8 *StringCopyN(u8 *dest, const u8 *src, u8 n)
+{
+ u16 i;
+
+ for (i = 0; i < n; i++)
+ dest[i] = src[i];
+
+ return &dest[n];
+}
+
+u8 *StringAppendN(u8 *dest, const u8 *src, u8 n)
+{
+ while (*dest != EOS)
+ dest++;
+
+ return StringCopyN(dest, src, n);
+}
+
+u16 StringLength(const u8 *str)
+{
+ u16 length = 0;
+
+ while (str[length] != EOS)
+ length++;
+
+ return length;
+}
+
+s32 StringCompare(const u8 *str1, const u8 *str2)
+{
+ while (*str1 == *str2)
+ {
+ if (*str1 == EOS)
+ return 0;
+ str1++;
+ str2++;
+ }
+
+ return *str1 - *str2;
+}
+
+s32 StringCompareN(const u8 *str1, const u8 *str2, u32 n)
+{
+ while (*str1 == *str2)
+ {
+ if (*str1 == EOS)
+ return 0;
+ str1++;
+ str2++;
+ if (--n == 0)
+ return 0;
+ }
+
+ return *str1 - *str2;
+}
+
+bool8 IsStringLengthAtLeast(const u8 *str, s32 n)
+{
+ u8 i;
+
+ for (i = 0; i < n; i++)
+ if (str[i] && str[i] != EOS)
+ return TRUE;
+
+ return FALSE;
+}
+
+u8 *ConvertIntToDecimalStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n)
+{
+ enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
+ s32 powerOfTen;
+ s32 largestPowerOfTen = sPowersOfTen[n - 1];
+
+ state = WAITING_FOR_NONZERO_DIGIT;
+
+ if (mode == STR_CONV_MODE_RIGHT_ALIGN)
+ state = WRITING_SPACES;
+
+ if (mode == STR_CONV_MODE_LEADING_ZEROS)
+ state = WRITING_DIGITS;
+
+ for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10)
+ {
+ u8 c;
+ u16 digit = value / powerOfTen;
+ s32 temp = value - (powerOfTen * digit);
+
+ if (state == WRITING_DIGITS)
+ {
+ u8 *out = dest++;
+
+ if (digit <= 9)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (digit != 0 || powerOfTen == 1)
+ {
+ u8 *out;
+ state = WRITING_DIGITS;
+ out = dest++;
+
+ if (digit <= 9)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (state == WRITING_SPACES)
+ {
+ *dest++ = 0x77;
+ }
+
+ value = temp;
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *ConvertUIntToDecimalStringN(u8 *dest, u32 value, enum StringConvertMode mode, u8 n)
+{
+ enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
+ s32 powerOfTen;
+ s32 largestPowerOfTen = sPowersOfTen[n - 1];
+
+ state = WAITING_FOR_NONZERO_DIGIT;
+
+ if (mode == STR_CONV_MODE_RIGHT_ALIGN)
+ state = WRITING_SPACES;
+
+ if (mode == STR_CONV_MODE_LEADING_ZEROS)
+ state = WRITING_DIGITS;
+
+ for (powerOfTen = largestPowerOfTen; powerOfTen > 0; powerOfTen /= 10)
+ {
+ u8 c;
+ u16 digit = value / powerOfTen;
+ u32 temp = value - (powerOfTen * digit);
+
+ if (state == WRITING_DIGITS)
+ {
+ u8 *out = dest++;
+
+ if (digit <= 9)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (digit != 0 || powerOfTen == 1)
+ {
+ u8 *out;
+ state = WRITING_DIGITS;
+ out = dest++;
+
+ if (digit <= 9)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (state == WRITING_SPACES)
+ {
+ *dest++ = 0x77;
+ }
+
+ value = temp;
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *ConvertIntToHexStringN(u8 *dest, s32 value, enum StringConvertMode mode, u8 n)
+{
+ enum { WAITING_FOR_NONZERO_DIGIT, WRITING_DIGITS, WRITING_SPACES } state;
+ u8 i;
+ s32 powerOfSixteen;
+ s32 largestPowerOfSixteen = 1;
+
+ for (i = 1; i < n; i++)
+ largestPowerOfSixteen *= 16;
+
+ state = WAITING_FOR_NONZERO_DIGIT;
+
+ if (mode == STR_CONV_MODE_RIGHT_ALIGN)
+ state = WRITING_SPACES;
+
+ if (mode == STR_CONV_MODE_LEADING_ZEROS)
+ state = WRITING_DIGITS;
+
+ for (powerOfSixteen = largestPowerOfSixteen; powerOfSixteen > 0; powerOfSixteen /= 16)
+ {
+ u8 c;
+ u32 digit = value / powerOfSixteen;
+ s32 temp = value % powerOfSixteen;
+
+ if (state == WRITING_DIGITS)
+ {
+ char *out = dest++;
+
+ if (digit <= 0xF)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (digit != 0 || powerOfSixteen == 1)
+ {
+ char *out;
+ state = WRITING_DIGITS;
+ out = dest++;
+
+ if (digit <= 0xF)
+ c = sDigits[digit];
+ else
+ c = CHAR_QUESTION_MARK;
+
+ *out = c;
+ }
+ else if (state == WRITING_SPACES)
+ {
+ *dest++ = 0x77;
+ }
+
+ value = temp;
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *StringExpandPlaceholders(u8 *dest, const u8 *src)
+{
+ for (;;)
+ {
+ u8 c = *src++;
+ u8 placeholderId;
+ u8 *expandedString;
+
+ switch (c)
+ {
+ case PLACEHOLDER_BEGIN:
+ placeholderId = *src++;
+ expandedString = GetExpandedPlaceholder(placeholderId);
+ dest = StringExpandPlaceholders(dest, expandedString);
+ break;
+ case EXT_CTRL_CODE_BEGIN:
+ *dest++ = c;
+ c = *src++;
+ *dest++ = c;
+
+ switch (c)
+ {
+ case 0x07:
+ case 0x09:
+ case 0x0F:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ break;
+ case 0x04:
+ *dest++ = *src++;
+ case 0x0B:
+ *dest++ = *src++;
+ default:
+ *dest++ = *src++;
+ }
+ break;
+ case EOS:
+ *dest = EOS;
+ return dest;
+ case 0xFA:
+ case 0xFB:
+ case 0xFE:
+ default:
+ *dest++ = c;
+ }
+ }
+}
+
+u8 *StringBraille(u8 *dest, const u8 *src)
+{
+ u8 setBrailleFont[] = { 0xFC, 0x06, 0x06, 0xFF };
+ u8 gotoLine2[] = { 0xFE, 0xFC, 0x0E, 0x02, 0xFF };
+
+ dest = StringCopy(dest, setBrailleFont);
+
+ for (;;)
+ {
+ u8 c = *src++;
+
+ switch (c)
+ {
+ case EOS:
+ *dest = c;
+ return dest;
+ case 0xFE:
+ dest = StringCopy(dest, gotoLine2);
+ break;
+ default:
+ *dest++ = c;
+ *dest++ = c + 0x40;
+ break;
+ }
+ }
+}
+
+static u8 *ExpandPlaceholder_UnknownStringVar(void)
+{
+ return gUnknownStringVar;
+}
+
+static u8 *ExpandPlaceholder_PlayerName(void)
+{
+ return gSaveBlock2Ptr->playerName;
+}
+
+static u8 *ExpandPlaceholder_StringVar1(void)
+{
+ return gStringVar1;
+}
+
+static u8 *ExpandPlaceholder_StringVar2(void)
+{
+ return gStringVar2;
+}
+
+static u8 *ExpandPlaceholder_StringVar3(void)
+{
+ return gStringVar3;
+}
+
+static u8 *ExpandPlaceholder_KunChan(void)
+{
+ if (gSaveBlock2Ptr->playerGender == MALE)
+ return gExpandedPlaceholder_Kun;
+ else
+ return gExpandedPlaceholder_Chan;
+}
+
+static u8 *ExpandPlaceholder_RivalName(void)
+{
+ if (gSaveBlock2Ptr->playerGender == MALE)
+ return gExpandedPlaceholder_May;
+ else
+ return gExpandedPlaceholder_Brendan;
+}
+
+static u8 *ExpandPlaceholder_Version(void)
+{
+ return gExpandedPlaceholder_Emerald;
+}
+
+static u8 *ExpandPlaceholder_Aqua(void)
+{
+ return gExpandedPlaceholder_Aqua;
+}
+
+static u8 *ExpandPlaceholder_Magma(void)
+{
+ return gExpandedPlaceholder_Magma;
+}
+
+static u8 *ExpandPlaceholder_Archie(void)
+{
+ return gExpandedPlaceholder_Archie;
+}
+
+static u8 *ExpandPlaceholder_Maxie(void)
+{
+ return gExpandedPlaceholder_Maxie;
+}
+
+static u8 *ExpandPlaceholder_Kyogre(void)
+{
+ return gExpandedPlaceholder_Kyogre;
+}
+
+static u8 *ExpandPlaceholder_Groudon(void)
+{
+ return gExpandedPlaceholder_Groudon;
+}
+
+u8 *GetExpandedPlaceholder(u32 id)
+{
+ typedef u8 *(*ExpandPlaceholderFunc)(void);
+
+ static const ExpandPlaceholderFunc funcs[] =
+ {
+ ExpandPlaceholder_UnknownStringVar,
+ ExpandPlaceholder_PlayerName,
+ ExpandPlaceholder_StringVar1,
+ ExpandPlaceholder_StringVar2,
+ ExpandPlaceholder_StringVar3,
+ ExpandPlaceholder_KunChan,
+ ExpandPlaceholder_RivalName,
+ ExpandPlaceholder_Version,
+ ExpandPlaceholder_Aqua,
+ ExpandPlaceholder_Magma,
+ ExpandPlaceholder_Archie,
+ ExpandPlaceholder_Maxie,
+ ExpandPlaceholder_Kyogre,
+ ExpandPlaceholder_Groudon,
+ };
+
+ if (id >= ARRAY_COUNT(funcs))
+ return gExpandedPlaceholder_Empty;
+ else
+ return funcs[id]();
+}
+
+u8 *StringFill(u8 *dest, u8 c, u16 n)
+{
+ u16 i;
+
+ for (i = 0; i < n; i++)
+ *dest++ = c;
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *StringCopyPadded(u8 *dest, const u8 *src, u8 c, u16 n)
+{
+ while (*src != EOS)
+ {
+ *dest++ = *src++;
+
+ if (n)
+ n--;
+ }
+
+ n--;
+
+ while (n != (u16)-1)
+ {
+ *dest++ = c;
+ n--;
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u8 *StringFillWithTerminator(u8 *dest, u16 n)
+{
+ return StringFill(dest, EOS, n);
+}
+
+u8 *StringCopyN_Multibyte(u8 *dest, u8 *src, u32 n)
+{
+ u32 i;
+
+ for (i = n - 1; i != (u32)-1; i--)
+ {
+ if (*src == EOS)
+ {
+ break;
+ }
+ else
+ {
+ *dest++ = *src++;
+ if (*(src - 1) == 0xF9)
+ *dest++ = *src++;
+ }
+ }
+
+ *dest = EOS;
+ return dest;
+}
+
+u32 StringLength_Multibyte(u8 *str)
+{
+ u32 length = 0;
+
+ while (*str != EOS)
+ {
+ if (*str == 0xF9)
+ str++;
+ str++;
+ length++;
+ }
+
+ return length;
+}
+
+u8 *WriteColorChangeControlCode(u8 *dest, u32 colorType, u8 color)
+{
+ *dest = 0xFC;
+ dest++;
+
+ switch (colorType)
+ {
+ case 0:
+ *dest = 1;
+ dest++;
+ break;
+ case 1:
+ *dest = 3;
+ dest++;
+ break;
+ case 2:
+ *dest = 2;
+ dest++;
+ break;
+ }
+
+ *dest = color;
+ dest++;
+ *dest = EOS;
+ return dest;
+}
+
+bool32 sub_8009228(u8 *str)
+{
+ while (*str != EOS)
+ {
+ if (*str <= 0xA0)
+ if (*str != 0)
+ return TRUE;
+ str++;
+ }
+
+ return FALSE;
+}
+
+bool32 sub_800924C(u8 *str, s32 n)
+{
+ s32 i;
+
+ for (i = 0; *str != EOS && i < n; i++)
+ {
+ if (*str <= 0xA0)
+ if (*str != 0)
+ return TRUE;
+ str++;
+ }
+
+ return FALSE;
+}
+
+u8 GetExtCtrlCodeLength(u8 code)
+{
+ static const u8 lengths[] =
+ {
+ 1,
+ 2,
+ 2,
+ 2,
+ 4,
+ 2,
+ 2,
+ 1,
+ 2,
+ 1,
+ 1,
+ 3,
+ 2,
+ 2,
+ 2,
+ 1,
+ 3,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ };
+
+ u8 length = 0;
+ if (code < ARRAY_COUNT(lengths))
+ length = lengths[code];
+ return length;
+}
+
+static const u8 *SkipExtCtrlCode(const u8 *s)
+{
+ while (*s == 0xFC)
+ {
+ s++;
+ s += GetExtCtrlCodeLength(*s);
+ }
+
+ return s;
+}
+
+s32 StringCompareWithoutExtCtrlCodes(const u8 *str1, const u8 *str2)
+{
+ s32 retVal = 0;
+
+ while (1)
+ {
+ str1 = SkipExtCtrlCode(str1);
+ str2 = SkipExtCtrlCode(str2);
+
+ if (*str1 > *str2)
+ break;
+
+ if (*str1 < *str2)
+ {
+ retVal = -1;
+ if (*str2 == 0xFF)
+ retVal = 1;
+ }
+
+ if (*str1 == 0xFF)
+ return retVal;
+
+ str1++;
+ str2++;
+ }
+
+ retVal = 1;
+
+ if (*str1 == 0xFF)
+ retVal = -1;
+
+ return retVal;
+}
+
+void ConvertInternationalString(u8 *s, u8 language)
+{
+ if (language == LANGUAGE_JAPANESE)
+ {
+ u8 i;
+
+ StripExtCtrlCodes(s);
+ i = StringLength(s);
+ s[i++] = 0xFC;
+ s[i++] = 22;
+ s[i++] = 0xFF;
+
+ i--;
+
+ while (i != (u8)-1)
+ {
+ s[i + 2] = s[i];
+ i--;
+ }
+
+ s[0] = 0xFC;
+ s[1] = 21;
+ }
+}
+
+void StripExtCtrlCodes(u8 *str)
+{
+ u16 srcIndex = 0;
+ u16 destIndex = 0;
+ while (str[srcIndex] != 0xFF)
+ {
+ if (str[srcIndex] == 0xFC)
+ {
+ srcIndex++;
+ srcIndex += GetExtCtrlCodeLength(str[srcIndex]);
+ }
+ else
+ {
+ str[destIndex++] = str[srcIndex++];
+ }
+ }
+ str[destIndex] = 0xFF;
+}
diff --git a/src/task.c b/src/task.c
index f84c83e80..bf8abd794 100644
--- a/src/task.c
+++ b/src/task.c
@@ -49,7 +49,6 @@ u8 CreateTask(TaskFunc func, u8 priority)
return 0;
}
-#ifdef NONMATCHING
static void InsertTask(u8 newTaskId)
{
u8 taskId = FindFirstActiveTask();
@@ -62,7 +61,7 @@ static void InsertTask(u8 newTaskId)
return;
}
- for (;;)
+ while (1)
{
if (gTasks[newTaskId].priority < gTasks[taskId].priority)
{
@@ -70,112 +69,22 @@ static void InsertTask(u8 newTaskId)
// so we insert the new task before it.
gTasks[newTaskId].prev = gTasks[taskId].prev;
gTasks[newTaskId].next = taskId;
-
if (gTasks[taskId].prev != HEAD_SENTINEL)
gTasks[gTasks[taskId].prev].next = newTaskId;
-
gTasks[taskId].prev = newTaskId;
return;
}
-
- if (gTasks[taskId].next != TAIL_SENTINEL)
- taskId = gTasks[taskId].next;
- else
- break;
+ if (gTasks[taskId].next == TAIL_SENTINEL)
+ {
+ // We've reached the end.
+ gTasks[newTaskId].prev = taskId;
+ gTasks[newTaskId].next = gTasks[taskId].next;
+ gTasks[taskId].next = newTaskId;
+ return;
+ }
+ taskId = gTasks[taskId].next;
}
-
- // We've reached the end.
- gTasks[newTaskId].prev = taskId;
- gTasks[newTaskId].next = gTasks[taskId].next;
- gTasks[taskId].next = newTaskId;
-}
-#else
-__attribute__((naked))
-static void InsertTask(u8 newTaskId)
-{
- asm("push {r4, r5, r6, r7, lr}\n\
- mov r7, r8\n\
- push {r7}\n\
- lsl r0, r0, #24\n\
- lsr r4, r0, #24\n\
- bl FindFirstActiveTask\n\
- lsl r0, r0, #24\n\
- lsr r1, r0, #24\n\
- cmp r1, #16\n\
- bne .LInsertTask_foundActiveTask\n\
- ldr r1, .LInsertTask_gTasks1\n\
- lsl r0, r4, #2\n\
- add r0, r0, r4\n\
- lsl r0, r0, #3\n\
- add r0, r0, r1\n\
- mov r1, #254\n\
- strb r1, [r0, #5]\n\
- mov r1, #255\n\
- strb r1, [r0, #6]\n\
- b .LInsertTask_done\n\
- .align 2, 0\n\
-.LInsertTask_gTasks1:\n\
- .word gTasks\n\
-.LInsertTask_foundActiveTask:\n\
- ldr r6, .LInsertTask_gTasks2\n\
- lsl r0, r4, #2\n\
- mov r12, r0\n\
- mov r8, r6\n\
- add r0, r0, r4\n\
- lsl r0, r0, #3\n\
- add r2, r0, r6\n\
-.LInsertTask_loop:\n\
- lsl r0, r1, #2\n\
- add r0, r0, r1\n\
- lsl r5, r0, #3\n\
- mov r7, r8\n\
- add r3, r5, r7\n\
- ldrb r0, [r2, #7]\n\
- ldrb r7, [r3, #7]\n\
- cmp r0, r7\n\
- bcs .LInsertTask_next\n\
- ldrb r0, [r3, #5]\n\
- strb r0, [r2, #5]\n\
- strb r1, [r2, #6]\n\
- ldrb r0, [r3, #5]\n\
- cmp r0, #254\n\
- beq .LInsertTask_insertAtHead\n\
- add r1, r0, #0\n\
- lsl r0, r1, #2\n\
- add r0, r0, r1\n\
- lsl r0, r0, #3\n\
- add r0, r0, r8\n\
- strb r4, [r0, #6]\n\
-.LInsertTask_insertAtHead:\n\
- strb r4, [r3, #5]\n\
- b .LInsertTask_done\n\
- .align 2, 0\n\
-.LInsertTask_gTasks2:\n\
- .word gTasks\n\
-.LInsertTask_next:\n\
- ldrb r0, [r3, #6]\n\
- cmp r0, #255\n\
- beq .LInsertTask_insertAtTail\n\
- add r1, r0, #0\n\
- b .LInsertTask_loop\n\
-.LInsertTask_insertAtTail:\n\
- mov r2, r12\n\
- add r0, r2, r4\n\
- lsl r0, r0, #3\n\
- add r0, r0, r6\n\
- strb r1, [r0, #5]\n\
- add r2, r5, r6\n\
- ldrb r1, [r2, #6]\n\
- strb r1, [r0, #6]\n\
- strb r4, [r2, #6]\n\
-.LInsertTask_done:\n\
- pop {r3}\n\
- mov r8, r3\n\
- pop {r4, r5, r6, r7}\n\
- pop {r0}\n\
- bx r0\n");
}
-#endif // NONMATCHING
void DestroyTask(u8 taskId)
{