forked from Mirror/GodMode9
Add handling for SD GBA VC .sav CMAC.
When identifying a selected `00000001.sav`, cmac type testing is first attempted for 3DS saves with the **DISA** format. If a 3DS save data is not detected, the search continues at expected offsets for gba vc bottom slots in increasingly larger save sizes. For the GBA VC saves, this only calculates and corrects whichever of the two slots is newer or has the higher counter. There are five additional **CMAC_AGBSAVE_** types to differentiate the different gba save sizes. If a bottom slot was found but determined to be older based on comparing the counter values, the search defaults to **CMAC_AGBSAVE_SD**. For the save counters, there are two scenarios where the value `00` is considered bigger than `FF` depending which slot has what value. If clarification is required, I will post a diagram at [issue #494](https://github.com/d0k3/GodMode9/issues/494) explaining what is meant by `00` bigger than `FF`. Please make corrections and improvements to the coding as you see fit in your review. I am not well versed or efficient in the C/C++ programming language.
This commit is contained in:
parent
499e301b7b
commit
a145e8d4a9
@ -67,15 +67,48 @@ u32 SetupSlot0x30(char drv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*u32 CheckAgbSaveHeader(const char* path) {
|
||||
u32 FindAgbSaveSlotOffset(const char* path, u32 cmac_type) {
|
||||
u32 counter[2]; // counter[0] = magic_check , counter[0] = upper_counter , counter[1] = lower_counter
|
||||
u32 slot_offset[6] = {0, 0x400, 0x2200, 0x8200, 0x10200, 0x20200};
|
||||
u32 i;
|
||||
|
||||
if (cmac_type == CMAC_AGBSAVE) return slot_offset[0]; // Does not apply for 'agbsave.bin'.
|
||||
|
||||
for (i = 1; i <= 5; i++) { // Look for the `.SAV` magic header at the expected bottom slots.
|
||||
if (fvx_qread(path, &counter[0], slot_offset[i], 0x4, NULL) != FR_OK) return 0;
|
||||
if (counter[0] == 0x5641532E) break; // Magic header '.SAV' found.
|
||||
}
|
||||
|
||||
if (i == 6) return 0; // Bottom slot not found.
|
||||
|
||||
// Compare top and bottom slots' counter values to determine which is newer.
|
||||
if (fvx_qread(path, &counter[0], 0x34, 0x4, NULL) != FR_OK) return 0;
|
||||
if (fvx_qread(path, &counter[1], slot_offset[i]+0x034, 0x4, NULL) != FR_OK) return 0;
|
||||
|
||||
if (counter[0] == 0xFFFFFFFF); // Scenario #1: First save is initialized. Bottom slot is newer.
|
||||
else if (counter[0] > counter[1]) i = 0; // Scenario #2: Top slot is newer.
|
||||
// else; // Scenario #3: Bottom slot is newer -or- both are the same.
|
||||
|
||||
return slot_offset[i];
|
||||
}
|
||||
|
||||
u32 CheckAgbSaveHeader(const char* path) {
|
||||
AgbSaveHeader agbsave;
|
||||
u32 magic_check[1];
|
||||
u32 slot_offset[6] = {0, 0x400, 0x2200, 0x8200, 0x10200, 0x20200};
|
||||
u32 i;
|
||||
UINT br;
|
||||
|
||||
if ((fvx_qread(path, &agbsave, 0, 0x200, &br) != FR_OK) || (br != 0x200))
|
||||
return 1;
|
||||
|
||||
|
||||
for (i = 0; i <= 5; i++) { // Look for the '.SAV' magic header at top and bottom slots.
|
||||
if (fvx_qread(path, magic_check, slot_offset[i], 0x4, NULL) != FR_OK) return 1;
|
||||
if (*magic_check == 0x5641532E) break; // Magic header '.SAV' found.
|
||||
}
|
||||
|
||||
if (i == 6) return 1; // No slot found.
|
||||
if ((fvx_qread(path, &agbsave, slot_offset[i], 0x200, &br) != FR_OK) || (br != 0x200)) return 1;
|
||||
|
||||
return ValidateAgbSaveHeader(&agbsave);
|
||||
}*/
|
||||
}
|
||||
|
||||
u32 CheckCmacHeader(const char* path) {
|
||||
u8 cmac_hdr[0x100];
|
||||
@ -99,7 +132,7 @@ u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write) {
|
||||
|
||||
if (!cmac_type) return 1;
|
||||
else if (cmac_type == CMAC_MOVABLE) offset = 0x130;
|
||||
else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010;
|
||||
else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = FindAgbSaveSlotOffset(path, cmac_type) + 0x010;
|
||||
else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here
|
||||
else offset = 0x000;
|
||||
|
||||
@ -130,8 +163,8 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
cmac_type = CMAC_EXTDATA_SD;
|
||||
} else if ((sscanf(path, "%c:/title/%08lx/%08lx/data/%08lx.sav", &drv, &tid_high, &tid_low, &sid) == 4) &&
|
||||
ext && (strncasecmp(ext, "sav", 4) == 0)) {
|
||||
// cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : (CheckAgbSaveHeader(path) == 0) ? CMAC_AGBSAVE_SD : 0;
|
||||
cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : 0;
|
||||
if (CheckCmacHeader(path) == 0) cmac_type = CMAC_SAVEDATA_SD; // Check for 3DS save data first.
|
||||
else if (CheckAgbSaveHeader(path) == 0) cmac_type = CMAC_AGBSAVE_SD;
|
||||
} else if ((sscanf(path, "%c:/title/%08lx/%08lx/content/cmd/%08lx.cmd", &drv, &tid_high, &tid_low, &sid) == 4) &&
|
||||
ext && (strncasecmp(ext, "cmd", 4) == 0)) {
|
||||
cmac_type = CMAC_CMD_SD; // this needs special handling, it's in here just for detection
|
||||
@ -187,7 +220,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
UINT br;
|
||||
|
||||
if (!agbsave) return 1;
|
||||
if ((fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) ||
|
||||
if ((fvx_qread(path, agbsave, FindAgbSaveSlotOffset(path, cmac_type), AGBSAVE_MAX_SIZE, &br) != FR_OK) || (br < 0x200) ||
|
||||
(ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br)) {
|
||||
free(agbsave);
|
||||
return 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user