tmc/src/save.c

408 lines
11 KiB
C

#include "save.h"
#include "gba/eeprom.h"
typedef struct SaveFileStatus {
u16 checksum1;
u16 checksum2;
u32 status;
} SaveFileStatus;
typedef struct {
u16 size;
u16 checksum1;
u16 checksum2;
u16 address1;
u16 address2;
} SaveFileEEPROMAddresses;
static SaveResult HandleSaveInit(u32);
static SaveResult HandleSaveInProgress(u32);
static SaveResult HandleSaveDone(u32);
const SaveFileEEPROMAddresses* GetSaveFileEEPROMAddresses(u32);
u32 DataDoubleReadWithStatus(u32, void* data);
u32 DataDoubleWriteWithStatus(u32, const void* data);
u32 VerifyChecksum(SaveFileStatus* fileStatus, u16* data, u32 size);
u16 CalculateChecksum(u16* data, u32);
u32 WriteSaveFile(u32 index, SaveFile* saveFile);
void SetFileStatusInit(u32 index);
u32 ReadSaveFileStatus(u32 address, SaveFileStatus* fileStatus);
u32 ParseSaveFileStatus(const SaveFileStatus* fileStatus);
u32 WriteSaveFileStatus(u32 address, const SaveFileStatus* fileStatus);
u32 DataRead(u32 address, void* data, u32 size);
u32 DataWrite(u32 address, const void* data, u32 size);
u32 DataCompare(u32 address, const void* data, u32 size);
const u16 gUnk_0811E454[] = { 0x0, 0x0, 0x100, 0x200, 0x300, 0x400, 0x500,
0x5C0, 0x680, 0x740, 0x800, 0x8C0, 0x9C0, 0xA80 };
const char gUnk_0811E470[6] = "LINK";
static SaveResult (*const sSaveHandlers[])(u32) = { HandleSaveInit, HandleSaveInProgress, HandleSaveDone };
static const char sSignatureLong[32] = "AGBZELDA:THE MINISH CAP:ZELDA 5";
// Save file is untouched
static const SaveFileStatus sSaveDescInit = { 0xffff, 0xffff, 'TINI' };
// Save file is deleted
static const SaveFileStatus sSaveDescDeleted = { 0xffff, 0xffff, 'FleD' };
const char gUnk_0811E4B4[8] = "DAMEDAME";
const SaveFileEEPROMAddresses gSaveFileEEPROMAddresses[] = { { 0x500, 0x30, 0x1030, 0x80, 0x1080 },
{ 0x500, 0x40, 0x1040, 0x580, 0x1580 },
{ 0x500, 0x50, 0x1050, 0xa80, 0x1a80 },
{ 0x10, 0x20, 0x1020, 0x70, 0x1070 },
{ 0x20, 0, 0, 0, 0x1000 },
{ 0x20, 0x60, 0x1060, 0xf80, 0x1f80 },
{ 0x8, 0xfa0, 0x1fa0, 0xfa0, 0x1fa0 } };
extern s16 gUnk_02021EE0[6];
void sub_0807CD9C() {
sub_080530C8();
}
SaveResult HandleSave(u32 arg0) {
return sSaveHandlers[gMenu.storyPanelIndex](arg0);
}
SaveResult HandleSaveInit(u32 arg0) {
gUnk_02021EE0[5] -= 8;
if (gUnk_02021EE0[4] <= 0) {
gMenu.field_0xa = 8;
gMenu.storyPanelIndex = SAVE_IN_PROGRESS;
}
return SAVE_BUSY;
}
SaveResult HandleSaveInProgress(u32 arg0) {
u32 temp;
if (gMenu.field_0xa == 0) {
InitDMA();
switch (arg0) {
case 0:
temp = WriteSaveFile(gUnk_02000000->saveFileId, &gSave);
break;
case 1:
SetFileStatusDeleted(gUnk_02000000->saveFileId);
temp = 1;
break;
case 2:
temp = Write_02000000(gUnk_02000000);
break;
}
gMenu.field_0xa = temp;
gMenu.storyPanelIndex = SAVE_DONE;
sub_08056208();
} else {
gMenu.field_0xa--;
}
return SAVE_BUSY;
}
SaveResult HandleSaveDone(u32 arg0) {
SaveResult result;
result = SAVE_BUSY;
gUnk_02021EE0[5] += 8;
if (gUnk_02021EE0[3] <= gUnk_02021EE0[5]) {
gUnk_02021EE0[5] = gUnk_02021EE0[3];
sub_08050384();
gMenu.storyPanelIndex = SAVE_INIT;
if (gMenu.field_0xa == 1) {
result = SAVE_OK;
} else {
result = SAVE_ERROR;
}
}
return result;
}
u32 InitSaveData(void) {
const SaveFileEEPROMAddresses* eepromAddresses;
u32 error;
EEPROMConfigure(0x40);
eepromAddresses = GetSaveFileEEPROMAddresses(4);
error = 0;
if (DataCompare(eepromAddresses->address1, sSignatureLong, eepromAddresses->size) == 0) {
error += 1;
}
if (DataCompare(eepromAddresses->address2, sSignatureLong, eepromAddresses->size) == 0) {
error += 2;
}
if (error != 0) {
if (error == 3) {
SetFileStatusInit(5);
SetFileStatusInit(3);
SetFileStatusInit(2);
SetFileStatusInit(1);
SetFileStatusInit(0);
}
DataWrite(eepromAddresses->address2, sSignatureLong, eepromAddresses->size);
DataWrite(eepromAddresses->address1, sSignatureLong, eepromAddresses->size);
}
return 1;
}
u32 WriteSaveFile(u32 index, SaveFile* saveFile) {
return DataDoubleWriteWithStatus(index, saveFile);
}
u32 Write_02000000(struct_02000000* arg0) {
return DataDoubleWriteWithStatus(3, arg0);
}
u32 sub_0807CF1C(u8* arg0) {
return DataDoubleWriteWithStatus(5, arg0);
}
s32 ReadSaveFile(u32 index, SaveFile* saveFile) {
return DataDoubleReadWithStatus(index, saveFile);
}
u32 Read_02000000(struct_02000000* arg0) {
return DataDoubleReadWithStatus(3, arg0);
}
u32 sub_0807CF3C(u8* arg0) {
return DataDoubleReadWithStatus(5, arg0);
}
void SetFileStatusDeleted(u32 index) {
const SaveFileEEPROMAddresses* eepromAddresses;
eepromAddresses = GetSaveFileEEPROMAddresses(index);
WriteSaveFileStatus(eepromAddresses->checksum2, &sSaveDescDeleted);
WriteSaveFileStatus(eepromAddresses->checksum1, &sSaveDescDeleted);
}
void SetFileStatusInit(u32 index) {
const SaveFileEEPROMAddresses* eepromAddresses;
const SaveFileStatus* fileStatus;
eepromAddresses = GetSaveFileEEPROMAddresses(index);
fileStatus = &sSaveDescInit;
WriteSaveFileStatus(eepromAddresses->checksum2, fileStatus);
WriteSaveFileStatus(eepromAddresses->checksum1, fileStatus);
}
u32 DataDoubleWriteWithStatus(u32 arg0, const void* data) {
SaveFileStatus fileStatus;
u32 ret;
const SaveFileEEPROMAddresses* eepromAddresses;
bool32 write1success, write2success;
u16 checksum;
eepromAddresses = GetSaveFileEEPROMAddresses(arg0);
fileStatus.status = 'MCZ3';
checksum = CalculateChecksum((u16*)&fileStatus.status, 4);
checksum += CalculateChecksum((u16*)data, eepromAddresses->size);
fileStatus.checksum1 = checksum;
fileStatus.checksum2 = -(u32)checksum;
write1success = DataWrite(eepromAddresses->address1, data, eepromAddresses->size);
if (write1success) {
write1success = WriteSaveFileStatus(eepromAddresses->checksum1, &fileStatus);
}
write2success = DataWrite(eepromAddresses->address2, data, eepromAddresses->size);
if (write2success) {
write2success = WriteSaveFileStatus(eepromAddresses->checksum2, &fileStatus);
}
ret = 0;
if (write1success || write2success) {
ret = 1;
}
return ret;
}
u32 DataDoubleReadWithStatus(u32 param_1, void* data) {
vu32 set_0;
SaveFileStatus fileStatus;
const SaveFileEEPROMAddresses* eepromAddresses;
u32 read1status;
u32 read2status;
u32 ret;
u32 temp;
eepromAddresses = GetSaveFileEEPROMAddresses(param_1);
read1status = ReadSaveFileStatus(eepromAddresses->checksum1, &fileStatus);
if (read1status == 2) {
if ((DataRead(eepromAddresses->address1, data, eepromAddresses->size) == 0) ||
(VerifyChecksum(&fileStatus, (u16*)data, eepromAddresses->size) == 0)) {
// read 1 failed
read1status = 0;
} else {
return 1;
}
}
read2status = ReadSaveFileStatus(eepromAddresses->checksum2, &fileStatus);
if (read2status == 2) {
if ((DataRead(eepromAddresses->address2, data, eepromAddresses->size) != 0) &&
(VerifyChecksum(&fileStatus, (u16*)data, (u32)eepromAddresses->size) != 0)) {
return 1;
}
// read 2 failed
read2status = 0;
}
set_0 = 0;
CpuSet((u16*)&set_0, data, eepromAddresses->size >> 2 | CPU_SET_SRC_FIXED | CPU_SET_32BIT);
temp = read1status | read2status;
ret = 0;
if (temp == 0) {
ret = -1; // both reads failed
}
return ret;
}
u32 VerifyChecksum(SaveFileStatus* fileStatus, u16* data, u32 size) {
u32 temp;
u16 checksum;
checksum = CalculateChecksum((u16*)&fileStatus->status, 4);
checksum += CalculateChecksum(data, size);
if ((fileStatus->checksum1 != checksum) ||
(temp = fileStatus->checksum1 << 0x10, fileStatus->checksum2 != (-temp >> 0x10)) ||
(fileStatus->status != 'MCZ3')) {
return 0;
} else
return 1;
}
u32 ReadSaveFileStatus(u32 address, SaveFileStatus* fileStatus) {
u32 ret;
if (!DataRead(address, fileStatus, 8)) {
ret = 0;
} else {
ret = ParseSaveFileStatus(fileStatus);
}
if (!ret && DataRead(address + 8, fileStatus, 8)) {
ret = ParseSaveFileStatus(fileStatus);
}
return ret;
}
/**
* 2 for valid existing file
* 1 for valid empty file
* 0 for bad file
* @param fileStatus
* @return
*/
u32 ParseSaveFileStatus(const SaveFileStatus* fileStatus) {
u32 ret;
switch (fileStatus->status) {
case 'MCZ3':
if (fileStatus->checksum1 + fileStatus->checksum2 == 0x10000) {
ret = 2;
} else {
ret = 0;
}
break;
case 'FleD':
case 'TINI':
ret = 0;
if ((fileStatus->checksum1 & fileStatus->checksum2) == 0xffff) {
ret = 1;
}
break;
default:
ret = 0;
break;
}
return ret;
}
bool32 WriteSaveFileStatus(u32 address, const struct SaveFileStatus* fileStatus) {
bool32 success;
success = DataWrite(address, fileStatus, 8);
if (!success) {
success = DataWrite(address + 8, fileStatus, 8);
}
return success;
}
u16 CalculateChecksum(u16* data, u32 size) {
u32 checksum;
checksum = 0;
while (size != 0) {
checksum += (*data ^ size);
data++;
size = size - 2;
}
return checksum;
}
const SaveFileEEPROMAddresses* GetSaveFileEEPROMAddresses(u32 unk_1) {
return &gSaveFileEEPROMAddresses[unk_1];
}
/**
* read arbitrary data from EEPROM
* @param address EEPROM address
* @param data buffer to read into
* @param size size in bytes to read
* @return TRUE on success
*/
bool32 DataRead(u32 address, void* data, u32 size) {
size /= 8;
address /= 8;
while (size-- > 0) {
if (EEPROMRead(address, data))
return FALSE;
address++;
data += 8;
}
return TRUE;
}
/**
* write arbitrary data to EEPROM
* @param address EEPROM address
* @param data buffer to write from
* @param size size in bytes to write
* @return TRUE on success
*/
bool32 DataWrite(u32 address, const void* data, u32 size) {
size /= 8;
address /= 8;
while (size-- > 0) {
if (EEPROMWrite0_8k_Check(address, data)) {
EEPROMWrite0_8k_Check(address, (const u16*)gUnk_0811E4B4);
return FALSE;
}
address++;
data += 8;
}
return TRUE;
}
/**
* compare arbitrary data with EEPROM
* @param address EEPROM address
* @param data buffer to compare to
* @param size size in bytes to compare
* @return TRUE on success
*/
bool32 DataCompare(u32 address, const void* data, u32 size) {
size /= 8;
address /= 8;
while (size-- > 0) {
if (EEPROMCompare(address, data))
return FALSE;
address++;
data += 8;
}
return TRUE;
}