Document src/save.c

This commit is contained in:
cbt6
2022-08-05 14:18:53 +08:00
parent 49ea462d7f
commit 686c8383e5
15 changed files with 449 additions and 455 deletions
+6 -6
View File
@@ -1,12 +1,12 @@
gFirstSaveSector gLastWrittenSector
gPrevSaveCounter gLastSaveCounter
gLastKnownGoodSector gLastKnownGoodSector
gDamagedSaveSectors gDamagedSaveSectors
gSaveCounter gSaveCounter
gFastSaveSection gSaveDataBufferPtr
gUnknown_3005398 gIncrementalSectorId
gSaveUnusedVar gSaveUnusedVar
gSaveFileStatus gSaveFileStatus
gGameContinueCallback gGameContinueCallback
gRamSaveSectionLocations gRamSaveSectorLocations
gSaveSucceeded gSaveAttemptStatus
+48 -65
View File
@@ -3,7 +3,28 @@
#include "global.h" #include "global.h"
#define NUM_SECTORS 32 // defined in agb_flash but not in a header // Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer.
// Only 12 bytes of the footer are used.
#define SECTOR_DATA_SIZE 3968
#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 SPECIAL_SECTOR_SENTINEL 0xB39D
#define SECTOR_ID_SAVEBLOCK2 0
#define SECTOR_ID_SAVEBLOCK1_START 1
#define SECTOR_ID_SAVEBLOCK1_END 4
#define SECTOR_ID_PKMN_STORAGE_START 5
#define SECTOR_ID_PKMN_STORAGE_END 13
#define NUM_SECTORS_PER_SLOT 14
// Save Slot 1: 0-13; Save Slot 2: 14-27
#define SECTOR_ID_HOF_1 28
#define SECTOR_ID_HOF_2 29
#define SECTOR_ID_TRAINER_TOWER_1 30
#define SECTOR_ID_TRAINER_TOWER_2 31
#define SECTORS_COUNT 32
#define SAVE_STATUS_EMPTY 0 #define SAVE_STATUS_EMPTY 0
#define SAVE_STATUS_OK 1 #define SAVE_STATUS_OK 1
@@ -21,45 +42,33 @@ enum
SAVE_HALL_OF_FAME_ERASE_BEFORE, // unused SAVE_HALL_OF_FAME_ERASE_BEFORE, // unused
}; };
struct SaveBlockChunk // A save sector location holds a pointer to the data for a particular sector
// and the size of that data. Size cannot be greater than SECTOR_DATA_SIZE.
struct SaveSectorLocation
{ {
u8 *data; u8 *data;
u16 size; u16 size;
}; };
struct SaveSection struct SaveSector
{ {
u8 data[0xFF4]; u8 data[SECTOR_DATA_SIZE];
u8 unused[SECTOR_FOOTER_SIZE - 12]; // Unused portion of the footer
u16 id; u16 id;
u16 checksum; u16 checksum;
u32 signature; u32 signature;
u32 counter; u32 counter;
}; // size is 0x1000 }; // size is 0x1000
// headless save section? #define SECTOR_SIGNATURE_OFFSET offsetof(struct SaveSector, signature)
struct UnkSaveSection #define SECTOR_COUNTER_OFFSET offsetof(struct SaveSector, counter)
{
u8 data[0xFF4];
u32 signature;
}; // size is 0xFF8
struct SaveSectionOffsets // Special sector id value for certain save functions
{ // to indicate that all sectors should be used
u16 toAdd; // instead of a specific sector.
u16 size; #define FULL_SAVE_SLOT 0xFFFF
};
// Emerald changes this definition to be the sectors per slot. // operations for SetDamagedSectorBits
#define NUM_SECTORS_PER_SAVE_SLOT 14 // Number of sectors occupied by a save slot
#define UNKNOWN_CHECK_VALUE 0x8012025
#define SECTOR_SAVE1(n) (n)
#define SECTOR_SAVE2(n) ((n) + NUM_SECTORS_PER_SAVE_SLOT)
#define SECTOR_HOF(n) ((n) + 2 * NUM_SECTORS_PER_SAVE_SLOT)
#define SECTOR_TTOWER(n) ((n) + 2 * NUM_SECTORS_PER_SAVE_SLOT + 2)
// SetSectorDamagedStatus states
enum enum
{ {
ENABLE, ENABLE,
@@ -67,53 +76,27 @@ enum
CHECK // unused CHECK // unused
}; };
extern u16 gFirstSaveSector;
extern u32 gPrevSaveCounter;
extern u16 gLastKnownGoodSector;
extern u32 gDamagedSaveSectors; extern u32 gDamagedSaveSectors;
extern u32 gSaveCounter; extern struct SaveSector *gSaveDataBufferPtr; // the pointer is in fast IWRAM but points to the slower EWRAM.
extern struct SaveSection *gFastSaveSection; // the pointer is in fast IWRAM but may sometimes point to the slower EWRAM.
extern u16 gUnknown_3005398;
extern u16 gSaveUnusedVar;
extern u16 gSaveFileStatus; extern u16 gSaveFileStatus;
extern void (*gGameContinueCallback)(void); extern void (*gGameContinueCallback)(void);
extern struct SaveBlockChunk gRamSaveSectionLocations[0xE]; extern u16 gSaveAttemptStatus;
extern u16 gSaveSucceeded;
extern struct SaveSection gSaveDataBuffer; extern struct SaveSector gSaveDataBuffer;
void ClearSaveData(void); void ClearSaveData(void);
void Save_ResetSaveCounters(void); void Save_ResetSaveCounters(void);
bool32 SetSectorDamagedStatus(u8 op, u8 bit);
u8 save_write_to_flash(u16 a1, const struct SaveBlockChunk *location);
u8 HandleWriteSector(u16 a1, const struct SaveBlockChunk *location);
u8 HandleWriteSectorNBytes(u8 sector, u8 *data, u16 size);
u8 TryWriteSector(u8 sector, u8 *data);
u32 RestoreSaveBackupVarsAndIncrement(const struct SaveBlockChunk *location);
u32 RestoreSaveBackupVars(const struct SaveBlockChunk *location);
u8 sub_80D9AA4(u16 a1, const struct SaveBlockChunk *location);
u8 sub_80D9B04(u16 a1, const struct SaveBlockChunk *location);
u8 ClearSaveData_2(u16 a1, const struct SaveBlockChunk *location);
u8 sav12_xor_get(u16 a1, const struct SaveBlockChunk *location);
u8 sub_80D9D88(u16 a1, const struct SaveBlockChunk *location);
u8 sub_80D9E14(u16 a1, const struct SaveBlockChunk *location);
u8 sub_80D9E54(u16 a1, const struct SaveBlockChunk *location);
u8 GetSaveValidStatus(const struct SaveBlockChunk *location);
u8 sub_80DA120(u8 a1, u8 *data, u16 size);
u8 DoReadFlashWholeSection(u8 sector, struct SaveSection *section);
u16 CalculateChecksum(void *data, u16 size);
void UpdateSaveAddresses(void);
u8 HandleSavingData(u8 saveType); u8 HandleSavingData(u8 saveType);
u8 TrySavingData(u8 saveType); u8 TrySavingData(u8 saveType);
u8 SaveGame_AfterLinkTrade(void); bool8 LinkFullSave_Init(void);
bool8 AfterLinkTradeSaveFailed(void); bool8 LinkFullSave_WriteSector(void);
u8 ClearSaveAfterLinkTradeSaveFailure(void); bool8 LinkFullSave_ReplaceLastSector(void);
u8 sub_80DA434(void); bool8 LinkFullSave_SetLastSectorSignature(void);
u8 sub_80DA45C(void); bool8 WriteSaveBlock2(void);
bool8 sub_80DA4A0(void); bool8 WriteSaveBlock1Sector(void);
u8 Save_LoadGameData(u8 saveType); u8 LoadGameSave(u8 saveType);
u32 TryCopySpecialSaveSection(u8 sector, u8 *dst); u32 TryReadSpecialSaveSector(u8 sector, u8 *dst);
u32 TryWriteSpecialSaveSection(u8 sector, u8 *src); u32 TryWriteSpecialSaveSector(u8 sector, u8 *src);
void Task_LinkSave(u8 taskId); void Task_LinkFullSave(u8 taskId);
#endif // GUARD_SAVE_H #endif // GUARD_SAVE_H
+2 -2
View File
@@ -2224,10 +2224,10 @@ static u32 Cmd_SaveGame(struct BerryCrushGame * game, u8 *args)
DrawDialogueFrame(0, FALSE); DrawDialogueFrame(0, FALSE);
AddTextPrinterParameterized2(0, FONT_2, gText_SavingDontTurnOffThePower2, 0, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY); AddTextPrinterParameterized2(0, FONT_2, gText_SavingDontTurnOffThePower2, 0, NULL, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_GRAY);
CopyWindowToVram(0, COPYWIN_FULL); CopyWindowToVram(0, COPYWIN_FULL);
CreateTask(Task_LinkSave, 0); CreateTask(Task_LinkFullSave, 0);
break; break;
case 3: case 3:
if (FuncIsActiveTask(Task_LinkSave)) if (FuncIsActiveTask(Task_LinkFullSave))
return 0; return 0;
break; break;
case 4: case 4:
+4 -4
View File
@@ -47,11 +47,11 @@ static bool32 CEReaderTool_SaveTrainerTower_r(struct EReaderTrainerTowerSet * tt
memset(buffer, 0, 0x1000); memset(buffer, 0, 0x1000);
memcpy(buffer, ttdata, SEC30_SIZE); memcpy(buffer, ttdata, SEC30_SIZE);
buffer[1] = sub_815D654(); buffer[1] = sub_815D654();
if (TryWriteSpecialSaveSection(SECTOR_TTOWER(0), buffer) != TRUE) if (TryWriteSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_1, buffer) != TRUE)
return FALSE; return FALSE;
memset(buffer, 0, 0x1000); memset(buffer, 0, 0x1000);
memcpy(buffer, (u8 *)ttdata + SEC30_SIZE, SEC31_SIZE); memcpy(buffer, (u8 *)ttdata + SEC30_SIZE, SEC31_SIZE);
if (TryWriteSpecialSaveSection(SECTOR_TTOWER(1), buffer) != TRUE) if (TryWriteSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_2, buffer) != TRUE)
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
@@ -66,11 +66,11 @@ bool32 CEReaderTool_SaveTrainerTower(struct EReaderTrainerTowerSet * ttdata)
static bool32 CEReaderTool_LoadTrainerTower_r(struct EReaderTrainerTowerSet * ttdata, void *buffer) static bool32 CEReaderTool_LoadTrainerTower_r(struct EReaderTrainerTowerSet * ttdata, void *buffer)
{ {
if (TryCopySpecialSaveSection(SECTOR_TTOWER(0), buffer) != 1) if (TryReadSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_1, buffer) != 1)
return FALSE; return FALSE;
memcpy(ttdata + 0x000, buffer, SEC30_SIZE); memcpy(ttdata + 0x000, buffer, SEC30_SIZE);
if (TryCopySpecialSaveSection(SECTOR_TTOWER(1), buffer) != 1) if (TryReadSpecialSaveSector(SECTOR_ID_TRAINER_TOWER_2, buffer) != 1)
return FALSE; return FALSE;
memcpy((u8 *)ttdata + SEC30_SIZE, buffer, SEC31_SIZE); memcpy((u8 *)ttdata + SEC30_SIZE, buffer, SEC31_SIZE);
+2 -2
View File
@@ -1431,12 +1431,12 @@ static void sub_8155A78(void)
case 2: case 2:
if (!IsDma3ManagerBusyWithBgCopy()) if (!IsDma3ManagerBusyWithBgCopy())
{ {
CreateTask(Task_LinkSave, 0); CreateTask(Task_LinkFullSave, 0);
gUnknown_203F440->state++; gUnknown_203F440->state++;
} }
break; break;
case 3: case 3:
if (!FuncIsActiveTask(Task_LinkSave)) if (!FuncIsActiveTask(Task_LinkFullSave))
gUnknown_203F440->state++; gUnknown_203F440->state++;
break; break;
default: default:
+2 -2
View File
@@ -429,7 +429,7 @@ static void Task_Hof_InitTeamSaveData(u8 taskId)
} }
else else
{ {
if (Save_LoadGameData(SAVE_HALL_OF_FAME) != TRUE) if (LoadGameSave(SAVE_HALL_OF_FAME) != SAVE_STATUS_OK)
memset(gDecompressionBuffer, 0, 0x2000); memset(gDecompressionBuffer, 0, 0x2000);
} }
@@ -759,7 +759,7 @@ static void Task_HofPC_CopySaveData(u8 taskId)
struct HallofFameTeam* savedTeams; struct HallofFameTeam* savedTeams;
CreateTopBarWindowLoadPalette(0, 30, 0, 0x0C, 0x226); CreateTopBarWindowLoadPalette(0, 30, 0, 0x0C, 0x226);
if (Save_LoadGameData(SAVE_HALL_OF_FAME) != SAVE_STATUS_OK) if (LoadGameSave(SAVE_HALL_OF_FAME) != SAVE_STATUS_OK)
{ {
gTasks[taskId].func = Task_HofPC_PrintDataIsCorrupted; gTasks[taskId].func = Task_HofPC_PrintDataIsCorrupted;
} }
+1 -1
View File
@@ -872,7 +872,7 @@ void c2_copyright_1(void)
{ {
ResetMenuAndMonGlobals(); ResetMenuAndMonGlobals();
Save_ResetSaveCounters(); Save_ResetSaveCounters();
Save_LoadGameData(SAVE_NORMAL); LoadGameSave(SAVE_NORMAL);
if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID) if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID)
Sav2_ClearSetDefault(); Sav2_ClearSetDefault();
SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound); SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound);
+2 -2
View File
@@ -1620,12 +1620,12 @@ static bool32 SavePokeJump(void)
case 2: case 2:
if (AreLinkQueuesEmpty()) if (AreLinkQueuesEmpty())
{ {
CreateTask(Task_LinkSave, 6); CreateTask(Task_LinkFullSave, 6);
sPokemonJump->mainState++; sPokemonJump->mainState++;
} }
break; break;
case 3: case 3:
if (!FuncIsActiveTask(Task_LinkSave)) if (!FuncIsActiveTask(Task_LinkFullSave))
{ {
ClearMessageWindow(); ClearMessageWindow();
sPokemonJump->mainState++; sPokemonJump->mainState++;
+1 -1
View File
@@ -809,7 +809,7 @@ static void QuestLog_StartFinalScene(void)
{ {
ResetSpecialVars(); ResetSpecialVars();
Save_ResetSaveCounters(); Save_ResetSaveCounters();
Save_LoadGameData(SAVE_NORMAL); LoadGameSave(SAVE_NORMAL);
SetMainCallback2(CB2_EnterFieldFromQuestLog); SetMainCallback2(CB2_EnterFieldFromQuestLog);
gFieldCallback2 = FieldCB2_FinalScene; gFieldCallback2 = FieldCB2_FinalScene;
FreeAllWindowBuffers(); FreeAllWindowBuffers();
+1 -1
View File
@@ -18,7 +18,7 @@ void ResetSaveHeap(void)
SetSaveBlocksPointers(); SetSaveBlocksPointers();
ResetMenuAndMonGlobals(); ResetMenuAndMonGlobals();
Save_ResetSaveCounters(); Save_ResetSaveCounters();
Save_LoadGameData(SAVE_NORMAL); LoadGameSave(SAVE_NORMAL);
if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID) if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID)
Sav2_ClearSetDefault(); Sav2_ClearSetDefault();
SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound); SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound);
+354 -343
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -74,12 +74,12 @@ bool32 RunSaveFailedScreen(void)
case 5: case 5:
if (TryWipeDamagedSectors() == TRUE) if (TryWipeDamagedSectors() == TRUE)
{ {
gSaveSucceeded = SAVE_STATUS_OK; gSaveAttemptStatus = SAVE_STATUS_OK;
PrintTextOnSaveFailedScreen(gText_SaveFailedScreen_SaveCompleted); PrintTextOnSaveFailedScreen(gText_SaveFailedScreen_SaveCompleted);
} }
else else
{ {
gSaveSucceeded = SAVE_STATUS_ERROR; gSaveAttemptStatus = SAVE_STATUS_ERROR;
PrintTextOnSaveFailedScreen(gText_SaveFailedScreen_BackupMemoryDamaged); PrintTextOnSaveFailedScreen(gText_SaveFailedScreen_BackupMemoryDamaged);
} }
sSaveFailedScreenState = 6; sSaveFailedScreenState = 6;
+5 -5
View File
@@ -798,7 +798,7 @@ static u8 SaveDialogCB_DoSave(void)
static u8 SaveDialogCB_PrintSaveResult(void) static u8 SaveDialogCB_PrintSaveResult(void)
{ {
if (gSaveSucceeded == TRUE) if (gSaveAttemptStatus == SAVE_STATUS_OK)
PrintSaveTextWithFollowupFunc(gText_PlayerSavedTheGame, SaveDialogCB_WaitPrintSuccessAndPlaySE); PrintSaveTextWithFollowupFunc(gText_PlayerSavedTheGame, SaveDialogCB_WaitPrintSuccessAndPlaySE);
else else
PrintSaveTextWithFollowupFunc(gText_SaveError_PleaseExchangeBackupMemory, SaveDialogCB_WaitPrintErrorAndPlaySE); PrintSaveTextWithFollowupFunc(gText_SaveError_PleaseExchangeBackupMemory, SaveDialogCB_WaitPrintErrorAndPlaySE);
@@ -921,11 +921,11 @@ static void task50_after_link_battle_save(u8 taskId)
break; break;
case 1: case 1:
SetContinueGameWarpStatusToDynamicWarp(); SetContinueGameWarpStatusToDynamicWarp();
sub_80DA45C(); WriteSaveBlock2();
data[0] = 2; data[0] = 2;
break; break;
case 2: case 2:
if (sub_80DA4A0()) if (WriteSaveBlock1Sector())
{ {
ClearContinueGameWarpStatus2(); ClearContinueGameWarpStatus2();
data[0] = 3; data[0] = 3;
@@ -941,11 +941,11 @@ static void task50_after_link_battle_save(u8 taskId)
DestroyTask(taskId); DestroyTask(taskId);
break; break;
case 5: case 5:
CreateTask(Task_LinkSave, 5); CreateTask(Task_LinkFullSave, 5);
data[0] = 6; data[0] = 6;
break; break;
case 6: case 6:
if (!FuncIsActiveTask(Task_LinkSave)) if (!FuncIsActiveTask(Task_LinkFullSave))
data[0] = 3; data[0] = 3;
break; break;
} }
+1 -1
View File
@@ -698,7 +698,7 @@ static void SetTitleScreenScene_Cry(s16 * data)
SetSaveBlocksPointers(); SetSaveBlocksPointers();
ResetMenuAndMonGlobals(); ResetMenuAndMonGlobals();
Save_ResetSaveCounters(); Save_ResetSaveCounters();
Save_LoadGameData(SAVE_NORMAL); LoadGameSave(SAVE_NORMAL);
if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID) if (gSaveFileStatus == SAVE_STATUS_EMPTY || gSaveFileStatus == SAVE_STATUS_INVALID)
Sav2_ClearSetDefault(); Sav2_ClearSetDefault();
SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound); SetPokemonCryStereo(gSaveBlock2Ptr->optionsSound);
+4 -4
View File
@@ -2587,7 +2587,7 @@ static void CB2_HandleTradeEnded(void)
MEvent_RecordIdOfWonderCardSenderByEventType(2, gLinkPlayers[GetMultiplayerId() ^ 1].trainerId); MEvent_RecordIdOfWonderCardSenderByEventType(2, gLinkPlayers[GetMultiplayerId() ^ 1].trainerId);
} }
SetContinueGameWarpStatusToDynamicWarp(); SetContinueGameWarpStatusToDynamicWarp();
SaveGame_AfterLinkTrade(); LinkFullSave_Init();
gMain.state++; gMain.state++;
sTradeData->timer = 0; sTradeData->timer = 0;
break; break;
@@ -2598,7 +2598,7 @@ static void CB2_HandleTradeEnded(void)
} }
break; break;
case 52: case 52:
if (AfterLinkTradeSaveFailed()) if (LinkFullSave_WriteSector())
{ {
ClearContinueGameWarpStatus2(); ClearContinueGameWarpStatus2();
gMain.state = 4; gMain.state = 4;
@@ -2610,7 +2610,7 @@ static void CB2_HandleTradeEnded(void)
} }
break; break;
case 4: case 4:
ClearSaveAfterLinkTradeSaveFailure(); LinkFullSave_ReplaceLastSector();
gMain.state = 40; gMain.state = 40;
sTradeData->timer = 0; sTradeData->timer = 0;
break; break;
@@ -2642,7 +2642,7 @@ static void CB2_HandleTradeEnded(void)
case 42: case 42:
if (IsLinkTaskFinished()) if (IsLinkTaskFinished())
{ {
sub_80DA434(); LinkFullSave_SetLastSectorSignature();
gMain.state = 5; gMain.state = 5;
} }
break; break;