Sync save

This commit is contained in:
GriffinR 2022-01-15 14:32:46 -05:00
parent 43d0fa817e
commit df4fa5e88b
5 changed files with 255 additions and 221 deletions

View File

@ -0,0 +1,8 @@
gLastWrittenSector
gLastSaveCounter
gLastKnownGoodSector
gDamagedSaveSectors
gSaveCounter
gReadWriteSector
gIncrementalSectorId
gFlashIdentIsValid

View File

@ -42,6 +42,13 @@ enum
SECTOR_CHECK, // unused
};
enum
{
SAVE_NORMAL, // Save full save slot
SAVE_SAVEBLOCKS, // Save just SaveBlock1 and SaveBlock2
SAVE_SAVEBLOCK2, // Save just SaveBlock2
};
enum MsgBoxUpdateMessage
{
MSGBOX_WILL_NOW_UPDATE = 0,
@ -62,13 +69,9 @@ struct SaveSector
}; // size is SECTOR_SIZE (0x1000)
#define SECTOR_SECURITY_OFFSET offsetof(struct SaveSector, security)
#define SECTOR_COUNTER_OFFSET offsetof(struct SaveSector, counter)
#define eSaveSection ((struct SaveSector *)0x2020000)
bool32 flash_maincb_ident_is_valid(void);
bool8 flash_maincb_read_save(u32);
bool32 BerryFix_IdentifyFlash(void);
bool8 BerryFix_LoadSave(u32);
void msg_load_gfx(void);
void msg_display(enum MsgBoxUpdateMessage);
bool32 flash_maincb_check_need_reset_pacifidlog_tm(void);

View File

@ -200,13 +200,13 @@ void main_callback(u32 * state, void * unused1, void * unused2)
++(*state); // MAINCB_CHECK_FLASH
break;
case MAINCB_CHECK_FLASH:
if (flash_maincb_ident_is_valid() == TRUE)
if (BerryFix_IdentifyFlash() == TRUE)
++(*state); // MAINCB_READ_SAVE
else
*state = MAINCB_ERROR;
break;
case MAINCB_READ_SAVE:
if (flash_maincb_read_save(0) == SAVE_STATUS_OK)
if (BerryFix_LoadSave(0) == SAVE_STATUS_OK)
++(*state); // MAINCB_CHECK_TIME
else
*state = MAINCB_ERROR;

View File

@ -3,31 +3,38 @@
#include "save.h"
#include "rtc.h"
/*
The Berry Fix Program contains a copy of most of Ruby/Sapphire's save code.
Much of it lies unused.
*/
struct SaveBlockChunk
{
u8 * data;
u16 size;
};
u8 WriteSaveBlockChunks(u16, const struct SaveBlockChunk *);
u8 WriteSingleChunk(u16, const struct SaveBlockChunk *);
u8 TryWriteSector(u8, u8 *);
static u8 WriteSaveSectorOrSlot(u16, const struct SaveBlockChunk *);
static u8 HandleWriteSector(u16, const struct SaveBlockChunk *);
static u8 TryWriteSector(u8, u8 *);
static u8 HandleReplaceSector(u16, const struct SaveBlockChunk *);
u8 TryReadAllSaveSectorsCurrentSlot(u16, const struct SaveBlockChunk *);
u8 ReadAllSaveSectorsCurrentSlot(u16, const struct SaveBlockChunk *);
u8 GetSaveValidStatus(const struct SaveBlockChunk *);
u32 DoReadFlashWholeSection(u8, struct SaveSector *);
u16 CalculateChecksum(const void *, u16);
static u8 TryLoadSaveSlot(u16, const struct SaveBlockChunk *);
static u8 CopySaveSlotData(u16, const struct SaveBlockChunk *);
static u8 GetSaveValidStatus(const struct SaveBlockChunk *);
static u32 ReadFlashSector(u8, struct SaveSector *);
static u16 CalculateChecksum(const void *, u16);
u16 gLastWrittenSector;
u32 gPrevSaveCounter;
u32 gLastSaveCounter;
u16 gLastKnownGoodSector;
u32 gDamagedSaveSectors;
u32 gSaveCounter;
struct SaveSector * gReadWriteSector;
u16 gCurSaveChunk;
u16 gIncrementalSectorId;
bool32 gFlashIdentIsValid;
#define gSaveDataBuffer ((struct SaveSector *)(EWRAM_START + 0x20000))
EWRAM_DATA struct SaveBlock2 gSaveBlock2 = {};
EWRAM_DATA struct SaveBlock1 gSaveBlock1 = {};
EWRAM_DATA struct PokemonStorage gPokemonStorage = {};
@ -62,7 +69,7 @@ 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)
bool32 BerryFix_IdentifyFlash(void)
{
gFlashIdentIsValid = TRUE;
if (!IdentifyFlash())
@ -74,61 +81,59 @@ bool32 flash_maincb_ident_is_valid(void)
return FALSE;
}
void Call_ReadFlash(u16 sectorNum, ptrdiff_t offset, void * dest, size_t size)
// Unused
static void BerryFix_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)
static u8 BerryFix_WriteSaveSectorOrSlot(u16 sectorId, const struct SaveBlockChunk * chunks)
{
return WriteSaveBlockChunks(a0, a1);
return WriteSaveSectorOrSlot(sectorId, chunks);
}
u8 Call_TryReadAllSaveSectorsCurrentSlot(u16 a0, const struct SaveBlockChunk * a1)
static u8 BerryFix_TryLoadSaveSlot(u16 sectorId, const struct SaveBlockChunk * chunks)
{
return TryReadAllSaveSectorsCurrentSlot(a0, a1);
return TryLoadSaveSlot(sectorId, chunks);
}
u32 * GetDamagedSaveSectorsPtr(void)
static u32 * BerryFix_GetDamagedSaveSectors(void)
{
return &gDamagedSaveSectors;
}
s32 flash_write_save_block_chunks(u8 a0)
static s32 BerryFix_Save(u8 mode)
{
u8 i;
switch (a0)
switch (mode)
{
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;
case SAVE_NORMAL:
default:
BerryFix_WriteSaveSectorOrSlot(FULL_SAVE_SLOT, sSaveBlockChunks);
break;
case SAVE_SAVEBLOCKS:
for (i = SECTOR_ID_SAVEBLOCK2; i <= SECTOR_ID_SAVEBLOCK1_END; i++)
BerryFix_WriteSaveSectorOrSlot(i, sSaveBlockChunks);
break;
case SAVE_SAVEBLOCK2:
BerryFix_WriteSaveSectorOrSlot(SECTOR_ID_SAVEBLOCK2, sSaveBlockChunks);
break;
}
return 0;
}
u8 flash_write_save_block_chunks_check_damage(u8 a0)
u8 BerryFix_TrySave(u8 mode)
{
flash_write_save_block_chunks(a0);
if (*GetDamagedSaveSectorsPtr() == 0)
return 1;
return 0xFF;
BerryFix_Save(mode);
if (*BerryFix_GetDamagedSaveSectors() == 0)
return SAVE_STATUS_OK;
return SAVE_STATUS_ERROR;
}
u8 flash_maincb_read_save(u32 unused)
u8 BerryFix_LoadSave(u32 unused)
{
return Call_TryReadAllSaveSectorsCurrentSlot(0xFFFF, sSaveBlockChunks);
return BerryFix_TryLoadSaveSlot(FULL_SAVE_SLOT, sSaveBlockChunks);
}
void msg_load_gfx(void)
@ -139,7 +144,7 @@ void msg_load_gfx(void)
REG_BLDCNT = 0;
LZ77UnCompVram(gInfoMessagesGfx, (void *)BG_VRAM);
LZ77UnCompVram(gInfoMessagesTilemap, (void *)BG_SCREEN_ADDR(28));
CpuCopy16(gInfoMessagesPal, (void *)BG_PLTT, 0x200);
CpuCopy16(gInfoMessagesPal, (void *)BG_PLTT, BG_PLTT_SIZE);
REG_BG0CNT = BGCNT_SCREENBASE(28) | BGCNT_TXT512x512;
REG_DISPCNT = DISPCNT_BG0_ON;
}
@ -171,199 +176,220 @@ void msg_display(enum MsgBoxUpdateMessage a0)
}
}
void Save_EraseAllData(void)
// Unused
static void ClearSaveData(void)
{
u16 i;
for (i = 0; i < 32; i++)
for (i = 0; i < SECTORS_COUNT; i++)
EraseFlashSector(i);
}
void Save_ResetSaveCounters(void)
// Unused
static void Save_ResetSaveCounters(void)
{
gSaveCounter = 0;
gLastWrittenSector = 0;
gDamagedSaveSectors = 0;
}
bool32 SetSectorDamagedStatus(u8 op, u8 sectorNum)
static bool32 SetDamagedSectorBits(u8 op, u8 sectorId)
{
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;
case SECTOR_DAMAGED:
gDamagedSaveSectors |= (1 << sectorId);
break;
case SECTOR_OK:
gDamagedSaveSectors &= ~(1 << sectorId);
break;
case SECTOR_CHECK: // unused
if (gDamagedSaveSectors & (1 << sectorId))
retVal = TRUE;
break;
}
return retVal;
}
u8 WriteSaveBlockChunks(u16 chunkId, const struct SaveBlockChunk *chunks)
static u8 WriteSaveSectorOrSlot(u16 sectorId, const struct SaveBlockChunk *chunks)
{
u32 retVal;
u32 status;
u16 i;
gReadWriteSector = eSaveSection;
gReadWriteSector = gSaveDataBuffer;
if (chunkId != 0xFFFF) // write single chunk
if (sectorId != FULL_SAVE_SLOT)
{
retVal = WriteSingleChunk(chunkId, chunks);
// A sector was specified, just write that sector.
status = HandleWriteSector(sectorId, chunks);
}
else // write all chunks
else
{
// No sector was specified, write full save slot.
gLastKnownGoodSector = gLastWrittenSector;
gPrevSaveCounter = gSaveCounter;
gLastSaveCounter = gSaveCounter;
gLastWrittenSector++;
gLastWrittenSector %= NUM_SECTORS_PER_SLOT;
gSaveCounter++;
retVal = SAVE_STATUS_OK;
status = SAVE_STATUS_OK;
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
WriteSingleChunk(i, chunks);
HandleWriteSector(i, chunks);
// Check for any bad sectors
if (gDamagedSaveSectors != 0) // skip the damaged sector.
if (gDamagedSaveSectors)
{
retVal = SAVE_STATUS_ERROR;
// At least one sector save failed
status = SAVE_STATUS_ERROR;
gLastWrittenSector = gLastKnownGoodSector;
gSaveCounter = gPrevSaveCounter;
gSaveCounter = gLastSaveCounter;
}
}
return retVal;
return status;
}
u8 WriteSingleChunk(u16 chunkId, const struct SaveBlockChunk * chunks)
static u8 HandleWriteSector(u16 sectorId, const struct SaveBlockChunk * chunks)
{
u16 i;
u16 sectorNum;
u8 *chunkData;
u16 chunkSize;
u8 *data;
u16 size;
// select sector number
sectorNum = chunkId + gLastWrittenSector;
// Adjust sector id for current save slot
sectorNum = sectorId + gLastWrittenSector;
sectorNum %= NUM_SECTORS_PER_SLOT;
// select save slot
sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % 2);
sectorNum += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
chunkData = chunks[chunkId].data;
chunkSize = chunks[chunkId].size;
// Get current save data
data = chunks[sectorId].data;
size = chunks[sectorId].size;
// clear save section.
for (i = 0; i < sizeof(struct SaveSector); i++)
// Clear temp save sector
for (i = 0; i < SECTOR_SIZE; i++)
((u8 *)gReadWriteSector)[i] = 0;
gReadWriteSector->id = chunkId;
// Set footer data
gReadWriteSector->id = sectorId;
gReadWriteSector->security = SECTOR_SECURITY_NUM;
gReadWriteSector->counter = gSaveCounter;
for (i = 0; i < chunkSize; i++)
gReadWriteSector->data[i] = chunkData[i];
gReadWriteSector->checksum = CalculateChecksum(chunkData, chunkSize);
// Copy current data to temp buffer for writing
for (i = 0; i < size; i++)
gReadWriteSector->data[i] = data[i];
gReadWriteSector->checksum = CalculateChecksum(data, size);
return TryWriteSector(sectorNum, gReadWriteSector->data);
}
u8 HandleWriteSectorNBytes(u8 sectorNum, u8 *data, u16 size)
// Unused
static u8 HandleWriteSectorNBytes(u8 sectorId, u8 *data, u16 size)
{
u16 i;
struct SaveSector *section = eSaveSection;
struct SaveSector *sector = gSaveDataBuffer;
for (i = 0; i < sizeof(struct SaveSector); i++)
((char *)section)[i] = 0;
// Clear temp save sector
for (i = 0; i < SECTOR_SIZE; i++)
((u8 *)sector)[i] = 0;
section->security = SECTOR_SECURITY_NUM;
sector->security = SECTOR_SECURITY_NUM;
// Copy data to temp buffer for writing
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.
sector->data[i] = data[i];
return TryWriteSector(sectorNum, section->data);
sector->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(sectorId, sector->data);
}
u8 TryWriteSector(u8 sectorNum, u8 *data)
static u8 TryWriteSector(u8 sectorNum, u8 *data)
{
if (ProgramFlashSectorAndVerify(sectorNum, data) != 0) // is damaged?
{
SetSectorDamagedStatus(SECTOR_DAMAGED, sectorNum); // set damaged sector bits.
// Failed
SetDamagedSectorBits(SECTOR_DAMAGED, sectorNum);
return SAVE_STATUS_ERROR;
}
else
{
SetSectorDamagedStatus(SECTOR_OK, sectorNum); // unset damaged sector bits. it's safe now.
// Succeeded
SetDamagedSectorBits(SECTOR_OK, sectorNum);
return SAVE_STATUS_OK;
}
}
u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunk) // chunk is unused
// Unused
static u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *chunks)
{
gReadWriteSector = eSaveSection;
gReadWriteSector = gSaveDataBuffer;
gLastKnownGoodSector = gLastWrittenSector;
gPrevSaveCounter = gSaveCounter;
gLastSaveCounter = gSaveCounter;
gLastWrittenSector++;
gLastWrittenSector %= NUM_SECTORS_PER_SLOT;
gSaveCounter++;
gCurSaveChunk = 0;
gIncrementalSectorId = 0;
gDamagedSaveSectors = 0;
return 0;
}
u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunk)
// Unused
static u32 RestoreSaveBackupVars(const struct SaveBlockChunk *chunks)
{
gReadWriteSector = eSaveSection;
gReadWriteSector = gSaveDataBuffer;
gLastKnownGoodSector = gLastWrittenSector;
gPrevSaveCounter = gSaveCounter;
gCurSaveChunk = 0;
gLastSaveCounter = gSaveCounter;
gIncrementalSectorId = 0;
gDamagedSaveSectors = 0;
return 0;
}
u8 WriteSingleChunkAndIncrement(u16 a1, const struct SaveBlockChunk * chunk)
// Unused
static u8 HandleWriteIncrementalSector(u16 numSectors, const struct SaveBlockChunk * chunks)
{
u8 retVal;
u8 status;
if (gCurSaveChunk < a1 - 1)
if (gIncrementalSectorId < numSectors - 1)
{
retVal = SAVE_STATUS_OK;
WriteSingleChunk(gCurSaveChunk, chunk);
gCurSaveChunk++;
status = SAVE_STATUS_OK;
HandleWriteSector(gIncrementalSectorId, chunks);
gIncrementalSectorId++;
if (gDamagedSaveSectors)
{
retVal = SAVE_STATUS_ERROR;
status = SAVE_STATUS_ERROR;
gLastWrittenSector = gLastKnownGoodSector;
gSaveCounter = gPrevSaveCounter;
gSaveCounter = gLastSaveCounter;
}
}
else
{
retVal = SAVE_STATUS_ERROR;
// Exceeded max sector, finished
status = SAVE_STATUS_ERROR;
}
return retVal;
return status;
}
u8 ErasePreviousChunk(u16 a1, const struct SaveBlockChunk *chunk)
// Unused
static u8 HandleReplaceSectorAndVerify(u16 sectorId, const struct SaveBlockChunk *chunks)
{
u8 retVal = SAVE_STATUS_OK;
u8 status = SAVE_STATUS_OK;
HandleReplaceSector(a1 - 1, chunk);
HandleReplaceSector(sectorId - 1, chunks);
if (gDamagedSaveSectors)
{
retVal = SAVE_STATUS_ERROR;
status = SAVE_STATUS_ERROR;
gLastWrittenSector = gLastKnownGoodSector;
gSaveCounter = gPrevSaveCounter;
gSaveCounter = gLastSaveCounter;
}
return retVal;
return status;
}
static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *locations)
// Unused
// Similar to HandleWriteSector, but fully erases the sector first, and skips writing the first security byte
static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *chunks)
{
u16 i;
u16 sector;
@ -377,8 +403,8 @@ static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *locatio
sector += NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
// Get current save data
data = locations[sectorId].data;
size = locations[sectorId].size;
data = chunks[sectorId].data;
size = chunks[sectorId].size;
// Clear temp save sector.
for (i = 0; i < SECTOR_SIZE; i++)
@ -412,7 +438,7 @@ static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *locatio
if (status == SAVE_STATUS_ERROR)
{
// Writing save data failed
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
return SAVE_STATUS_ERROR;
}
else
@ -434,19 +460,20 @@ static u8 HandleReplaceSector(u16 sectorId, const struct SaveBlockChunk *locatio
if (status == SAVE_STATUS_ERROR)
{
// Writing security/counter failed
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
return SAVE_STATUS_ERROR;
}
else
{
// Succeeded
SetSectorDamagedStatus(SECTOR_OK, sector);
SetDamagedSectorBits(SECTOR_OK, sector);
return SAVE_STATUS_OK;
}
}
}
static u8 CopySectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *locations)
// Unused
static u8 CopySectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *chunks)
{
// Adjust sector id for current save slot
u16 sector = sectorId + gLastWrittenSector - 1;
@ -457,19 +484,20 @@ static u8 CopySectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *loca
if (ProgramFlashByte(sector, SECTOR_SECURITY_OFFSET, ((u8 *)gReadWriteSector)[SECTOR_SECURITY_OFFSET]))
{
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
gLastWrittenSector = gLastKnownGoodSector;
gSaveCounter = gPrevSaveCounter;
gSaveCounter = gLastSaveCounter;
return SAVE_STATUS_ERROR;
}
else
{
SetSectorDamagedStatus(SECTOR_OK, sector);
SetDamagedSectorBits(SECTOR_OK, sector);
return SAVE_STATUS_OK;
}
}
static u8 WriteSectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *locations)
// Unused
static u8 WriteSectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *chunks)
{
// Adjust sector id for current save slot
u16 sector = sectorId + gLastWrittenSector - 1;
@ -480,52 +508,57 @@ static u8 WriteSectorSecurityByte(u16 sectorId, const struct SaveBlockChunk *loc
if (ProgramFlashByte(sector, SECTOR_SECURITY_OFFSET, SECTOR_SECURITY_NUM & 0xFF))
{
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
SetSectorDamagedStatus(SECTOR_DAMAGED, sector);
SetDamagedSectorBits(SECTOR_DAMAGED, sector);
gLastWrittenSector = gLastKnownGoodSector;
gSaveCounter = gPrevSaveCounter;
gSaveCounter = gLastSaveCounter;
return SAVE_STATUS_ERROR;
}
else
{
// Succeeded
SetSectorDamagedStatus(SECTOR_OK, sector);
SetDamagedSectorBits(SECTOR_OK, sector);
return SAVE_STATUS_OK;
}
}
u8 TryReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunk)
static u8 TryLoadSaveSlot(u16 sectorId, const struct SaveBlockChunk *chunks)
{
u8 retVal;
gReadWriteSector = eSaveSection;
if (a1 != 0xFFFF)
u8 status;
gReadWriteSector = gSaveDataBuffer;
if (sectorId != FULL_SAVE_SLOT)
{
retVal = SAVE_STATUS_ERROR;
// This function may not be used with a specific sector id
status = SAVE_STATUS_ERROR;
}
else
{
retVal = GetSaveValidStatus(chunk);
ReadAllSaveSectorsCurrentSlot(0xFFFF, chunk);
status = GetSaveValidStatus(chunks);
CopySaveSlotData(FULL_SAVE_SLOT, chunks);
}
return retVal;
return status;
}
u8 ReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunks)
// sectorId arg is ignored, this always reads the full save slot
static u8 CopySaveSlotData(u16 sectorId, const struct SaveBlockChunk *chunks)
{
u16 i;
u16 checksum;
u16 sector = NUM_SECTORS_PER_SLOT * (gSaveCounter % 2);
u16 slotOffset = NUM_SECTORS_PER_SLOT * (gSaveCounter % NUM_SAVE_SLOTS);
u16 id;
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
{
DoReadFlashWholeSection(i + sector, gReadWriteSector);
ReadFlashSector(i + slotOffset, gReadWriteSector);
id = gReadWriteSector->id;
if (id == 0)
gLastWrittenSector = i;
checksum = CalculateChecksum(gReadWriteSector->data, chunks[id].size);
if (gReadWriteSector->security == SECTOR_SECURITY_NUM
&& gReadWriteSector->checksum == checksum)
// Only copy data for sectors whose security and checksum fields are correct
if (gReadWriteSector->security == SECTOR_SECURITY_NUM && gReadWriteSector->checksum == checksum)
{
u16 j;
for (j = 0; j < chunks[id].size; j++)
@ -533,13 +566,13 @@ u8 ReadAllSaveSectorsCurrentSlot(u16 a1, const struct SaveBlockChunk *chunks)
}
}
return 1;
return SAVE_STATUS_OK;
}
u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
static u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
{
u16 sector;
bool8 signatureValid;
u16 i;
bool8 securityPassed;
u16 checksum;
u32 slot1saveCounter = 0;
u32 slot2saveCounter = 0;
@ -548,15 +581,15 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
u32 validSectors;
const u32 ALL_SECTORS = (1 << NUM_SECTORS_PER_SLOT) - 1; // bitmask of all saveblock sectors
// check save slot 1.
// Check save slot 1
validSectors = 0;
signatureValid = FALSE;
for (sector = 0; sector < NUM_SECTORS_PER_SLOT; sector++)
securityPassed = FALSE;
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
{
DoReadFlashWholeSection(sector, gReadWriteSector);
ReadFlashSector(i, gReadWriteSector);
if (gReadWriteSector->security == SECTOR_SECURITY_NUM)
{
signatureValid = TRUE;
securityPassed = TRUE;
checksum = CalculateChecksum(gReadWriteSector->data, chunks[gReadWriteSector->id].size);
if (gReadWriteSector->checksum == checksum)
{
@ -566,7 +599,7 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
}
}
if (signatureValid)
if (securityPassed)
{
if (validSectors == ALL_SECTORS)
slot1Status = SAVE_STATUS_OK;
@ -575,18 +608,19 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
}
else
{
// No sectors in slot 1 have the security number, treat it as empty
slot1Status = SAVE_STATUS_EMPTY;
}
// check save slot 2.
// Check save slot 2
validSectors = 0;
signatureValid = FALSE;
for (sector = 0; sector < NUM_SECTORS_PER_SLOT; sector++)
securityPassed = FALSE;
for (i = 0; i < NUM_SECTORS_PER_SLOT; i++)
{
DoReadFlashWholeSection(NUM_SECTORS_PER_SLOT + sector, gReadWriteSector);
ReadFlashSector(NUM_SECTORS_PER_SLOT + i, gReadWriteSector);
if (gReadWriteSector->security == SECTOR_SECURITY_NUM)
{
signatureValid = TRUE;
securityPassed = TRUE;
checksum = CalculateChecksum(gReadWriteSector->data, chunks[gReadWriteSector->id].size);
if (gReadWriteSector->checksum == checksum)
{
@ -596,7 +630,7 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
}
}
if (signatureValid)
if (securityPassed)
{
if (validSectors == ALL_SECTORS)
slot2Status = SAVE_STATUS_OK;
@ -605,13 +639,15 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
}
else
{
// No sectors in slot 2 have the security number, treat it as empty.
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 ((slot1saveCounter == -1 && slot2saveCounter == 0)
|| (slot1saveCounter == 0 && slot2saveCounter == -1))
{
if ((unsigned)(slot1saveCounter + 1) < (unsigned)(slot2saveCounter + 1))
gSaveCounter = slot2saveCounter;
@ -628,69 +664,78 @@ u8 GetSaveValidStatus(const struct SaveBlockChunk *chunks)
return SAVE_STATUS_OK;
}
// One or both save slots are not OK
if (slot1Status == SAVE_STATUS_OK)
{
gSaveCounter = slot1saveCounter;
if (slot2Status == SAVE_STATUS_ERROR)
return SAVE_STATUS_ERROR;
return SAVE_STATUS_ERROR; // Slot 2 errored
else
return SAVE_STATUS_OK;
return SAVE_STATUS_OK; // Slot 1 is OK, slot 2 is empty
}
if (slot2Status == SAVE_STATUS_OK)
{
gSaveCounter = slot2saveCounter;
if (slot1Status == SAVE_STATUS_ERROR)
return SAVE_STATUS_ERROR;
return SAVE_STATUS_ERROR; // Slot 1 errored
else
return SAVE_STATUS_OK;
return SAVE_STATUS_OK; // Slot 2 is OK, slot 1 is empty
}
if (slot1Status == SAVE_STATUS_EMPTY && slot2Status == SAVE_STATUS_EMPTY)
// Neither slot is OK, check if both are empty
if (slot1Status == SAVE_STATUS_EMPTY
&& slot2Status == SAVE_STATUS_EMPTY)
{
gSaveCounter = 0;
gLastWrittenSector = 0;
return SAVE_STATUS_EMPTY;
}
// Both slots errored
gSaveCounter = 0;
gLastWrittenSector = 0;
return 2;
return SAVE_STATUS_CORRUPT;
}
u8 ReadSomeUnknownSectorAndVerify(u8 sector, u8 *data, u16 size)
// Unused
static u8 TryLoadSaveSector(u8 sectorId, u8 *data, u16 size)
{
u16 i;
struct SaveSector *section = eSaveSection;
DoReadFlashWholeSection(sector, section);
if (section->security == SECTOR_SECURITY_NUM)
struct SaveSector *sector = gSaveDataBuffer;
ReadFlashSector(sectorId, sector);
if (sector->security == SECTOR_SECURITY_NUM)
{
u16 checksum = CalculateChecksum(section->data, size);
if (section->id == checksum)
u16 checksum = CalculateChecksum(sector->data, size);
if (sector->id == checksum)
{
// Security and checksum are correct, copy data
for (i = 0; i < size; i++)
data[i] = section->data[i];
data[i] = sector->data[i];
return SAVE_STATUS_OK;
}
else
{
return 2;
// Incorrect checksum
return SAVE_STATUS_CORRUPT;
}
}
else
{
// Incorrect security value
return SAVE_STATUS_EMPTY;
}
}
u32 DoReadFlashWholeSection(u8 sector, struct SaveSector *section)
// Return value always ignored
static u32 ReadFlashSector(u8 sectorId, struct SaveSector *sector)
{
ReadFlash(sector, 0, section->data, sizeof(struct SaveSector));
ReadFlash(sectorId, 0, sector->data, SECTOR_SIZE);
return 1;
}
u16 CalculateChecksum(const void *data, u16 size)
static u16 CalculateChecksum(const void *data, u16 size)
{
u16 i;
u32 checksum = 0;
@ -704,15 +749,18 @@ u16 CalculateChecksum(const void *data, u16 size)
return ((checksum >> 16) + checksum);
}
void nullsub_0201182C()
// Unused
static void SaveDummy1()
{
}
void nullsub_02011830()
// Unused
static void SaveDummy2()
{
}
void nullsub_02011834()
// Unused
static void SaveDummy3()
{
}
@ -745,7 +793,7 @@ bool32 flash_maincb_reset_pacifidlog_tm(void)
if (gRtcUTCTime.days < 0)
return FALSE;
*GetVarPointer(VAR_PACIFIDLOG_TM_RECEIVED_DAY) = 1;
if (flash_write_save_block_chunks_check_damage(0) != TRUE)
if (BerryFix_TrySave(SAVE_NORMAL) != SAVE_STATUS_OK)
return FALSE;
return TRUE;
}
}

View File

@ -1,29 +1,4 @@
.include "main.o"
.include "rtc.o"
.align 4
gLastWrittenSector: @ 0x03001220
.space 0x4
gPrevSaveCounter: @ 0x03001224
.space 0x4
gLastKnownGoodSector: @ 0x03001228
.space 0x4
gDamagedSaveSectors: @ 0x0300122C
.space 0x4
gSaveCounter: @ 0x03001230
.space 0x4
gReadWriteSector: @ 0x03001234
.space 0x4
gCurSaveChunk:
.space 0x4
gFlashIdentIsValid: @ 0x0300123C
.space 0x4
.include "agb_flash.o"
.include "save.o"
.include "agb_flash.o"