From f16b6100c8fced4780e40d95d47b795f7eae470c Mon Sep 17 00:00:00 2001 From: GriffinR Date: Fri, 5 Aug 2022 11:49:17 -0400 Subject: [PATCH] Sync save sector checks --- include/global.h | 4 +++ include/save.h | 19 +++++++++----- src/cereader_tool.c | 17 ++++++++----- src/hall_of_fame.c | 18 +++++++------ src/save.c | 62 +++++++++++++++++++++++++-------------------- 5 files changed, 70 insertions(+), 50 deletions(-) diff --git a/include/global.h b/include/global.h index ce8f050f5..e474e1eaa 100644 --- a/include/global.h +++ b/include/global.h @@ -129,6 +129,10 @@ extern u8 gStringVar4[]; #define NUM_FLAG_BYTES ROUND_BITS_TO_BYTES(FLAGS_COUNT) #define NUM_ADDITIONAL_PHRASE_BYTES ROUND_BITS_TO_BYTES(NUM_ADDITIONAL_PHRASES) +// This produces an error at compile-time if expr is zero. +// It looks like file.c:line: size of array `id' is negative +#define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1]; + struct Coords8 { s8 x; diff --git a/include/save.h b/include/save.h index bea8c7189..87fb9b615 100644 --- a/include/save.h +++ b/include/save.h @@ -9,7 +9,10 @@ #define SECTOR_FOOTER_SIZE 128 #define SECTOR_SIZE (SECTOR_DATA_SIZE + SECTOR_FOOTER_SIZE) -#define FILE_SIGNATURE 0x08012025 // signature value to determine if a sector is in use +#define NUM_SAVE_SLOTS 2 + +// If the sector's signature field is not this value then the sector is either invalid or empty. +#define SECTOR_SIGNATURE 0x08012025 #define SPECIAL_SECTOR_SENTINEL 0xB39D @@ -26,12 +29,19 @@ #define SECTOR_ID_TRAINER_TOWER_2 31 #define SECTORS_COUNT 32 +#define NUM_HOF_SECTORS 2 + #define SAVE_STATUS_EMPTY 0 #define SAVE_STATUS_OK 1 #define SAVE_STATUS_INVALID 2 #define SAVE_STATUS_NO_FLASH 4 #define SAVE_STATUS_ERROR 0xFF +// Special sector id value for certain save functions +// to indicate that all sectors should be used +// instead of a specific sector. +#define FULL_SAVE_SLOT 0xFFFF + enum { SAVE_NORMAL, @@ -58,16 +68,11 @@ struct SaveSector u16 checksum; u32 signature; u32 counter; -}; // size is 0x1000 +}; // size is SECTOR_SIZE (0x1000) #define SECTOR_SIGNATURE_OFFSET offsetof(struct SaveSector, signature) #define SECTOR_COUNTER_OFFSET offsetof(struct SaveSector, counter) -// Special sector id value for certain save functions -// to indicate that all sectors should be used -// instead of a specific sector. -#define FULL_SAVE_SLOT 0xFFFF - // operations for SetDamagedSectorBits enum { diff --git a/src/cereader_tool.c b/src/cereader_tool.c index 96fa195cc..ebe0965ac 100644 --- a/src/cereader_tool.c +++ b/src/cereader_tool.c @@ -4,6 +4,12 @@ #include "save.h" #include "cereader_tool.h" +#define SEC30_SIZE (offsetof(struct EReaderTrainerTowerSet, floors[4])) +#define SEC31_SIZE (sizeof(struct EReaderTrainerTowerSet) - SEC30_SIZE) + +// The trainer tower data exceeds SECTOR_DATA_SIZE. They're allowed to use the full save sector up to the counter field. +STATIC_ASSERT(SEC30_SIZE + SEC31_SIZE <= SECTOR_COUNTER_OFFSET * 2, EReaderTrainerTowerSetFreeSpace); + u8 sub_815D654(void) { return (gSaveBlock1Ptr->trainerTower[0].unk9 + 1) % 256; @@ -36,20 +42,17 @@ bool32 ValidateTrainerTowerData(struct EReaderTrainerTowerSet * ttdata) return TRUE; } -#define SEC30_SIZE (offsetof(struct EReaderTrainerTowerSet, floors[4])) -#define SEC31_SIZE (sizeof(struct EReaderTrainerTowerSet) - SEC30_SIZE) - static bool32 CEReaderTool_SaveTrainerTower_r(struct EReaderTrainerTowerSet * ttdata, u8 * buffer) { AGB_ASSERT_EX(ttdata->dummy == 0, ABSPATH("cereader_tool.c"), 198); AGB_ASSERT_EX(ttdata->id == 0, ABSPATH("cereader_tool.c"), 199) - memset(buffer, 0, 0x1000); + memset(buffer, 0, SECTOR_SIZE); memcpy(buffer, ttdata, SEC30_SIZE); buffer[1] = sub_815D654(); if (TryWriteSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_1, buffer) != TRUE) return FALSE; - memset(buffer, 0, 0x1000); + memset(buffer, 0, SECTOR_SIZE); memcpy(buffer, (u8 *)ttdata + SEC30_SIZE, SEC31_SIZE); if (TryWriteSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_2, buffer) != TRUE) return FALSE; @@ -58,7 +61,7 @@ static bool32 CEReaderTool_SaveTrainerTower_r(struct EReaderTrainerTowerSet * tt bool32 CEReaderTool_SaveTrainerTower(struct EReaderTrainerTowerSet * ttdata) { - u8 * buffer = AllocZeroed(0x1000); + u8 * buffer = AllocZeroed(SECTOR_SIZE); bool32 result = CEReaderTool_SaveTrainerTower_r(ttdata, buffer); Free(buffer); return result; @@ -81,7 +84,7 @@ static bool32 CEReaderTool_LoadTrainerTower_r(struct EReaderTrainerTowerSet * tt bool32 CEReaderTool_LoadTrainerTower(struct EReaderTrainerTowerSet * ttdata) { - void *buffer = AllocZeroed(0x1000); + void *buffer = AllocZeroed(SECTOR_SIZE); bool32 success = CEReaderTool_LoadTrainerTower_r(ttdata, buffer); Free(buffer); return success; diff --git a/src/hall_of_fame.c b/src/hall_of_fame.c index 67aabf80c..7d5423e09 100644 --- a/src/hall_of_fame.c +++ b/src/hall_of_fame.c @@ -26,13 +26,16 @@ #include "constants/songs.h" #include "constants/maps.h" +#define HALL_OF_FAME_MAX_TEAMS 50 +#define HALL_OF_FAME_BG_PAL RGB(22, 24, 29) + struct HallofFameMon { u32 tid; u32 personality; u16 species:9; u16 lvl:7; - u8 nick[10]; + u8 nick[POKEMON_NAME_LENGTH]; }; struct HallofFameTeam @@ -40,6 +43,8 @@ struct HallofFameTeam struct HallofFameMon mon[PARTY_SIZE]; }; +STATIC_ASSERT(sizeof(struct HallofFameTeam) * HALL_OF_FAME_MAX_TEAMS <= SECTOR_DATA_SIZE * NUM_HOF_SECTORS, HallOfFameFreeSpace); + struct HofGfx { u16 state; @@ -52,9 +57,6 @@ static EWRAM_DATA u32 sSelectedPaletteIndices = 0; static EWRAM_DATA struct HallofFameTeam * sHofMonPtr = NULL; static EWRAM_DATA struct HofGfx * sHofGfxPtr = NULL; -#define HALL_OF_FAME_MAX_TEAMS 50 -#define HALL_OF_FAME_BG_PAL (RGB(22, 24, 29)) - static void Task_Hof_InitMonData(u8 taskId); static void Task_Hof_InitTeamSaveData(u8 taskId); static void Task_Hof_TrySaveData(u8 taskId); @@ -425,12 +427,12 @@ static void Task_Hof_InitTeamSaveData(u8 taskId) SaveQuestLogData(); if (!gHasHallOfFameRecords) { - memset(gDecompressionBuffer, 0, 0x2000); + memset(gDecompressionBuffer, 0, SECTOR_SIZE * NUM_HOF_SECTORS); } else { if (LoadGameSave(SAVE_HALL_OF_FAME) != SAVE_STATUS_OK) - memset(gDecompressionBuffer, 0, 0x2000); + memset(gDecompressionBuffer, 0, SECTOR_SIZE * NUM_HOF_SECTORS); } for (i = 0; i < HALL_OF_FAME_MAX_TEAMS; i++, lastSavedTeam++) @@ -747,7 +749,7 @@ void CB2_InitHofPC(void) SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(16, 7)); SetGpuReg(REG_OFFSET_BLDY, 0); CreateTask(Task_HofPC_CopySaveData, 0); - sHofMonPtr = AllocZeroed(0x2000); + sHofMonPtr = AllocZeroed(SECTOR_SIZE * NUM_HOF_SECTORS); SetMainCallback2(CB2_HofIdle); break; } @@ -765,7 +767,7 @@ static void Task_HofPC_CopySaveData(u8 taskId) } else { - CpuCopy16(gDecompressionBuffer, sHofMonPtr, 0x2000); + CpuCopy16(gDecompressionBuffer, sHofMonPtr, SECTOR_SIZE * NUM_HOF_SECTORS); savedTeams = sHofMonPtr; for (i = 0; i < HALL_OF_FAME_MAX_TEAMS; i++, savedTeams++) { diff --git a/src/save.c b/src/save.c index 61c97a3a8..08b8465b7 100644 --- a/src/save.c +++ b/src/save.c @@ -49,26 +49,32 @@ struct { u16 offset; u16 size; -} static const sSaveSlotLayout[] = +} static const sSaveSlotLayout[NUM_SECTORS_PER_SLOT] = { - SAVEBLOCK_CHUNK(gSaveBlock2, 0), // SECTOR_ID_SAVEBLOCK2 + SAVEBLOCK_CHUNK(struct SaveBlock2, 0), // SECTOR_ID_SAVEBLOCK2 - SAVEBLOCK_CHUNK(gSaveBlock1, 0), // SECTOR_ID_SAVEBLOCK1_START - SAVEBLOCK_CHUNK(gSaveBlock1, 1), - SAVEBLOCK_CHUNK(gSaveBlock1, 2), - SAVEBLOCK_CHUNK(gSaveBlock1, 3), // SECTOR_ID_SAVEBLOCK1_END + SAVEBLOCK_CHUNK(struct SaveBlock1, 0), // SECTOR_ID_SAVEBLOCK1_START + SAVEBLOCK_CHUNK(struct SaveBlock1, 1), + SAVEBLOCK_CHUNK(struct SaveBlock1, 2), + SAVEBLOCK_CHUNK(struct SaveBlock1, 3), // SECTOR_ID_SAVEBLOCK1_END - SAVEBLOCK_CHUNK(gPokemonStorage, 0), // SECTOR_ID_PKMN_STORAGE_START - SAVEBLOCK_CHUNK(gPokemonStorage, 1), - SAVEBLOCK_CHUNK(gPokemonStorage, 2), - SAVEBLOCK_CHUNK(gPokemonStorage, 3), - SAVEBLOCK_CHUNK(gPokemonStorage, 4), - SAVEBLOCK_CHUNK(gPokemonStorage, 5), - SAVEBLOCK_CHUNK(gPokemonStorage, 6), - SAVEBLOCK_CHUNK(gPokemonStorage, 7), - SAVEBLOCK_CHUNK(gPokemonStorage, 8), // SECTOR_ID_PKMN_STORAGE_END + SAVEBLOCK_CHUNK(struct PokemonStorage, 0), // SECTOR_ID_PKMN_STORAGE_START + SAVEBLOCK_CHUNK(struct PokemonStorage, 1), + SAVEBLOCK_CHUNK(struct PokemonStorage, 2), + SAVEBLOCK_CHUNK(struct PokemonStorage, 3), + SAVEBLOCK_CHUNK(struct PokemonStorage, 4), + SAVEBLOCK_CHUNK(struct PokemonStorage, 5), + SAVEBLOCK_CHUNK(struct PokemonStorage, 6), + SAVEBLOCK_CHUNK(struct PokemonStorage, 7), + SAVEBLOCK_CHUNK(struct PokemonStorage, 8), // SECTOR_ID_PKMN_STORAGE_END }; +// These will produce an error if a save struct is larger than the space +// alloted for it in the flash. +STATIC_ASSERT(sizeof(struct SaveBlock2) <= SECTOR_DATA_SIZE, SaveBlock2FreeSpace); +STATIC_ASSERT(sizeof(struct SaveBlock1) <= SECTOR_DATA_SIZE * (SECTOR_ID_SAVEBLOCK1_END - SECTOR_ID_SAVEBLOCK1_START + 1), SaveBlock1FreeSpace); +STATIC_ASSERT(sizeof(struct PokemonStorage) <= SECTOR_DATA_SIZE * (SECTOR_ID_PKMN_STORAGE_END - SECTOR_ID_PKMN_STORAGE_START + 1), PokemonStorageFreeSpace); + // Sector num to begin writing save data. Sectors are rotated each time the game is saved. (possibly to avoid wear on flash memory?) u16 gLastWrittenSector; u32 gLastSaveCounter; @@ -165,7 +171,7 @@ static u8 HandleWriteSector(u16 sectorId, const struct SaveSectorLocation *locat sectorNum = gLastWrittenSector + sectorId; sectorNum %= NUM_SECTORS_PER_SLOT; - sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2); + sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS); data = locations[sectorId].data; size = locations[sectorId].size; @@ -176,7 +182,7 @@ static u8 HandleWriteSector(u16 sectorId, const struct SaveSectorLocation *locat // fill buffer with save data gSaveDataBufferPtr->id = sectorId; - gSaveDataBufferPtr->signature = FILE_SIGNATURE; + gSaveDataBufferPtr->signature = SECTOR_SIGNATURE; gSaveDataBufferPtr->counter = gSaveCounter; for (i = 0; i < size; i++) @@ -194,7 +200,7 @@ static u8 HandleWriteSectorNBytes(u8 sectorId, u8 *data, u16 size) for (i = 0; i < SECTOR_SIZE; i++) ((char *)sector)[i] = 0; - sector->signature = FILE_SIGNATURE; + sector->signature = SECTOR_SIGNATURE; for (i = 0; i < size; i++) sector->data[i] = data[i]; @@ -287,7 +293,7 @@ static u8 HandleReplaceSector(u16 sectorId, const struct SaveSectorLocation *loc sectorNum = gLastWrittenSector + sectorId; sectorNum %= NUM_SECTORS_PER_SLOT; - sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2); + sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS); data = locations[sectorId].data; size = locations[sectorId].size; @@ -298,7 +304,7 @@ static u8 HandleReplaceSector(u16 sectorId, const struct SaveSectorLocation *loc // fill buffer with save data gSaveDataBufferPtr->id = sectorId; - gSaveDataBufferPtr->signature = FILE_SIGNATURE; + gSaveDataBufferPtr->signature = SECTOR_SIGNATURE; gSaveDataBufferPtr->counter = gSaveCounter; for (i = 0; i < size; i++) gSaveDataBufferPtr->data[i] = data[i]; @@ -359,7 +365,7 @@ static u8 CopySectorSignatureByte(u16 sectorId, const struct SaveSectorLocation sector = gLastWrittenSector + sectorId - 1; sector %= NUM_SECTORS_PER_SLOT; - sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2); + sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS); if (ProgramFlashByte(sector, SECTOR_SIGNATURE_OFFSET, ((u8 *)gSaveDataBufferPtr)[SECTOR_SIGNATURE_OFFSET])) { @@ -382,10 +388,10 @@ static u8 WriteSectorSignatureByte(u16 sectorId, const struct SaveSectorLocation sector = gLastWrittenSector + sectorId - 1; sector %= NUM_SECTORS_PER_SLOT; - sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2); + sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS); // write only the first byte of the signature, which was skipped in HandleReplaceSector - if (ProgramFlashByte(sector, SECTOR_SIGNATURE_OFFSET, FILE_SIGNATURE & 0xFF)) + if (ProgramFlashByte(sector, SECTOR_SIGNATURE_OFFSET, SECTOR_SIGNATURE & 0xFF)) { // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter. SetDamagedSectorBits(ENABLE, sector); @@ -421,7 +427,7 @@ static u8 CopySaveSlotData(u16 sectorId, const struct SaveSectorLocation *locati { u16 i; u16 checksum; - u16 sector = NUM_SECTORS_PER_SLOT * (gSaveCounter % 2); + u16 sector = NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS); u16 id; for (i = 0; i < NUM_SECTORS_PER_SLOT; i++) @@ -432,7 +438,7 @@ static u8 CopySaveSlotData(u16 sectorId, const struct SaveSectorLocation *locati gLastWrittenSector = i; checksum = CalculateChecksum(gSaveDataBufferPtr->data, locations[id].size); - if (gSaveDataBufferPtr->signature == FILE_SIGNATURE && gSaveDataBufferPtr->checksum == checksum) + if (gSaveDataBufferPtr->signature == SECTOR_SIGNATURE && gSaveDataBufferPtr->checksum == checksum) { u16 j; for (j = 0; j < locations[id].size; j++) @@ -461,7 +467,7 @@ static u8 GetSaveValidStatus(const struct SaveSectorLocation *locations) for (sector = 0; sector < NUM_SECTORS_PER_SLOT; sector++) { ReadFlashSector(sector, gSaveDataBufferPtr); - if (gSaveDataBufferPtr->signature == FILE_SIGNATURE) + if (gSaveDataBufferPtr->signature == SECTOR_SIGNATURE) { signatureValid = TRUE; checksum = CalculateChecksum(gSaveDataBufferPtr->data, locations[gSaveDataBufferPtr->id].size); @@ -489,7 +495,7 @@ static u8 GetSaveValidStatus(const struct SaveSectorLocation *locations) for (sector = 0; sector < NUM_SECTORS_PER_SLOT; sector++) { ReadFlashSector(NUM_SECTORS_PER_SLOT + sector, gSaveDataBufferPtr); - if (gSaveDataBufferPtr->signature == FILE_SIGNATURE) + if (gSaveDataBufferPtr->signature == SECTOR_SIGNATURE) { signatureValid = TRUE; checksum = CalculateChecksum(gSaveDataBufferPtr->data, locations[gSaveDataBufferPtr->id].size); @@ -567,7 +573,7 @@ static u8 TryLoadSaveSector(u8 sectorId, u8 *data, u16 size) struct SaveSector *sector = &gSaveDataBuffer; ReadFlashSector(sectorId, sector); - if (sector->signature == FILE_SIGNATURE) + if (sector->signature == SECTOR_SIGNATURE) { u16 checksum = CalculateChecksum(sector->data, size); if (sector->id == checksum)