diff options
author | PikalaxALT <pikalaxalt@gmail.com> | 2018-12-16 20:17:31 -0500 |
---|---|---|
committer | PikalaxALT <pikalaxalt@gmail.com> | 2018-12-16 20:18:44 -0500 |
commit | f91b71d3955e5df5455ec7a302b45c7f174100bf (patch) | |
tree | a5bd99bd61f0af04426b4e334a37695e2139bc8a /berry_fix/payload/src | |
parent | cc84f666554ecfba83d79a29831c978a21a2d3c5 (diff) |
Port over berry fix program
Diffstat (limited to 'berry_fix/payload/src')
-rw-r--r-- | berry_fix/payload/src/flash.c | 752 | ||||
-rw-r--r-- | berry_fix/payload/src/main.c | 289 | ||||
-rw-r--r-- | berry_fix/payload/src/rtc.c | 346 |
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); + } + } +} |