summaryrefslogtreecommitdiff
path: root/berry_fix/payload/src
diff options
context:
space:
mode:
Diffstat (limited to 'berry_fix/payload/src')
-rw-r--r--berry_fix/payload/src/flash.c752
-rw-r--r--berry_fix/payload/src/main.c289
-rw-r--r--berry_fix/payload/src/rtc.c346
3 files changed, 1387 insertions, 0 deletions
diff --git a/berry_fix/payload/src/flash.c b/berry_fix/payload/src/flash.c
new file mode 100644
index 000000000..3a0369dda
--- /dev/null
+++ b/berry_fix/payload/src/flash.c
@@ -0,0 +1,752 @@
+#include <gba/gba.h>
+#include <agb_flash.h>
+#include "constants/vars.h"
+#include "global.h"
+#include "main.h"
+#include "flash.h"
+#include "rtc.h"
+
+struct SaveBlockChunk
+{
+ u8 * data;
+ u16 size;
+};
+
+u8 WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1);
+u8 WriteSingleChunk(u16 a0, const struct SaveBlockChunk * a1);
+u8 TryWriteSector(u8, u8 *);
+u8 EraseCurrentChunk(u16 a0, const struct SaveBlockChunk * a1);
+u8 TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1);
+u8 ReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1);
+u8 GetSaveValidStatus(const struct SaveBlockChunk * a1);
+u32 DoReadFlashWholeSection(u8 a0, struct SaveSector * a1);
+u16 CalculateChecksum(const void *, u16);
+
+u16 gFirstSaveSector;
+u32 gPrevSaveCounter;
+u16 gLastKnownGoodSector;
+u32 gDamagedSaveSectors;
+u32 gSaveCounter;
+struct SaveSector * gFastSaveSection;
+u16 gCurSaveChunk;
+bool32 gFlashIdentIsValid;
+
+EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {};
+EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {};
+EWRAM_DATA struct PokemonStorage gPokemonStorage = {};
+
+// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer
+#define SECTOR_DATA_SIZE 3968
+#define SECTOR_FOOTER_SIZE 128
+
+#define SAVEBLOCK_CHUNK(structure, chunkNum) \
+{ \
+ (u8 *)&structure + chunkNum * SECTOR_DATA_SIZE, \
+ min(sizeof(structure) - chunkNum * SECTOR_DATA_SIZE, SECTOR_DATA_SIZE) \
+} \
+
+static const struct SaveBlockChunk sSaveBlockChunks[] =
+{
+ SAVEBLOCK_CHUNK(gSaveBlock2, 0),
+
+ SAVEBLOCK_CHUNK(gSaveBlock1, 0),
+ SAVEBLOCK_CHUNK(gSaveBlock1, 1),
+ SAVEBLOCK_CHUNK(gSaveBlock1, 2),
+ SAVEBLOCK_CHUNK(gSaveBlock1, 3),
+
+ SAVEBLOCK_CHUNK(gPokemonStorage, 0),
+ 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),
+};
+
+const u16 gInfoMessagesPal[] = INCBIN_U16("graphics/msg_box.gbapal");
+const u8 gInfoMessagesTilemap[] = INCBIN_U8("graphics/msg_box.tilemap.lz");
+const u8 gInfoMessagesGfx[] = INCBIN_U8("graphics/msg_box.4bpp.lz");
+
+bool32 flash_maincb_ident_is_valid(void)
+{
+ gFlashIdentIsValid = TRUE;
+ if (!IdentifyFlash())
+ {
+ SetFlashTimerIntr(0, &((IntrFunc *)gIntrFuncPointers)[9]);
+ return TRUE;
+ }
+ gFlashIdentIsValid = FALSE;
+ return FALSE;
+}
+
+void Call_ReadFlash(u16 sectorNum, ptrdiff_t offset, void * dest, size_t size)
+{
+ ReadFlash(sectorNum, offset, dest, size);
+}
+
+u8 Call_WriteSaveBlockChunks(u16 a0, const struct SaveBlockChunk * a1)
+{
+ return WriteSaveBlockChunks(a0, a1);
+}
+
+u8 Call_TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1)
+{
+ return TryReadAllSaveSectorsCurrentSlot(a0, a1);
+}
+
+u32 * GetDamagedSaveSectorsPtr(void)
+{
+ return &gDamagedSaveSectors;
+}
+
+s32 flash_write_save_block_chunks(u8 a0)
+{
+ u8 i;
+
+ switch (a0)
+ {
+ case 0:
+ default:
+ Call_WriteSaveBlockChunks(0xFFFF, sSaveBlockChunks);
+ break;
+ case 1:
+ for (i = 0; i < 5; i++)
+ {
+ Call_WriteSaveBlockChunks(i, sSaveBlockChunks);
+ }
+ break;
+ case 2:
+ Call_WriteSaveBlockChunks(0, sSaveBlockChunks);
+ break;
+ }
+
+ return 0;
+}
+
+u8 flash_write_save_block_chunks_check_damage(u8 a0)
+{
+ flash_write_save_block_chunks(a0);
+ if (*GetDamagedSaveSectorsPtr() == 0)
+ return 1;
+ return 0xFF;
+}
+
+u8 flash_maincb_read_save(u32 unused)
+{
+ return Call_TryReadAllSaveSectorsCurrentSlot(0xFFFF, sSaveBlockChunks);
+}
+
+void msg_load_gfx(void)
+{
+ REG_DISPCNT = 0;
+ REG_BG0HOFS = 0;
+ REG_BG0VOFS = 0;
+ REG_BLDCNT = 0;
+ LZ77UnCompVram(gInfoMessagesGfx, (void *)BG_VRAM);
+ LZ77UnCompVram(gInfoMessagesTilemap, (void *)BG_SCREEN_ADDR(28));
+ CpuCopy16(gInfoMessagesPal, (void *)BG_PLTT, 0x200);
+ REG_BG0CNT = BGCNT_SCREENBASE(28) | BGCNT_TXT512x512;
+ REG_DISPCNT = DISPCNT_BG0_ON;
+}
+
+void msg_display(enum MsgBoxUpdateMessage a0)
+{
+ switch (a0)
+ {
+ case MSGBOX_WILL_NOW_UPDATE:
+ REG_BG0HOFS = 0;
+ REG_BG0VOFS = 0;
+ break;
+ case MSGBOX_HAS_BEEN_UPDATED:
+ REG_BG0HOFS = 0x100;
+ REG_BG0VOFS = 0;
+ break;
+ case MSGBOX_UNABLE_TO_UPDATE:
+ REG_BG0HOFS = 0x100;
+ REG_BG0VOFS = 0xB0;
+ break;
+ case MSGBOX_NO_NEED_TO_UPDATE:
+ REG_BG0HOFS = 0;
+ REG_BG0VOFS = 0xB0;
+ break;
+ case MSGBOX_UPDATING:
+ REG_BG0HOFS = 0;
+ REG_BG0VOFS = 0x160;
+ break;
+ }
+}
+
+void Save_EraseAllData(void)
+{
+ u16 i;
+ for (i = 0; i < 32; i++)
+ EraseFlashSector(i);
+}
+
+void Save_ResetSaveCounters(void)
+{
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ gDamagedSaveSectors = 0;
+}
+
+bool32 SetSectorDamagedStatus(u8 op, u8 sectorNum)
+{
+ bool32 retVal = FALSE;
+
+ switch (op)
+ {
+ case SECTOR_DAMAGED:
+ gDamagedSaveSectors |= (1 << sectorNum);
+ break;
+ case SECTOR_OK:
+ gDamagedSaveSectors &= ~(1 << sectorNum);
+ break;
+ case SECTOR_CHECK: // unused
+ if (gDamagedSaveSectors & (1 << sectorNum))
+ retVal = TRUE;
+ break;
+ }
+
+ return retVal;
+}
+
+u8 WriteSaveBlockChunks(u16 chunkId, const struct SaveBlockChunk *chunks)
+{
+ u32 retVal;
+ u16 i;
+
+ gFastSaveSection = eSaveSection;
+
+ if (chunkId != 0xFFFF) // write single chunk
+ {
+ retVal = WriteSingleChunk(chunkId, chunks);
+ }
+ else // write all chunks
+ {
+ gLastKnownGoodSector = gFirstSaveSector;
+ gPrevSaveCounter = gSaveCounter;
+ gFirstSaveSector++;
+ gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT;
+ gSaveCounter++;
+ retVal = SAVE_STATUS_OK;
+
+ for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
+ WriteSingleChunk(i, chunks);
+
+ // Check for any bad sectors
+ if (gDamagedSaveSectors != 0) // skip the damaged sector.
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ }
+
+ return retVal;
+}
+
+u8 WriteSingleChunk(u16 chunkId, const struct SaveBlockChunk * chunks)
+{
+ u16 i;
+ u16 sectorNum;
+ u8 *chunkData;
+ u16 chunkSize;
+
+ // select sector number
+ sectorNum = chunkId + gFirstSaveSector;
+ sectorNum %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sectorNum += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ chunkData = chunks[chunkId].data;
+ chunkSize = chunks[chunkId].size;
+
+ // clear save section.
+ for (i = 0; i < sizeof(struct SaveSector); i++)
+ ((u8 *)gFastSaveSection)[i] = 0;
+
+ gFastSaveSection->id = chunkId;
+ gFastSaveSection->signature = FILE_SIGNATURE;
+ gFastSaveSection->counter = gSaveCounter;
+ for (i = 0; i < chunkSize; i++)
+ gFastSaveSection->data[i] = chunkData[i];
+ gFastSaveSection->checksum = CalculateChecksum(chunkData, chunkSize);
+
+ return TryWriteSector(sectorNum, gFastSaveSection->data);
+}
+
+u8 HandleWriteSectorNBytes(u8 sectorNum, u8 *data, u16 size)
+{
+ u16 i;
+ struct SaveSector *section = eSaveSection;
+
+ for (i = 0; i < sizeof(struct SaveSector); i++)
+ ((char *)section)[i] = 0;
+
+ section->signature = FILE_SIGNATURE;
+ for (i = 0; i < size; i++)
+ section->data[i] = data[i];
+ section->id = CalculateChecksum(data, size); // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used.
+
+ return TryWriteSector(sectorNum, section->data);
+}
+
+u8 TryWriteSector(u8 sectorNum, u8 *data)
+{
+ if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged?
+ {
+ SetSectorDamagedStatus(SECTOR_DAMAGED, sectorNum); // set damaged sector bits.
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(SECTOR_OK, sectorNum); // unset damaged sector bits. it's safe now.
+ return SAVE_STATUS_OK;
+ }
+}
+
+u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunk) // chunk is unused
+{
+ gFastSaveSection = eSaveSection;
+ gLastKnownGoodSector = gFirstSaveSector;
+ gPrevSaveCounter = gSaveCounter;
+ gFirstSaveSector++;
+ gFirstSaveSector %= NUM_SECTORS_PER_SAVE_SLOT;
+ gSaveCounter++;
+ gCurSaveChunk = 0;
+ gDamagedSaveSectors = 0;
+ return 0;
+}
+
+u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunk)
+{
+ gFastSaveSection = eSaveSection;
+ gLastKnownGoodSector = gFirstSaveSector;
+ gPrevSaveCounter = gSaveCounter;
+ gCurSaveChunk = 0;
+ gDamagedSaveSectors = 0;
+ return 0;
+}
+
+u8 WriteSingleChunkAndIncrement(u16 a1, const struct SaveBlockChunk * chunk)
+{
+ u8 retVal;
+
+ if (gCurSaveChunk < a1 - 1)
+ {
+ retVal = SAVE_STATUS_OK;
+ WriteSingleChunk(gCurSaveChunk, chunk);
+ gCurSaveChunk++;
+ if (gDamagedSaveSectors)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ }
+ else
+ {
+ retVal = SAVE_STATUS_ERROR;
+ }
+
+ return retVal;
+}
+
+u8 ErasePreviousChunk(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u8 retVal = SAVE_STATUS_OK;
+
+ EraseCurrentChunk(a1 - 1, chunk);
+
+ if (gDamagedSaveSectors)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ }
+ return retVal;
+}
+
+u8 EraseCurrentChunk(u16 chunkId, const struct SaveBlockChunk *chunks)
+{
+ u16 i;
+ u16 sector;
+ u8 *data;
+ u16 size;
+ u8 status;
+
+ // select sector number
+ sector = chunkId + gFirstSaveSector;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ data = chunks[chunkId].data;
+ size = chunks[chunkId].size;
+
+ // clear temp save section.
+ for (i = 0; i < sizeof(struct SaveSector); i++)
+ ((char *)gFastSaveSection)[i] = 0;
+
+ gFastSaveSection->id = chunkId;
+ gFastSaveSection->signature = FILE_SIGNATURE;
+ gFastSaveSection->counter = gSaveCounter;
+
+ // set temp section's data.
+ for (i = 0; i < size; i++)
+ gFastSaveSection->data[i] = data[i];
+
+ // calculate checksum.
+ gFastSaveSection->checksum = CalculateChecksum(data, size);
+
+ EraseFlashSector(sector);
+
+ status = SAVE_STATUS_OK;
+
+ for (i = 0; i < sizeof(struct UnkSaveSection); i++)
+ {
+ if (ProgramFlashByte(sector, i, gFastSaveSection->data[i]))
+ {
+ status = SAVE_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if (status == SAVE_STATUS_ERROR)
+ {
+ SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ status = SAVE_STATUS_OK;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (ProgramFlashByte(sector, 0xFF9 + i, ((u8 *)gFastSaveSection)[0xFF9 + i]))
+ {
+ status = SAVE_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if (status == SAVE_STATUS_ERROR)
+ {
+ SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(SECTOR_OK, sector);
+ return SAVE_STATUS_OK;
+ }
+ }
+}
+
+u8 WriteSomeFlashByteToPrevSector(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u16 sector;
+
+ // select sector number
+ sector = a1 + gFirstSaveSector - 1;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ // select save slot
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), ((u8 *)gFastSaveSection)[sizeof(struct UnkSaveSection)]))
+ {
+ // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
+ SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(SECTOR_OK, sector);
+ return SAVE_STATUS_OK;
+ }
+}
+
+u8 WriteSomeFlashByte0x25ToPrevSector(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u16 sector;
+
+ sector = a1 + gFirstSaveSector - 1;
+ sector %= NUM_SECTORS_PER_SAVE_SLOT;
+ sector += NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+
+ if (ProgramFlashByte(sector, sizeof(struct UnkSaveSection), 0x25))
+ {
+ // sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
+ SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
+ gFirstSaveSector = gLastKnownGoodSector;
+ gSaveCounter = gPrevSaveCounter;
+ return SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ SetSectorDamagedStatus(SECTOR_OK, sector);
+ return SAVE_STATUS_OK;
+ }
+}
+
+u8 TryReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunk)
+{
+ u8 retVal;
+ gFastSaveSection = eSaveSection;
+ if (a1 != 0xFFFF)
+ {
+ retVal = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ retVal = GetSaveValidStatus(chunk);
+ ReadAllSaveSectorsCurrentSlot(0xFFFF, chunk);
+ }
+
+ return retVal;
+}
+
+u8 ReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunks)
+{
+ u16 i;
+ u16 checksum;
+ u16 sector = NUM_SECTORS_PER_SAVE_SLOT * (gSaveCounter % 2);
+ u16 id;
+
+ for (i = 0; i < NUM_SECTORS_PER_SAVE_SLOT; i++)
+ {
+ DoReadFlashWholeSection(i + sector, gFastSaveSection);
+ id = gFastSaveSection->id;
+ if (id == 0)
+ gFirstSaveSector = i;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[id].size);
+ if (gFastSaveSection->signature == FILE_SIGNATURE
+ && gFastSaveSection->checksum == checksum)
+ {
+ u16 j;
+ for (j = 0; j < chunks[id].size; j++)
+ chunks[id].data[j] = gFastSaveSection->data[j];
+ }
+ }
+
+ return 1;
+}
+
+u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
+{
+ u16 sector;
+ bool8 signatureValid;
+ u16 checksum;
+ u32 slot1saveCounter = 0;
+ u32 slot2saveCounter = 0;
+ u8 slot1Status;
+ u8 slot2Status;
+ u32 validSectors;
+ const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SAVE_SLOT) - 1; // bitmask of all saveblock sectors
+
+ // check save slot 1.
+ validSectors = 0;
+ signatureValid = FALSE;
+ for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
+ {
+ DoReadFlashWholeSection(sector, gFastSaveSection);
+ if (gFastSaveSection->signature == FILE_SIGNATURE)
+ {
+ signatureValid = TRUE;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
+ if (gFastSaveSection->checksum == checksum)
+ {
+ slot1saveCounter = gFastSaveSection->counter;
+ validSectors |= 1 << gFastSaveSection->id;
+ }
+ }
+ }
+
+ if (signatureValid)
+ {
+ if (validSectors == ALL_SECTORS)
+ slot1Status = SAVE_STATUS_OK;
+ else
+ slot1Status = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ slot1Status = SAVE_STATUS_EMPTY;
+ }
+
+ // check save slot 2.
+ validSectors = 0;
+ signatureValid = FALSE;
+ for (sector = 0; sector < NUM_SECTORS_PER_SAVE_SLOT; sector++)
+ {
+ DoReadFlashWholeSection(NUM_SECTORS_PER_SAVE_SLOT + sector, gFastSaveSection);
+ if (gFastSaveSection->signature == FILE_SIGNATURE)
+ {
+ signatureValid = TRUE;
+ checksum = CalculateChecksum(gFastSaveSection->data, chunks[gFastSaveSection->id].size);
+ if (gFastSaveSection->checksum == checksum)
+ {
+ slot2saveCounter = gFastSaveSection->counter;
+ validSectors |= 1 << gFastSaveSection->id;
+ }
+ }
+ }
+
+ if (signatureValid)
+ {
+ if (validSectors == ALL_SECTORS)
+ slot2Status = SAVE_STATUS_OK;
+ else
+ slot2Status = SAVE_STATUS_ERROR;
+ }
+ else
+ {
+ slot2Status = SAVE_STATUS_EMPTY;
+ }
+
+ if (slot1Status == SAVE_STATUS_OK && slot2Status == SAVE_STATUS_OK)
+ {
+ // Choose counter of the most recent save file
+ if ((slot1saveCounter == -1 && slot2saveCounter == 0) || (slot1saveCounter == 0 && slot2saveCounter == -1))
+ {
+ if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1))
+ gSaveCounter = slot2saveCounter;
+ else
+ gSaveCounter = slot1saveCounter;
+ }
+ else
+ {
+ if (slot1saveCounter < slot2saveCounter)
+ gSaveCounter = slot2saveCounter;
+ else
+ gSaveCounter = slot1saveCounter;
+ }
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot1Status == SAVE_STATUS_OK)
+ {
+ gSaveCounter = slot1saveCounter;
+ if (slot2Status == SAVE_STATUS_ERROR)
+ return SAVE_STATUS_ERROR;
+ else
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot2Status == SAVE_STATUS_OK)
+ {
+ gSaveCounter = slot2saveCounter;
+ if (slot1Status == SAVE_STATUS_ERROR)
+ return SAVE_STATUS_ERROR;
+ else
+ return SAVE_STATUS_OK;
+ }
+
+ if (slot1Status == SAVE_STATUS_EMPTY && slot2Status == SAVE_STATUS_EMPTY)
+ {
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ return SAVE_STATUS_EMPTY;
+ }
+
+ gSaveCounter = 0;
+ gFirstSaveSector = 0;
+ return 2;
+}
+
+u8 ReadSomeUnknownSectorAndVerify(u8 sector, u8 *data, u16 size)
+{
+ u16 i;
+ struct SaveSector *section = eSaveSection;
+
+ DoReadFlashWholeSection(sector, section);
+ if (section->signature == FILE_SIGNATURE)
+ {
+ u16 checksum = CalculateChecksum(section->data, size);
+ if (section->id == checksum)
+ {
+ for (i = 0; i < size; i++)
+ data[i] = section->data[i];
+ return SAVE_STATUS_OK;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ else
+ {
+ return SAVE_STATUS_EMPTY;
+ }
+}
+
+u32 DoReadFlashWholeSection(u8 sector, struct SaveSector *section)
+{
+ ReadFlash(sector, 0, section->data, sizeof(struct SaveSector));
+ return 1;
+}
+
+u16 CalculateChecksum(const void *data, u16 size)
+{
+ u16 i;
+ u32 checksum = 0;
+
+ for (i = 0; i < (size / 4); i++)
+ {
+ checksum += *((u32 *)data);
+ data += sizeof(u32);
+ }
+
+ return ((checksum >> 16) + checksum);
+}
+
+void nullsub_0201182C()
+{
+}
+
+void nullsub_02011830()
+{
+}
+
+void nullsub_02011834()
+{
+}
+
+u16 * get_var_addr(u16 a0)
+{
+ if (a0 < VARS_START)
+ return NULL;
+ if (a0 < VAR_SPECIAL_0)
+ return &gSaveBlock1.vars[a0 - VARS_START];
+ return NULL;
+}
+
+bool32 flash_maincb_check_need_reset_pacifidlog_tm(void)
+{
+ u8 sp0;
+ u16 * data = get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY);
+ rtc_maincb_is_time_since_last_berry_update_positive(&sp0);
+ if (*data <= gRtcUTCTime.days)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+bool32 flash_maincb_reset_pacifidlog_tm(void)
+{
+ u8 sp0;
+ if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE)
+ return TRUE;
+ rtc_maincb_is_time_since_last_berry_update_positive(&sp0);
+ if (gRtcUTCTime.days < 0)
+ return FALSE;
+ *get_var_addr(VAR_PACIFIDLOG_TM_RECEIVED_DAY) = 1;
+ if (flash_write_save_block_chunks_check_damage(0) != TRUE)
+ return FALSE;
+ return TRUE;
+}
diff --git a/berry_fix/payload/src/main.c b/berry_fix/payload/src/main.c
new file mode 100644
index 000000000..249150665
--- /dev/null
+++ b/berry_fix/payload/src/main.c
@@ -0,0 +1,289 @@
+#include <gba/gba.h>
+#include "global.h"
+#include "main.h"
+#include "rtc.h"
+#include "flash.h"
+
+static s32 gInitialWaitTimer;
+IntrFunc gIntrTable[16];
+u16 gHeldKeys;
+u16 gNewKeys;
+u8 gIntrVector[0x100];
+u32 gUpdateSuccessful;
+u32 gUnknown_3001194;
+u32 gUnknown_30011A0[0x19];
+u32 gMainCallbackState;
+u32 gGameVersion;
+
+EWRAM_DATA u8 gSharedMem[0x8000] = {};
+
+void IntrMain(void);
+void ReadKeys(void);
+void dummy_intr_0(void);
+void dummy_intr_1(void);
+void main_callback(u32 *, void *, void *);
+
+
+const char gBerryFixGameCode[] = "AGBJ";
+const IntrFunc gIntrFuncPointers[] = {
+ dummy_intr_0,
+ dummy_intr_1,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ dummy_intr_0,
+ NULL,
+ NULL,
+ NULL
+};
+const char gVersionData[][2] = {
+ {'J', 1},
+ {'E', 2},
+ {'D', 1},
+ {'F', 1},
+ {'I', 1},
+ {'S', 1}
+};
+const char gRubyTitleAndCode[] = "POKEMON RUBYAXV";
+const char gSapphireTitleAndCode[] = "POKEMON SAPPAXP";
+const u16 sDebugPals[20] = {
+ RGB(00, 00, 00),
+ RGB(31, 00, 00),
+ RGB(00, 31, 00),
+ RGB(00, 00, 31)
+};
+const u16 sDebugDigitsGfx[] = INCBIN_U16("graphics/debug_digits.4bpp");
+
+void AgbMain(void)
+{
+ RegisterRamReset(0x1E);
+ DmaCopy32(3, gIntrFuncPointers, gIntrTable, sizeof gIntrFuncPointers);
+ DmaCopy32(3, IntrMain, gIntrVector, sizeof(gIntrVector));
+ INTR_VECTOR = gIntrVector;
+ REG_IE = INTR_FLAG_VBLANK;
+ if (*RomHeaderMagic == 0x96 && *(u32 *)RomHeaderGameCode == *(u32 *)gBerryFixGameCode)
+ REG_IE |= INTR_FLAG_GAMEPAK;
+ REG_DISPSTAT = DISPSTAT_VBLANK_INTR;
+ REG_IME = INTR_FLAG_VBLANK;
+ msg_load_gfx();
+ gMainCallbackState = MAINCB_INIT;
+ gUnknown_3001194 = 0;
+ for (;;)
+ {
+ VBlankIntrWait();
+ ReadKeys();
+ main_callback(&gMainCallbackState, gUnknown_30011A0, gSharedMem);
+ }
+}
+
+void dummy_intr_1(void)
+{}
+
+void dummy_intr_0(void)
+{}
+
+void ReadKeys(void)
+{
+ u16 keyInput = REG_KEYINPUT ^ KEYS_MASK;
+ gNewKeys = keyInput & ~gHeldKeys;
+ gHeldKeys = keyInput;
+}
+
+void fill_palette(const u8 * src, u16 * dest, u8 value)
+{
+ s32 i;
+ for (i = 0; src[i] != 0; i++)
+ dest[i] = src[i] | value << 12;
+}
+
+bool32 berry_fix_memcmp(const char * src1, const char * src2, size_t size)
+{
+ s32 i;
+ for (i = 0; i < size; i++)
+ {
+ if (src1[i] != src2[i])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+s32 validate_rom_header_internal(void)
+{
+ char languageCode = *(RomHeaderGameCode + 3);
+ s32 softwareVersion = *RomHeaderSoftwareVersion;
+ s32 shouldUpdate = -1;
+ s32 i;
+ for (i = 0; i < ARRAY_COUNT(gVersionData); i++)
+ {
+ if (languageCode == gVersionData[i][0])
+ {
+ if (softwareVersion >= gVersionData[i][1])
+ {
+ shouldUpdate = 0;
+ }
+ else
+ {
+ shouldUpdate = 1;
+ }
+ break;
+ }
+ }
+ if (shouldUpdate != -1)
+ {
+ if (berry_fix_memcmp(RomHeaderGameTitle, gRubyTitleAndCode, 15) == TRUE)
+ {
+ if (shouldUpdate == 0)
+ return RUBY_NONEED;
+ else
+ {
+ gGameVersion = VERSION_RUBY;
+ return RUBY_UPDATABLE;
+ }
+ }
+ else if (berry_fix_memcmp(RomHeaderGameTitle, gSapphireTitleAndCode, 15) == TRUE)
+ {
+ if (shouldUpdate == 0)
+ return SAPPHIRE_NONEED;
+ else
+ {
+ gGameVersion = VERSION_SAPPHIRE;
+ return SAPPHIRE_UPDATABLE;
+ }
+ }
+ }
+ return INVALID;
+}
+
+s32 validate_rom_header(void)
+{
+ if (*RomHeaderMakerCode == '0' && *(RomHeaderMakerCode + 1) == '1' && *RomHeaderMagic == 0x96)
+ return validate_rom_header_internal();
+ else
+ return INVALID;
+}
+
+void main_callback(u32 * state, void * unused1, void * unused2)
+{
+ u8 year;
+ switch (*state)
+ {
+ case MAINCB_INIT:
+ msg_display(MSGBOX_WILL_NOW_UPDATE);
+ if (++gInitialWaitTimer >= 180)
+ {
+ gInitialWaitTimer = 0;
+ gUpdateSuccessful = 0;
+ switch (validate_rom_header())
+ {
+ case SAPPHIRE_UPDATABLE:
+ case RUBY_UPDATABLE: // Should Update Ruby
+ ++(*state); // MAINCB_CHECK_RTC
+ break;
+ case INVALID: // Invalid header
+ *state = MAINCB_ERROR;
+ break;
+ case SAPPHIRE_NONEED: // Should not update Sapphire
+ case RUBY_NONEED: // Should not update Ruby
+ *state = MAINCB_NO_NEED_TO_FIX;
+ break;
+ }
+ }
+ break;
+ case MAINCB_CHECK_RTC:
+ if (!rtc_maincb_is_rtc_working())
+ *state = MAINCB_ERROR;
+ else
+ ++(*state); // MAINCB_CHECK_FLASH
+ break;
+ case MAINCB_CHECK_FLASH:
+ if (flash_maincb_ident_is_valid() == TRUE)
+ ++(*state); // MAINCB_READ_SAVE
+ else
+ *state = MAINCB_ERROR;
+ break;
+ case MAINCB_READ_SAVE:
+ if (flash_maincb_read_save(0) == SAVE_STATUS_OK)
+ ++(*state); // MAINCB_CHECK_TIME
+ else
+ *state = MAINCB_ERROR;
+ break;
+ case MAINCB_CHECK_TIME:
+ if (rtc_maincb_is_time_since_last_berry_update_positive(&year) == TRUE)
+ {
+ if (year == 0)
+ ++(*state); // MAINCB_FIX_DATE
+ else
+ *state = MAINCB_CHECK_PACIFIDLOG_TM;
+ }
+ else
+ {
+ if (year != 1)
+ *state = MAINCB_YEAR_MAKES_NO_SENSE;
+ else
+ ++(*state); // MAINCB_FIX_DATE
+ }
+ break;
+ case MAINCB_FIX_DATE:
+ rtc_maincb_fix_date();
+ gUpdateSuccessful |= 1;
+ *state = MAINCB_CHECK_PACIFIDLOG_TM;
+ break;
+ case MAINCB_CHECK_PACIFIDLOG_TM:
+ if (flash_maincb_check_need_reset_pacifidlog_tm() == TRUE)
+ *state = MAINCB_FINISHED;
+ else
+ *state = MAINCB_FIX_PACIFIDLOG_TM;
+ break;
+ case MAINCB_FIX_PACIFIDLOG_TM:
+ msg_display(MSGBOX_UPDATING);
+ if (flash_maincb_reset_pacifidlog_tm() == TRUE)
+ {
+ gUpdateSuccessful |= 1;
+ *state = MAINCB_FINISHED;
+ }
+ else
+ *state = MAINCB_ERROR;
+ break;
+ case MAINCB_FINISHED:
+ if (gUpdateSuccessful == 0)
+ *state = MAINCB_NO_NEED_TO_FIX;
+ else
+ msg_display(MSGBOX_HAS_BEEN_UPDATED);
+ break;
+ case MAINCB_NO_NEED_TO_FIX:
+ msg_display(MSGBOX_NO_NEED_TO_UPDATE);
+ break;
+ case MAINCB_YEAR_MAKES_NO_SENSE:
+ msg_display(MSGBOX_UNABLE_TO_UPDATE);
+ break;
+ case MAINCB_ERROR:
+ msg_display(MSGBOX_UNABLE_TO_UPDATE);
+ break;
+ }
+}
+
+void DBG_LoadDigitsPal(void)
+{
+ const u16 * src;
+ s32 i;
+ register vu16 * dest asm("r3") = (vu16 *)BG_PLTT + 1;
+ DmaFill16(3, RGB(31, 31, 31), (vu16 *)BG_PLTT, BG_PLTT_SIZE);
+ src = sDebugPals;
+ for (i = 0; i < 4; i++)
+ {
+ *dest = *src;
+ dest += 16;
+ src++;
+ }
+}
+
+void DBG_LoadDigits(void)
+{
+ DmaFill16(3, 0x1111, (void *)VRAM + 0x8420, 0x1800);
+ DmaCopy32(3, sDebugDigitsGfx, (void *)VRAM + 0x8600, 0x200);
+ DBG_LoadDigitsPal();
+}
diff --git a/berry_fix/payload/src/rtc.c b/berry_fix/payload/src/rtc.c
new file mode 100644
index 000000000..97692e205
--- /dev/null
+++ b/berry_fix/payload/src/rtc.c
@@ -0,0 +1,346 @@
+#include <gba/gba.h>
+#include <siirtc.h>
+#include "global.h"
+#include "main.h"
+
+struct Time gTimeSinceBerryUpdate;
+struct Time gRtcUTCTime;
+
+static u16 sRtcProbeStatus;
+static struct SiiRtcInfo sRtcInfoBuffer;
+static u8 sRtcProbeCode;
+static u16 sImeBak;
+static struct SiiRtcInfo sRtcInfoWork;
+
+const struct SiiRtcInfo sDefaultRTC = {
+ .year = 0, // 2000
+ .month = 1, // January
+ .day = 1, // 01
+ .dayOfWeek = 0,
+ .hour = 0,
+ .minute = 0,
+ .second = 0,
+ .status = 0,
+ .alarmHour = 0,
+ .alarmMinute = 0
+};
+const s32 sDaysPerMonth[] = {
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+};
+
+void rtc_get_status_and_datetime(struct SiiRtcInfo *);
+u16 rtc_validate_datetime(struct SiiRtcInfo *);
+
+
+void rtc_intr_disable(void)
+{
+ sImeBak = REG_IME;
+ REG_IME = 0;
+}
+
+void rtc_intr_enable(void)
+{
+ REG_IME = sImeBak;
+}
+
+s32 bcd_to_hex(u8 a0)
+{
+ if (a0 >= 0xa0 || (a0 & 0xF) >= 10)
+ return 0xFF;
+ return ((a0 >> 4) & 0xF) * 10 + (a0 & 0xF);
+}
+
+bool8 is_leap_year(u8 year)
+{
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
+ return TRUE;
+ return FALSE;
+}
+
+u16 rtc_count_days_parameterized(u8 year, u8 month, u8 day)
+{
+ u16 numDays = 0;
+ s32 i;
+ for (i = year - 1; i > 0; i--)
+ {
+ numDays += 365;
+ if (is_leap_year(i) == TRUE)
+ numDays++;
+ }
+ for (i = 0; i < month - 1; i++)
+ numDays += sDaysPerMonth[i];
+ if (month > MONTH_FEB && is_leap_year(year) == TRUE)
+ numDays++;
+ numDays += day;
+ return numDays;
+}
+
+u16 rtc_count_days_from_info(struct SiiRtcInfo *info)
+{
+ return rtc_count_days_parameterized(bcd_to_hex(info->year), bcd_to_hex(info->month), bcd_to_hex(info->day));
+}
+
+static void rtc_probe_status(void)
+{
+ sRtcProbeStatus = 0;
+ rtc_intr_disable();
+ SiiRtcUnprotect();
+ sRtcProbeCode = SiiRtcProbe();
+ rtc_intr_enable();
+ if ((sRtcProbeCode & 0xF) != 1)
+ sRtcProbeStatus = 1;
+ else
+ {
+ if (sRtcProbeCode & 0xF0)
+ sRtcProbeStatus = 2;
+ else
+ sRtcProbeStatus = 0;
+ rtc_get_status_and_datetime(&sRtcInfoBuffer);
+ sRtcProbeStatus = rtc_validate_datetime(&sRtcInfoBuffer);
+ }
+}
+
+u16 rtc_get_probe_status(void)
+{
+ return sRtcProbeStatus;
+}
+
+void sub_020106EC(struct SiiRtcInfo * info)
+{
+ if (sRtcProbeStatus & 0xFF0)
+ *info = sDefaultRTC;
+ else
+ rtc_get_status_and_datetime(info);
+}
+
+void rtc_get_datetime(struct SiiRtcInfo * info)
+{
+ rtc_intr_disable();
+ SiiRtcGetDateTime(info);
+ rtc_intr_enable();
+}
+
+void rtc_get_status(struct SiiRtcInfo * info)
+{
+ rtc_intr_disable();
+ SiiRtcGetStatus(info);
+ rtc_intr_enable();
+}
+
+void rtc_get_status_and_datetime(struct SiiRtcInfo * info)
+{
+ rtc_get_status(info);
+ rtc_get_datetime(info);
+}
+
+u16 rtc_validate_datetime(struct SiiRtcInfo * info)
+{
+ s32 year, month, day;
+ u16 r4 = (info->status & SIIRTCINFO_POWER) ? 0x20 : 0;
+ if (!(info->status & SIIRTCINFO_24HOUR))
+ r4 |= 0x10;
+ year = bcd_to_hex(info->year);
+ if (year == 0xFF)
+ r4 |= 0x40;
+ month = bcd_to_hex(info->month);
+ if (month == 0xFF || month == 0 || month > 12)
+ r4 |= 0x80;
+ day = bcd_to_hex(info->day);
+ if (day == 0xFF)
+ r4 |= 0x100;
+ if (month == MONTH_FEB)
+ {
+ if (day > is_leap_year(year) + sDaysPerMonth[1])
+ r4 |= 0x100;
+ }
+ else
+ {
+ if (day > sDaysPerMonth[month - 1])
+ r4 |= 0x100;
+ }
+ day = bcd_to_hex(info->hour);
+ if (day > 24)
+ r4 |= 0x200;
+ day = bcd_to_hex(info->minute);
+ if (day > 60)
+ r4 |= 0x400;
+ day = bcd_to_hex(info->second);
+ if (day > 60)
+ r4 |= 0x800;
+ return r4;
+}
+
+void rtc_reset(void)
+{
+ rtc_intr_disable();
+ SiiRtcReset();
+ rtc_intr_enable();
+}
+
+void rtc_sub_time_from_datetime(struct SiiRtcInfo * datetime, struct Time * dest, struct Time * timediff)
+{
+ u16 r4 = rtc_count_days_from_info(datetime);
+ dest->seconds = bcd_to_hex(datetime->second) - timediff->seconds;
+ dest->minutes = bcd_to_hex(datetime->minute) - timediff->minutes;
+ dest->hours = bcd_to_hex(datetime->hour) - timediff->hours;
+ dest->days = r4 - timediff->days;
+ if (dest->seconds < 0)
+ {
+ dest->seconds += 60;
+ dest->minutes--;
+ }
+ if (dest->minutes < 0)
+ {
+ dest->minutes += 60;
+ dest->hours--;
+ }
+ if (dest->hours < 0)
+ {
+ dest->hours += 24;
+ dest->days--;
+ }
+}
+
+void rtc_sub_time_from_time(struct Time * dest, struct Time * diff, struct Time * src)
+{
+ dest->seconds = src->seconds - diff->seconds;
+ dest->minutes = src->minutes - diff->minutes;
+ dest->hours = src->hours - diff->hours;
+ dest->days = src->days - diff->days;
+ if (dest->seconds < 0)
+ {
+ dest->seconds += 60;
+ dest->minutes--;
+ }
+ if (dest->minutes < 0)
+ {
+ dest->minutes += 60;
+ dest->hours--;
+ }
+ if (dest->hours < 0)
+ {
+ dest->hours += 24;
+ dest->days--;
+ }
+}
+
+bool32 rtc_maincb_is_rtc_working(void)
+{
+ rtc_probe_status();
+ if (rtc_get_probe_status() & 0xFF0)
+ return FALSE;
+ return TRUE;
+}
+
+void rtc_set_datetime(struct SiiRtcInfo * info)
+{
+ vu16 imeBak = REG_IME;
+ REG_IME = 0;
+ SiiRtcSetDateTime(info);
+ REG_IME = imeBak;
+}
+
+bool32 rtc_maincb_is_time_since_last_berry_update_positive(u8 * a0)
+{
+ rtc_get_status_and_datetime(&sRtcInfoWork);
+ *a0 = bcd_to_hex(sRtcInfoWork.year);
+ rtc_sub_time_from_datetime(&sRtcInfoWork, &gRtcUTCTime, LocalTimeOffset);
+ rtc_sub_time_from_time(&gTimeSinceBerryUpdate, LastBerryTreeUpdate, &gRtcUTCTime);
+ if (gTimeSinceBerryUpdate.days * 1440 + gTimeSinceBerryUpdate.hours * 60 + gTimeSinceBerryUpdate.minutes >= 0)
+ return TRUE;
+ return FALSE;
+}
+
+u32 hex_to_bcd(u8 a0)
+{
+ u32 r4;
+ if (a0 > 99)
+ return 0xFF;
+ r4 = Div(a0, 10) << 4;
+ r4 |= Mod(a0, 10);
+ return r4;
+}
+
+void sii_rtc_inc(u8 * a0)
+{
+ *a0 = hex_to_bcd(bcd_to_hex(*a0) + 1);
+}
+
+void sii_rtc_inc_month(struct SiiRtcInfo * a0)
+{
+ sii_rtc_inc(&a0->month);
+ if (bcd_to_hex(a0->month) > 12)
+ {
+ sii_rtc_inc(&a0->year);
+ a0->month = MONTH_JAN;
+ }
+}
+
+void sii_rtc_inc_day(struct SiiRtcInfo * a0)
+{
+ sii_rtc_inc(&a0->day);
+ if (bcd_to_hex(a0->day) > sDaysPerMonth[bcd_to_hex(a0->month) - 1])
+ {
+ if (!is_leap_year(bcd_to_hex(a0->year)) || bcd_to_hex(a0->month) != MONTH_FEB || bcd_to_hex(a0->day) != 29)
+ {
+ a0->day = 1;
+ sii_rtc_inc_month(a0);
+ }
+ }
+}
+
+bool32 rtc_is_past_feb_28_2000(struct SiiRtcInfo * a0)
+{
+ if (bcd_to_hex(a0->year) == 0)
+ {
+ if (bcd_to_hex(a0->month) == MONTH_JAN)
+ return FALSE;
+ if (bcd_to_hex(a0->month) > MONTH_FEB)
+ return TRUE;
+ if (bcd_to_hex(a0->day) == 29)
+ return TRUE;
+ return FALSE;
+ }
+ if (bcd_to_hex(a0->year) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+void rtc_maincb_fix_date(void)
+{
+ rtc_get_status_and_datetime(&sRtcInfoWork);
+ if (bcd_to_hex(sRtcInfoWork.year) == 0 || bcd_to_hex(sRtcInfoWork.year) == 1)
+ {
+ if (bcd_to_hex(sRtcInfoWork.year) == 1)
+ {
+ sRtcInfoWork.year = 2;
+ sRtcInfoWork.month = MONTH_JAN;
+ sRtcInfoWork.day = 2;
+ rtc_set_datetime(&sRtcInfoWork);
+ }
+ else
+ {
+ if (rtc_is_past_feb_28_2000(&sRtcInfoWork) == TRUE)
+ {
+ sii_rtc_inc_day(&sRtcInfoWork);
+ sii_rtc_inc(&sRtcInfoWork.year);
+ }
+ else
+ {
+ sii_rtc_inc(&sRtcInfoWork.year);
+ }
+ rtc_set_datetime(&sRtcInfoWork);
+ }
+ }
+}