mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 03:34:15 +01:00
0d5b00aaf2
# Conflicts: # README.md # data/battle_ai_scripts.s # data/battle_scripts_1.s # data/maps/BattleFrontier_Lounge7/scripts.inc # include/battle_anim.h # include/battle_message.h # include/constants/items.h # src/battle_ai_script_commands.c # src/battle_ai_switch_items.c # src/battle_anim_throw.c # src/battle_anim_utility_funcs.c # src/battle_controller_link_opponent.c # src/battle_controller_link_partner.c # src/battle_controller_opponent.c # src/battle_controller_player.c # src/battle_controller_player_partner.c # src/battle_controller_recorded_opponent.c # src/battle_controller_recorded_player.c # src/battle_controller_wally.c # src/battle_gfx_sfx_util.c # src/battle_message.c # src/battle_script_commands.c # src/battle_util.c # src/data/party_menu.h # src/data/pokemon/tmhm_learnsets.h # src/daycare.c # src/field_specials.c # src/hall_of_fame.c # src/party_menu.c # src/pokemon.c # src/tv.c
876 lines
28 KiB
C
Executable File
876 lines
28 KiB
C
Executable File
#include "global.h"
|
|
#include "malloc.h"
|
|
#include "decompress.h"
|
|
#include "ereader_helpers.h"
|
|
#include "link.h"
|
|
#include "main.h"
|
|
#include "union_room.h"
|
|
#include "save.h"
|
|
#include "sprite.h"
|
|
#include "task.h"
|
|
#include "util.h"
|
|
#include "trainer_hill.h"
|
|
#include "constants/trainers.h"
|
|
#include "constants/moves.h"
|
|
#include "constants/items.h"
|
|
#include "constants/trainer_hill.h"
|
|
|
|
// Save data using TryWriteSpecialSaveSector is allowed to exceed SECTOR_DATA_SIZE (up to the counter field)
|
|
STATIC_ASSERT(sizeof(struct TrainerHillChallenge) <= SECTOR_COUNTER_OFFSET, TrainerHillChallengeFreeSpace);
|
|
|
|
struct SendRecvMgr
|
|
{
|
|
bool8 isParent;
|
|
u8 state; // EREADER_XFR_STATE_*
|
|
u8 xferState; // EREADER_XFER_*
|
|
u8 checksumResult; // EREADER_CHECKSUM_*
|
|
u8 cancellationReason; // EREADER_CANCEL_*
|
|
u32 *data; // Payload source or destination
|
|
int cursor; // Index of the next word
|
|
int size; // Last word index
|
|
int checksum;
|
|
};
|
|
|
|
static void GetKeyInput(void);
|
|
static u16 DetermineSendRecvState(u8);
|
|
static void EnableSio(void);
|
|
static void DisableTm3(void);
|
|
static void SetUpTransferManager(size_t, const void *, void *);
|
|
static void StartTm3(void);
|
|
|
|
static struct SendRecvMgr sSendRecvMgr;
|
|
static u16 sJoyNewOrRepeated;
|
|
static u16 sJoyNew;
|
|
static u16 sSendRecvStatus;
|
|
static u16 sCounter1;
|
|
static u32 sCounter2;
|
|
static u16 sSavedIme;
|
|
static u16 sSavedIe;
|
|
static u16 sSavedTm3Cnt;
|
|
static u16 sSavedSioCnt;
|
|
static u16 sSavedRCnt;
|
|
|
|
static const struct TrainerHillTrainer sTrainerHillTrainerTemplates_JP[] = {
|
|
[0] = {
|
|
.name = __("マキエ$$$$$ "),
|
|
.facilityClass = FACILITY_CLASS_HEX_MANIAC,
|
|
.unused = 0x1,
|
|
.speechBefore = { EC_WORD_PREPOSTEROUS, EC_WORD_CASE, EC_WORD_THERE, EC_WORD_TO_HER, EC_WORD_CHALLENGE, EC_WORD_JOKING },
|
|
.speechWin = { EC_WORD_HERS, EC_WORD_TRUMP_CARD, EC_MOVE2(SECRET_POWER), EC_WORD_USING, EC_WORD_WON, EC_WORD_EXCL_EXCL },
|
|
.speechLose = { EC_WORD_TO_HER, EC_WORD_WIN, EC_WORD_JOKING, EC_WORD_HIGHS, EC_WORD_SCARY, EC_WORD_ELLIPSIS_EXCL },
|
|
.speechAfter = { EC_WORD_IGNORANT, EC_WORD_SO, EC_WORD_TODAY, EC_WORD_NIGHTTIME, EC_WORD_YOU_RE, EC_WORD_ELLIPSIS_ELLIPSIS_ELLIPSIS },
|
|
.mons = {
|
|
[0] = DUMMY_HILL_MON,
|
|
[1] = DUMMY_HILL_MON,
|
|
[2] = DUMMY_HILL_MON,
|
|
[3] = {
|
|
.species = SPECIES_SWALOT,
|
|
.heldItem = ITEM_SHELL_BELL,
|
|
.moves = { MOVE_SLUDGE_BOMB, MOVE_SHADOW_BALL, MOVE_PAIN_SPLIT, MOVE_YAWN },
|
|
.hpEV = 55,
|
|
.attackEV = 255,
|
|
.defenseEV = 100,
|
|
.speedEV = 0,
|
|
.spAttackEV = 0,
|
|
.spDefenseEV = 100,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 1,
|
|
.personality = 0x80,
|
|
.nickname = __("マルノーム$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[4] = {
|
|
.species = SPECIES_DUSTOX,
|
|
.heldItem = ITEM_BRIGHT_POWDER,
|
|
.moves = { MOVE_SILVER_WIND, MOVE_SLUDGE_BOMB, MOVE_SHADOW_BALL, MOVE_GIGA_DRAIN },
|
|
.hpEV = 0,
|
|
.attackEV = 255,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x6,
|
|
.nickname = __("ドクケイル$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[5] = {
|
|
.species = SPECIES_RELICANTH,
|
|
.heldItem = ITEM_QUICK_CLAW,
|
|
.moves = { MOVE_ANCIENT_POWER, MOVE_SURF, MOVE_EARTHQUAKE, MOVE_AMNESIA },
|
|
.hpEV = 100,
|
|
.attackEV = 0,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 155,
|
|
.spDefenseEV = 255,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x2f,
|
|
.nickname = __("ジーランス$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
}
|
|
},
|
|
[1] = {
|
|
.name = __("ハルヒト$$$$ "),
|
|
.facilityClass = FACILITY_CLASS_CAMPER,
|
|
.unused = 0x1,
|
|
.speechBefore = { EC_MOVE2(BOUNCE), EC_WORD_AS_MUCH_AS, EC_EMPTY_WORD, EC_WORD_THEY_RE, EC_WORD_STRONG, EC_WORD_EXCL },
|
|
.speechWin = { EC_MOVE(FLY), EC_WORD_AS_MUCH_AS, EC_EMPTY_WORD, EC_WORD_THEY_RE, EC_WORD_HAPPY, EC_WORD_EXCL },
|
|
.speechLose = { EC_MOVE2(MINIMIZE), EC_WORD_AS_MUCH_AS, EC_EMPTY_WORD, EC_WORD_THEY_RE, EC_WORD_SAD, EC_WORD_EXCL },
|
|
.speechAfter = { EC_MOVE(BITE), EC_WORD_AS_MUCH_AS, EC_EMPTY_WORD, EC_WORD_THEY_RE, EC_WORD_ANGRY, EC_WORD_EXCL },
|
|
.mons = {
|
|
[0] = DUMMY_HILL_MON,
|
|
[1] = DUMMY_HILL_MON,
|
|
[2] = DUMMY_HILL_MON,
|
|
[3] = {
|
|
.species = SPECIES_CACTURNE,
|
|
.heldItem = ITEM_QUICK_CLAW,
|
|
.moves = { MOVE_GIGA_DRAIN, MOVE_FEINT_ATTACK, MOVE_THUNDER_PUNCH, MOVE_GROWTH },
|
|
.hpEV = 55,
|
|
.attackEV = 0,
|
|
.defenseEV = 100,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 100,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x8c,
|
|
.nickname = __("ノクタス$$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[4] = {
|
|
.species = SPECIES_SWELLOW,
|
|
.heldItem = ITEM_BRIGHT_POWDER,
|
|
.moves = { MOVE_FACADE, MOVE_AERIAL_ACE, MOVE_QUICK_ATTACK, MOVE_DOUBLE_TEAM },
|
|
.hpEV = 255,
|
|
.attackEV = 255,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 0,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x80,
|
|
.nickname = __("オオスバメ$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[5] = {
|
|
.species = SPECIES_WHISCASH,
|
|
.heldItem = ITEM_CHESTO_BERRY,
|
|
.moves = { MOVE_SURF, MOVE_EARTHQUAKE, MOVE_AMNESIA, MOVE_REST },
|
|
.hpEV = 0,
|
|
.attackEV = 255,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x0,
|
|
.nickname = __("ナマズン$$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
}
|
|
},
|
|
[2] = {
|
|
.name = __("メイコ$$$$$ "),
|
|
.facilityClass = FACILITY_CLASS_SCHOOL_KID_F,
|
|
.unused = 0x1,
|
|
.speechBefore = { EC_WORD_SHINE, EC_WORD_POKEMON, EC_WORD_RELEASE, EC_WORD_WAS, EC_MOVE2(FRUSTRATION), EC_WORD_WITHOUT },
|
|
.speechWin = { EC_WORD_SHINE, EC_WORD_POKEMON, EC_WORD_TO_HER, EC_MOVE2(PRESENT), EC_WORD_KNOWS, EC_WORD_WITHOUT },
|
|
.speechLose = { EC_WORD_THAT, EC_WORD_ABOVE, EC_WORD_LOST, EC_WORD_STORES, EC_WORD_JOKING, EC_WORD_ELLIPSIS_ELLIPSIS_ELLIPSIS },
|
|
.speechAfter = { EC_WORD_ENTERTAINING, EC_WORD_NONE, EC_WORD_HEY_QUES, EC_WORD_ALMOST, EC_WORD_EXCL, EC_EMPTY_WORD },
|
|
.mons = {
|
|
[0] = DUMMY_HILL_MON,
|
|
[1] = DUMMY_HILL_MON,
|
|
[2] = DUMMY_HILL_MON,
|
|
[3] = {
|
|
.species = SPECIES_DELCATTY,
|
|
.heldItem = ITEM_LUM_BERRY,
|
|
.moves = { MOVE_SING, MOVE_BODY_SLAM, MOVE_SHADOW_BALL, MOVE_IRON_TAIL },
|
|
.hpEV = 0,
|
|
.attackEV = 255,
|
|
.defenseEV = 0,
|
|
.speedEV = 255,
|
|
.spAttackEV = 0,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x3,
|
|
.nickname = __("エネコロロ$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[4] = {
|
|
.species = SPECIES_ROSELIA,
|
|
.heldItem = ITEM_LEFTOVERS,
|
|
.moves = { MOVE_GIGA_DRAIN, MOVE_GRASS_WHISTLE, MOVE_TOXIC, MOVE_LEECH_SEED },
|
|
.hpEV = 255,
|
|
.attackEV = 0,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 1,
|
|
.personality = 0x6,
|
|
.nickname = __("ロゼリア$$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[5] = {
|
|
.species = SPECIES_BEAUTIFLY,
|
|
.heldItem = ITEM_BRIGHT_POWDER,
|
|
.moves = { MOVE_SILVER_WIND, MOVE_AERIAL_ACE, MOVE_ATTRACT, MOVE_PSYCHIC },
|
|
.hpEV = 100,
|
|
.attackEV = 200,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 200,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x6,
|
|
.nickname = __("アゲハント$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
}
|
|
},
|
|
[3] = {
|
|
.name = __("ピエール$$$$ "),
|
|
.facilityClass = FACILITY_CLASS_GENTLEMAN,
|
|
.unused = 0x1,
|
|
.speechBefore = { EC_WORD_SHE_WAS, EC_WORD_NO_1, EC_WORD_STRONG, EC_WORD_UNCLE, EC_WORD_THERE, EC_WORD_EXCL },
|
|
.speechWin = { EC_WORD_HAHAHA, EC_WORD_TEACHER, EC_WORD_BECOMES, EC_WORD_GIVE, EC_WORD_IS_IT_QUES, EC_EMPTY_WORD },
|
|
.speechLose = { EC_WORD_OUTSIDE, EC_WORD_UNCLE, EC_WORD_SURPRISE, EC_WORD_THESE, EC_WORD_HEY_QUES, EC_WORD_ELLIPSIS_EXCL },
|
|
.speechAfter = { EC_WORD_HE_S, EC_WORD_NO_1, EC_WORD_STRONG, EC_WORD_CHILDREN, EC_WORD_CAN_T, EC_WORD_EXCL_EXCL },
|
|
.mons = {
|
|
[0] = DUMMY_HILL_MON,
|
|
[1] = DUMMY_HILL_MON,
|
|
[2] = DUMMY_HILL_MON,
|
|
[3] = {
|
|
.species = SPECIES_MAWILE,
|
|
.heldItem = ITEM_BRIGHT_POWDER,
|
|
.moves = { MOVE_CRUNCH, MOVE_FLAMETHROWER, MOVE_THUNDER_PUNCH, MOVE_COMET_PUNCH },
|
|
.hpEV = 0,
|
|
.attackEV = 0,
|
|
.defenseEV = 100,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 155,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 1,
|
|
.personality = 0x0,
|
|
.nickname = __("クチート$$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[4] = {
|
|
.species = SPECIES_SHARPEDO,
|
|
.heldItem = ITEM_SCOPE_LENS,
|
|
.moves = { MOVE_SURF, MOVE_CRUNCH, MOVE_DOUBLE_EDGE, MOVE_EARTHQUAKE },
|
|
.hpEV = 255,
|
|
.attackEV = 0,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x96,
|
|
.nickname = __("サメハダー$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
[5] = {
|
|
.species = SPECIES_BANETTE,
|
|
.heldItem = ITEM_LUM_BERRY,
|
|
.moves = { MOVE_PSYCHIC, MOVE_SHADOW_BALL, MOVE_THUNDERBOLT, MOVE_WILL_O_WISP },
|
|
.hpEV = 255,
|
|
.attackEV = 0,
|
|
.defenseEV = 0,
|
|
.speedEV = 0,
|
|
.spAttackEV = 255,
|
|
.spDefenseEV = 0,
|
|
.otId = TRAINER_HILL_OTID,
|
|
.hpIV = 5,
|
|
.attackIV = 5,
|
|
.defenseIV = 5,
|
|
.speedIV = 5,
|
|
.spAttackIV = 5,
|
|
.spDefenseIV = 5,
|
|
.abilityNum = 0,
|
|
.personality = 0x96,
|
|
.nickname = __("ジュペッタ$$$$$$"),
|
|
.friendship = MAX_FRIENDSHIP
|
|
},
|
|
}
|
|
},
|
|
};
|
|
|
|
static u8 GetTrainerHillUnkVal(void)
|
|
{
|
|
return (gSaveBlock1Ptr->trainerHill.unused + 1) % 256;
|
|
}
|
|
|
|
static bool32 ValidateTrainerChecksum(struct EReaderTrainerHillTrainer * hillTrainer)
|
|
{
|
|
int checksum = CalcByteArraySum((u8 *)hillTrainer, offsetof(typeof(*hillTrainer), checksum));
|
|
if (checksum != hillTrainer->checksum)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bool8 ValidateTrainerHillData(struct EReaderTrainerHillSet * hillSet)
|
|
{
|
|
u32 i;
|
|
u32 checksum;
|
|
int numTrainers = hillSet->numTrainers;
|
|
|
|
// Validate number of trainers
|
|
if (numTrainers < 1 || numTrainers > NUM_TRAINER_HILL_TRAINERS)
|
|
return FALSE;
|
|
|
|
// Validate trainers
|
|
for (i = 0; i < numTrainers; i++)
|
|
{
|
|
if (!ValidateTrainerChecksum(&hillSet->trainers[i]))
|
|
return FALSE;
|
|
}
|
|
|
|
// Validate checksum
|
|
checksum = CalcByteArraySum((u8 *)hillSet->trainers, numTrainers * sizeof(struct EReaderTrainerHillTrainer));
|
|
if (checksum != hillSet->checksum)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 ValidateTrainerHillChecksum(struct EReaderTrainerHillSet *hillSet)
|
|
{
|
|
u32 checksum;
|
|
int numTrainers = hillSet->numTrainers;
|
|
if (numTrainers < 1 || numTrainers > NUM_TRAINER_HILL_TRAINERS)
|
|
return FALSE;
|
|
|
|
checksum = CalcByteArraySum((u8 *)hillSet->trainers, sizeof(struct EReaderTrainerHillSet) - offsetof(struct EReaderTrainerHillSet, trainers));
|
|
if (checksum != hillSet->checksum)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 TryWriteTrainerHill_Internal(struct EReaderTrainerHillSet * hillSet, struct TrainerHillChallenge * challenge)
|
|
{
|
|
int i;
|
|
|
|
AGB_ASSERT_EX(hillSet->dummy == 0, "cereader_tool.c", 450);
|
|
AGB_ASSERT_EX(hillSet->id == 0, "cereader_tool.c", 452);
|
|
|
|
memset(challenge, 0, SECTOR_SIZE);
|
|
challenge->numTrainers = hillSet->numTrainers;
|
|
challenge->unused1 = GetTrainerHillUnkVal();
|
|
challenge->numFloors = (hillSet->numTrainers + 1) / HILL_TRAINERS_PER_FLOOR;
|
|
|
|
for (i = 0; i < hillSet->numTrainers; i++)
|
|
{
|
|
if (!(i & 1))
|
|
{
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].trainerNum1 = hillSet->trainers[i].trainerNum;
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].map = hillSet->trainers[i].map;
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].trainers[0] = hillSet->trainers[i].trainer;
|
|
}
|
|
else
|
|
{
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].trainerNum2 = hillSet->trainers[i].trainerNum;
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].trainers[1] = hillSet->trainers[i].trainer;
|
|
}
|
|
}
|
|
|
|
if (i & 1)
|
|
{
|
|
challenge->floors[i / HILL_TRAINERS_PER_FLOOR].trainers[1] = sTrainerHillTrainerTemplates_JP[i / HILL_TRAINERS_PER_FLOOR];
|
|
}
|
|
|
|
challenge->checksum = CalcByteArraySum((u8 *)challenge->floors, NUM_TRAINER_HILL_FLOORS * sizeof(struct TrainerHillFloor));
|
|
if (TryWriteSpecialSaveSector(SECTOR_ID_TRAINER_HILL, (u8 *)challenge) != SAVE_STATUS_OK)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bool32 TryWriteTrainerHill(struct EReaderTrainerHillSet * hillSet)
|
|
{
|
|
void *buffer = AllocZeroed(SECTOR_SIZE);
|
|
bool32 result = TryWriteTrainerHill_Internal(hillSet, buffer);
|
|
Free(buffer);
|
|
return result;
|
|
}
|
|
|
|
static bool32 TryReadTrainerHill_Internal(struct EReaderTrainerHillSet * dest, u8 *buffer)
|
|
{
|
|
if (TryReadSpecialSaveSector(SECTOR_ID_TRAINER_HILL, buffer) != SAVE_STATUS_OK)
|
|
return FALSE;
|
|
|
|
memcpy(dest, buffer, sizeof(struct EReaderTrainerHillSet));
|
|
if (!ValidateTrainerHillChecksum(dest))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool32 TryReadTrainerHill(struct EReaderTrainerHillSet * hillSet)
|
|
{
|
|
u8 *buffer = AllocZeroed(SECTOR_SIZE);
|
|
bool32 result = TryReadTrainerHill_Internal(hillSet, buffer);
|
|
Free(buffer);
|
|
return result;
|
|
}
|
|
|
|
bool32 ReadTrainerHillAndValidate(void)
|
|
{
|
|
struct EReaderTrainerHillSet *hillSet = AllocZeroed(SECTOR_SIZE);
|
|
bool32 result = TryReadTrainerHill(hillSet);
|
|
Free(hillSet);
|
|
return result;
|
|
}
|
|
|
|
int EReader_Send(int size, const void * src)
|
|
{
|
|
int result;
|
|
int sendStatus;
|
|
|
|
EReaderHelper_SaveRegsState();
|
|
while (1)
|
|
{
|
|
GetKeyInput();
|
|
if (sJoyNew & B_BUTTON)
|
|
gShouldAdvanceLinkState = 2;
|
|
|
|
sendStatus = EReaderHandleTransfer(1, size, src, NULL);
|
|
sSendRecvStatus = sendStatus;
|
|
if ((sSendRecvStatus & EREADER_XFER_MASK) == 0 && sSendRecvStatus & EREADER_CHECKSUM_OK_MASK)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
else if (sSendRecvStatus & EREADER_CANCEL_KEY_MASK)
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
else if (sSendRecvStatus & EREADER_CANCEL_TIMEOUT_MASK)
|
|
{
|
|
result = 2;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
gShouldAdvanceLinkState = 0;
|
|
VBlankIntrWait();
|
|
}
|
|
}
|
|
|
|
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
|
|
EReaderHelper_RestoreRegsState();
|
|
return result;
|
|
}
|
|
|
|
int EReader_Recv(void * dest)
|
|
{
|
|
int result;
|
|
int recvStatus;
|
|
|
|
EReaderHelper_SaveRegsState();
|
|
while (1)
|
|
{
|
|
GetKeyInput();
|
|
if (sJoyNew & B_BUTTON)
|
|
gShouldAdvanceLinkState = 2;
|
|
|
|
recvStatus = EReaderHandleTransfer(0, 0, NULL, dest);
|
|
sSendRecvStatus = recvStatus;
|
|
if ((sSendRecvStatus & EREADER_XFER_MASK) == 0 && sSendRecvStatus & EREADER_CHECKSUM_OK_MASK)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
else if (sSendRecvStatus & EREADER_CANCEL_KEY_MASK)
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
else if (sSendRecvStatus & EREADER_CANCEL_TIMEOUT_MASK)
|
|
{
|
|
result = 2;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
gShouldAdvanceLinkState = 0;
|
|
VBlankIntrWait();
|
|
}
|
|
}
|
|
|
|
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
|
|
EReaderHelper_RestoreRegsState();
|
|
return result;
|
|
}
|
|
|
|
static void CloseSerial(void)
|
|
{
|
|
REG_IME = 0;
|
|
REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
|
|
REG_IME = 1;
|
|
REG_SIOCNT = 0;
|
|
REG_TM3CNT_H = 0;
|
|
REG_IF = INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL;
|
|
}
|
|
|
|
static void OpenSerialMulti(void)
|
|
{
|
|
REG_IME = 0;
|
|
REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
|
|
REG_IME = 1;
|
|
REG_RCNT = 0;
|
|
REG_SIOCNT = SIO_MULTI_MODE;
|
|
REG_SIOCNT |= SIO_INTR_ENABLE | SIO_115200_BPS;
|
|
REG_IME = 0;
|
|
REG_IE |= INTR_FLAG_SERIAL;
|
|
REG_IME = 1;
|
|
|
|
if (sSendRecvMgr.state == 0)
|
|
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
|
|
}
|
|
|
|
static void OpenSerial32(void)
|
|
{
|
|
REG_RCNT = 0;
|
|
REG_SIOCNT = SIO_32BIT_MODE | SIO_INTR_ENABLE;
|
|
REG_SIOCNT |= SIO_MULTI_SD;
|
|
gShouldAdvanceLinkState = 0;
|
|
sCounter1 = 0;
|
|
sCounter2 = 0;
|
|
}
|
|
|
|
int EReaderHandleTransfer(u8 mode, size_t size, const void * data, void * recvBuffer)
|
|
{
|
|
switch (sSendRecvMgr.state)
|
|
{
|
|
case EREADER_XFR_STATE_INIT:
|
|
OpenSerialMulti();
|
|
sSendRecvMgr.xferState = EREADER_XFER_EXE;
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_HANDSHAKE;
|
|
break;
|
|
case EREADER_XFR_STATE_HANDSHAKE:
|
|
if (DetermineSendRecvState(mode))
|
|
EnableSio();
|
|
|
|
if (gShouldAdvanceLinkState == 2)
|
|
{
|
|
sSendRecvMgr.cancellationReason = EREADER_CANCEL_KEY;
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
|
|
}
|
|
break;
|
|
case EREADER_XFR_STATE_START:
|
|
OpenSerial32();
|
|
SetUpTransferManager(size, data, recvBuffer);
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_TRANSFER;
|
|
// fall through
|
|
case EREADER_XFR_STATE_TRANSFER:
|
|
if (gShouldAdvanceLinkState == 2)
|
|
{
|
|
sSendRecvMgr.cancellationReason = EREADER_CANCEL_KEY;
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
sCounter1++;
|
|
sCounter2++;
|
|
if (!sSendRecvMgr.isParent && sCounter2 > 60)
|
|
{
|
|
sSendRecvMgr.cancellationReason = EREADER_CANCEL_TIMEOUT;
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
|
|
}
|
|
|
|
if (sSendRecvMgr.xferState != EREADER_XFER_CHK)
|
|
{
|
|
if (sSendRecvMgr.isParent && sCounter1 > 2)
|
|
{
|
|
EnableSio();
|
|
sSendRecvMgr.xferState = EREADER_XFER_CHK;
|
|
}
|
|
else
|
|
{
|
|
EnableSio();
|
|
sSendRecvMgr.xferState = EREADER_XFER_CHK;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EREADER_XFR_STATE_TRANSFER_DONE:
|
|
OpenSerialMulti();
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_CHECKSUM;
|
|
break;
|
|
case EREADER_XFR_STATE_CHECKSUM:
|
|
if (sSendRecvMgr.isParent == TRUE && sCounter1 > 2)
|
|
EnableSio();
|
|
|
|
if (++sCounter1 > 60)
|
|
{
|
|
sSendRecvMgr.cancellationReason = EREADER_CANCEL_TIMEOUT;
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
|
|
}
|
|
break;
|
|
case EREADER_XFR_STATE_DONE:
|
|
if (sSendRecvMgr.xferState)
|
|
{
|
|
CloseSerial();
|
|
sSendRecvMgr.xferState = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (sSendRecvMgr.xferState << EREADER_XFER_SHIFT)
|
|
| (sSendRecvMgr.cancellationReason << EREADER_CANCEL_SHIFT)
|
|
| (sSendRecvMgr.checksumResult << EREADER_CHECKSUM_SHIFT);
|
|
}
|
|
|
|
static u16 DetermineSendRecvState(u8 mode)
|
|
{
|
|
bool16 resp;
|
|
if ((*(vu32 *)REG_ADDR_SIOCNT & (SIO_MULTI_SI | SIO_MULTI_SD)) == SIO_MULTI_SD && mode)
|
|
resp = sSendRecvMgr.isParent = TRUE;
|
|
else
|
|
resp = sSendRecvMgr.isParent = FALSE;
|
|
return resp;
|
|
}
|
|
|
|
static void SetUpTransferManager(size_t size, const void * data, void * recvBuffer)
|
|
{
|
|
if (sSendRecvMgr.isParent)
|
|
{
|
|
REG_SIOCNT |= SIO_38400_BPS;
|
|
sSendRecvMgr.data = (void *)data;
|
|
REG_SIODATA32 = size;
|
|
sSendRecvMgr.size = size / 4 + 1;
|
|
StartTm3();
|
|
}
|
|
else
|
|
{
|
|
REG_SIOCNT = REG_SIOCNT;
|
|
sSendRecvMgr.data = recvBuffer;
|
|
}
|
|
}
|
|
|
|
static void StartTm3(void)
|
|
{
|
|
REG_TM3CNT_L = -601;
|
|
REG_TM3CNT_H = TIMER_INTR_ENABLE;
|
|
REG_IME = 0;
|
|
REG_IE |= INTR_FLAG_TIMER3;
|
|
REG_IME = 1;
|
|
}
|
|
|
|
void EReaderHelper_Timer3Callback(void)
|
|
{
|
|
DisableTm3();
|
|
EnableSio();
|
|
}
|
|
|
|
void EReaderHelper_SerialCallback(void)
|
|
{
|
|
u16 i, cnt1, cnt2;
|
|
u32 recv32;
|
|
u16 recv[4];
|
|
|
|
switch (sSendRecvMgr.state)
|
|
{
|
|
case EREADER_XFR_STATE_HANDSHAKE:
|
|
REG_SIOMLT_SEND = EREADER_HANDSHAKE;
|
|
*(u64 *)recv = REG_SIOMLT_RECV;
|
|
for (i = 0, cnt1 = 0, cnt2 = 0; i < 4; i++)
|
|
{
|
|
if (recv[i] == EREADER_HANDSHAKE)
|
|
cnt1++;
|
|
else if (recv[i] != 0xFFFF)
|
|
cnt2++;
|
|
}
|
|
|
|
if (cnt1 == 2 && cnt2 == 0)
|
|
sSendRecvMgr.state = 2;
|
|
break;
|
|
case EREADER_XFR_STATE_TRANSFER:
|
|
recv32 = REG_SIODATA32;
|
|
// The first value sent by the EReader is the payload size
|
|
if (!sSendRecvMgr.cursor && !sSendRecvMgr.isParent)
|
|
sSendRecvMgr.size = recv32 / 4 + 1;
|
|
|
|
if (sSendRecvMgr.isParent == TRUE)
|
|
{
|
|
// Send mode
|
|
if (sSendRecvMgr.cursor < sSendRecvMgr.size)
|
|
{
|
|
REG_SIODATA32 = sSendRecvMgr.data[sSendRecvMgr.cursor];
|
|
sSendRecvMgr.checksum += sSendRecvMgr.data[sSendRecvMgr.cursor];
|
|
}
|
|
else
|
|
{
|
|
REG_SIODATA32 = sSendRecvMgr.checksum;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Receive mode
|
|
if (sSendRecvMgr.cursor > 0 && sSendRecvMgr.cursor < sSendRecvMgr.size + 1)
|
|
{
|
|
sSendRecvMgr.data[sSendRecvMgr.cursor - 1] = recv32;
|
|
sSendRecvMgr.checksum += recv32;
|
|
}
|
|
else if (sSendRecvMgr.cursor)
|
|
{
|
|
if (sSendRecvMgr.checksum == recv32)
|
|
sSendRecvMgr.checksumResult = EREADER_CHECKSUM_OK;
|
|
else
|
|
sSendRecvMgr.checksumResult = EREADER_CHECKSUM_ERR;
|
|
}
|
|
|
|
sCounter2 = 0;
|
|
}
|
|
|
|
if (++sSendRecvMgr.cursor < sSendRecvMgr.size + 2)
|
|
{
|
|
if (sSendRecvMgr.isParent)
|
|
REG_TM3CNT_H |= TIMER_ENABLE;
|
|
else
|
|
EnableSio();
|
|
}
|
|
else
|
|
{
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_TRANSFER_DONE;
|
|
sCounter1 = 0;
|
|
}
|
|
break;
|
|
case EREADER_XFR_STATE_CHECKSUM:
|
|
if (!sSendRecvMgr.isParent)
|
|
REG_SIOMLT_SEND = sSendRecvMgr.checksumResult;
|
|
|
|
*(vu64 *)recv = REG_SIOMLT_RECV;
|
|
if (recv[1] == EREADER_CHECKSUM_OK || recv[1] == EREADER_CHECKSUM_ERR)
|
|
{
|
|
if (sSendRecvMgr.isParent == TRUE)
|
|
sSendRecvMgr.checksumResult = recv[1]; // EReader has (in)validated the payload
|
|
|
|
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void EnableSio(void)
|
|
{
|
|
REG_SIOCNT |= SIO_ENABLE;
|
|
}
|
|
|
|
static void DisableTm3(void)
|
|
{
|
|
REG_TM3CNT_H &= ~TIMER_ENABLE;
|
|
REG_TM3CNT_L = 0xFDA7;
|
|
}
|
|
|
|
static void GetKeyInput(void)
|
|
{
|
|
int rawKeys = REG_KEYINPUT ^ KEYS_MASK;
|
|
sJoyNew = rawKeys & ~sJoyNewOrRepeated;
|
|
sJoyNewOrRepeated = rawKeys;
|
|
}
|
|
|
|
void EReaderHelper_SaveRegsState(void)
|
|
{
|
|
sSavedIme = REG_IME;
|
|
sSavedIe = REG_IE;
|
|
sSavedTm3Cnt = REG_TM3CNT_H;
|
|
sSavedSioCnt = REG_SIOCNT;
|
|
sSavedRCnt = REG_RCNT;
|
|
}
|
|
|
|
void EReaderHelper_RestoreRegsState(void)
|
|
{
|
|
REG_IME = sSavedIme;
|
|
REG_IE = sSavedIe;
|
|
REG_TM3CNT_H = sSavedTm3Cnt;
|
|
REG_SIOCNT = sSavedSioCnt;
|
|
REG_RCNT = sSavedRCnt;
|
|
}
|
|
|
|
void EReaderHelper_ClearSendRecvMgr(void)
|
|
{
|
|
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
|
|
}
|