mirror of https://github.com/pmret/papermario.git
328 lines
9.3 KiB
C
328 lines
9.3 KiB
C
#include "fio.h"
|
|
#include "PR/os_flash.h"
|
|
|
|
extern SaveData D_8009A6B0;
|
|
extern s32 logicalSaveInfo[4][2];
|
|
extern s32 physicalSaveInfo[6][2];
|
|
extern s32 nextAvailableSavePage;
|
|
|
|
char magicSaveString[] = "Mario Story 006";
|
|
|
|
void fio_deserialize_state(void);
|
|
void fio_serialize_state(void);
|
|
s32 fio_read_flash(s32 pageNum, void* readBuffer, u32 numBytes);
|
|
s32 fio_write_flash(s32 pageNum, s8* readBuffer, u32 numBytes);
|
|
void fio_erase_flash(s32 pageNum);
|
|
|
|
s32 get_spirits_rescued(void) {
|
|
s32 storyProgress = evt_get_variable(NULL, GB_StoryProgress);
|
|
s32 ret = 7;
|
|
|
|
if (storyProgress < evt_get_variable(NULL, STORY_CH1_STAR_SPIRIT_RESCUED)) {
|
|
ret = 0;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH2_STAR_SPIRIT_RESCUED)) {
|
|
ret = 1;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH3_STAR_SPIRIT_RESCUED)) {
|
|
ret = 2;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH4_STAR_SPIRIT_RESCUED)) {
|
|
ret = 3;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH5_OPENED_ESCAPE_ROUTE)) {
|
|
ret = 4;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH6_STAR_SPIRIT_RESCUED)) {
|
|
ret = 5;
|
|
} else if (storyProgress < evt_get_variable(NULL, STORY_CH7_STAR_SPIRIT_RESCUED)) {
|
|
ret = 6;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
s32 fio_calc_header_checksum(void) {
|
|
u32 sum = 0;
|
|
s32* it = (s32*)&D_800D95E8;
|
|
u32 i;
|
|
|
|
for (i = 0; i < sizeof(D_800D95E8) / sizeof(*it); i++, it++) {
|
|
sum += *it;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
s32 fio_validate_header_checksums(void) {
|
|
SaveDataHeader* header = &D_800D95E8;
|
|
|
|
if (strcmp(header->magicString, magicSaveString)) {
|
|
return FALSE;
|
|
}
|
|
if (header->crc1 != ~header->crc2) {
|
|
return FALSE;
|
|
}
|
|
return fio_calc_header_checksum() == header->crc1;
|
|
}
|
|
|
|
s32 fio_has_valid_backup(void) {
|
|
fio_read_flash(6, &D_800D95E8, sizeof(D_800D95E8));
|
|
|
|
if (!fio_validate_header_checksums()) {
|
|
fio_read_flash(7, &D_800D95E8, sizeof(D_800D95E8));
|
|
|
|
if (!fio_validate_header_checksums()) {
|
|
bzero(&D_800D95E8, sizeof(D_800D95E8));
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
s32 fio_flush_backups(void) {
|
|
s32 checksum;
|
|
|
|
strcpy(D_800D95E8.magicString, magicSaveString);
|
|
D_800D95E8.crc1 = 0;
|
|
D_800D95E8.crc2 = -1;
|
|
checksum = fio_calc_header_checksum();
|
|
D_800D95E8.crc1 = checksum;
|
|
D_800D95E8.crc2 = ~checksum;
|
|
fio_erase_flash(6);
|
|
fio_write_flash(6, (s8*)&D_800D95E8, sizeof(D_800D95E8));
|
|
fio_erase_flash(7);
|
|
fio_write_flash(7, (s8*)&D_800D95E8, sizeof(D_800D95E8));
|
|
return 1;
|
|
}
|
|
|
|
s32 fio_calc_file_checksum(SaveData* saveData) {
|
|
u32 sum = 0;
|
|
s32* it = (s32*)saveData;
|
|
u32 i;
|
|
|
|
for (i = 0; i < sizeof(*saveData) / sizeof(*it); i++, it++) {
|
|
sum += *it;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
s32 fio_validate_file_checksum(SaveData* saveData) {
|
|
if (!strcmp(saveData->magicString, magicSaveString) && saveData->crc1 == ~saveData->crc2) {
|
|
return fio_calc_file_checksum(saveData) == saveData->crc1;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
s32 fio_fetch_saved_file_info(void) {
|
|
SaveData* buffer = &D_8009A6B0; // temps required to match
|
|
SaveData* buffer2 = buffer;
|
|
s32 i, j, savePage;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(logicalSaveInfo); i++) {
|
|
logicalSaveInfo[i][0] = -1;
|
|
logicalSaveInfo[i][1] = -1;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_COUNT(physicalSaveInfo); i++) {
|
|
fio_read_flash(i, buffer, sizeof(SaveData));
|
|
if (fio_validate_file_checksum(buffer)) {
|
|
physicalSaveInfo[i][0] = buffer2->saveSlot;
|
|
physicalSaveInfo[i][1] = buffer2->saveCount;
|
|
if (logicalSaveInfo[buffer2->saveSlot][1] < buffer2->saveCount) {
|
|
logicalSaveInfo[buffer2->saveSlot][0] = i;
|
|
logicalSaveInfo[buffer2->saveSlot][1] = buffer2->saveCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
savePage = 0x7FFFFFFF;
|
|
for (j = 0; j < ARRAY_COUNT(physicalSaveInfo); j++) {
|
|
for (i = 0; i < ARRAY_COUNT(logicalSaveInfo); i++) {
|
|
if (j == logicalSaveInfo[i][0]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ARRAY_COUNT(logicalSaveInfo)) {
|
|
if (physicalSaveInfo[j][1] < savePage) {
|
|
savePage = physicalSaveInfo[j][1];
|
|
nextAvailableSavePage = j;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
s32 fio_load_game(s32 saveSlot) {
|
|
gGameStatusPtr->saveSlot = saveSlot;
|
|
|
|
fio_fetch_saved_file_info();
|
|
fio_read_flash(logicalSaveInfo[saveSlot][0], &gCurrentSaveFile, sizeof(SaveData));
|
|
|
|
if (strcmp(gCurrentSaveFile.magicString, magicSaveString) == 0) {
|
|
if (gGameStatusPtr->saveCount < gCurrentSaveFile.saveCount) {
|
|
gGameStatusPtr->saveCount = gCurrentSaveFile.saveCount;
|
|
}
|
|
fio_deserialize_state();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void fio_save_game(s32 saveSlot) {
|
|
fio_fetch_saved_file_info();
|
|
|
|
gGameStatusPtr->saveSlot = saveSlot;
|
|
|
|
fio_serialize_state();
|
|
|
|
strcpy(gCurrentSaveFile.magicString, magicSaveString);
|
|
|
|
gCurrentSaveFile.saveSlot = saveSlot;
|
|
gGameStatusPtr->saveCount++;
|
|
gCurrentSaveFile.saveCount = gGameStatusPtr->saveCount;
|
|
|
|
gCurrentSaveFile.crc1 = 0;
|
|
gCurrentSaveFile.crc2 = -1;
|
|
gCurrentSaveFile.crc1 = fio_calc_file_checksum(&gCurrentSaveFile);
|
|
gCurrentSaveFile.crc2 = ~gCurrentSaveFile.crc1;
|
|
|
|
fio_erase_flash(nextAvailableSavePage);
|
|
fio_write_flash(nextAvailableSavePage, (s8*)&gCurrentSaveFile, sizeof(SaveData));
|
|
}
|
|
|
|
void fio_erase_game(s32 saveSlot) {
|
|
s32 i;
|
|
|
|
fio_fetch_saved_file_info();
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
if (physicalSaveInfo[i][0] == saveSlot) {
|
|
fio_erase_flash(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fio_deserialize_state(void) {
|
|
SaveData* saveData = &gCurrentSaveFile;
|
|
s32 i, j;
|
|
|
|
gPlayerData = saveData->player;
|
|
|
|
gGameStatusPtr->areaID = saveData->areaID;
|
|
gGameStatusPtr->mapID = saveData->mapID;
|
|
gGameStatusPtr->entryID = saveData->entryID;
|
|
gGameStatusPtr->savedPos.x = saveData->savePos.x;
|
|
gGameStatusPtr->savedPos.y = saveData->savePos.y;
|
|
gGameStatusPtr->savedPos.z = saveData->savePos.z;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(gCurrentEncounter.defeatFlags[0]); i++) {
|
|
for (j = 0; j < ARRAY_COUNT(gCurrentEncounter.defeatFlags); j++) {
|
|
gCurrentEncounter.defeatFlags[j][i] = saveData->enemyDefeatFlags[j][i];
|
|
}
|
|
}
|
|
|
|
|
|
gGameStatusPtr->debugEnemyContact = DEBUG_CONTACT_NONE;
|
|
gGameStatusPtr->unk_76 = 0;
|
|
gGameStatusPtr->unk_77 = 0;
|
|
gGameStatusPtr->musicEnabled = TRUE;
|
|
|
|
gSaveSlotMetadata[gGameStatusPtr->saveSlot] = saveData->unk_12EC;
|
|
}
|
|
|
|
void func_8002B608(void) {
|
|
gGameStatusPtr->entryID = 10;
|
|
fio_serialize_state();
|
|
}
|
|
|
|
void fio_serialize_state(void) {
|
|
SaveData* saveData = &gCurrentSaveFile;
|
|
s32 i, j;
|
|
|
|
saveData->player = gPlayerData;
|
|
|
|
saveData->areaID = gGameStatusPtr->areaID;
|
|
saveData->mapID = gGameStatusPtr->mapID;
|
|
saveData->entryID = gGameStatusPtr->entryID;
|
|
saveData->savePos.x = gGameStatusPtr->savedPos.x;
|
|
saveData->savePos.y = gGameStatusPtr->savedPos.y;
|
|
saveData->savePos.z = gGameStatusPtr->savedPos.z;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(gCurrentEncounter.defeatFlags[0]); i++) {
|
|
for (j = 0; j < ARRAY_COUNT(gCurrentEncounter.defeatFlags); j++) {
|
|
saveData->enemyDefeatFlags[j][i] = gCurrentEncounter.defeatFlags[j][i];
|
|
}
|
|
}
|
|
|
|
saveData->debugEnemyContact = gGameStatusPtr->debugEnemyContact;
|
|
saveData->unk_12E1 = gGameStatusPtr->unk_76;
|
|
saveData->unk_12E2 = gGameStatusPtr->unk_77;
|
|
saveData->musicEnabled = gGameStatusPtr->musicEnabled;
|
|
|
|
gSaveSlotMetadata[gGameStatusPtr->saveSlot].level = gPlayerData.level;
|
|
gSaveSlotMetadata[gGameStatusPtr->saveSlot].spiritsRescued = get_spirits_rescued();
|
|
gSaveSlotMetadata[gGameStatusPtr->saveSlot].timePlayed = gPlayerData.frameCounter;
|
|
|
|
saveData->unk_12EC = gSaveSlotMetadata[gGameStatusPtr->saveSlot];
|
|
}
|
|
|
|
void fio_init_flash(void) {
|
|
osFlashInit();
|
|
}
|
|
|
|
s32 fio_read_flash(s32 pageNum, void* readBuffer, u32 numBytes) {
|
|
OSIoMesg mb;
|
|
OSMesgQueue mesgQueue;
|
|
OSMesg mesg;
|
|
s8* buf = (s8*)readBuffer;
|
|
s32 amt;
|
|
u16 i;
|
|
|
|
osInvalDCache(buf, numBytes);
|
|
osCreateMesgQueue(&mesgQueue, &mesg, 1);
|
|
|
|
i = 0;
|
|
while (numBytes != 0) {
|
|
if (numBytes > sizeof(SaveDataHeader)) {
|
|
amt = sizeof(SaveDataHeader);
|
|
} else {
|
|
amt = numBytes;
|
|
}
|
|
|
|
osFlashReadArray(&mb, 0, pageNum * sizeof(SaveDataHeader) + i, buf, 1, &mesgQueue);
|
|
osRecvMesg(&mesgQueue, NULL, 1);
|
|
i++;
|
|
numBytes -= amt;
|
|
buf += amt;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
s32 fio_write_flash(s32 pageNum, s8* readBuffer, u32 numBytes) {
|
|
OSIoMesg mb;
|
|
OSMesgQueue mesgQueue;
|
|
OSMesg mesg;
|
|
s32 amt;
|
|
u16 i;
|
|
|
|
osWritebackDCache(readBuffer, numBytes);
|
|
osCreateMesgQueue(&mesgQueue, &mesg, 1);
|
|
|
|
i = 0;
|
|
while (numBytes != 0) {
|
|
if (numBytes > sizeof(SaveDataHeader)) {
|
|
amt = sizeof(SaveDataHeader);
|
|
} else {
|
|
amt = numBytes;
|
|
}
|
|
|
|
osFlashWriteBuffer(&mb, 0, readBuffer, &mesgQueue);
|
|
osFlashWriteArray((pageNum * sizeof(SaveDataHeader)) + i);
|
|
osRecvMesg(&mesgQueue, NULL, 1);
|
|
i++;
|
|
numBytes -= amt;
|
|
readBuffer += amt;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void fio_erase_flash(s32 pageNum) {
|
|
osFlashSectorErase(pageNum * sizeof(SaveDataHeader));
|
|
}
|