2023-04-15 12:54:02 +02:00

104 lines
3.5 KiB
C

#include "gba.h"
#include "sha.h"
#include "sdmmc.h"
#define AGBLOGO_SHA256 \
0x08, 0xA0, 0x15, 0x3C, 0xFD, 0x6B, 0x0E, 0xA5, 0x4B, 0x93, 0x8F, 0x7D, 0x20, 0x99, 0x33, 0xFA, \
0x84, 0x9D, 0xA0, 0xD5, 0x6F, 0x5A, 0x34, 0xC4, 0x81, 0x06, 0x0C, 0x9F, 0xF2, 0xFA, 0xD8, 0x18
u32 ValidateAgbSaveHeader(AgbSaveHeader* header) {
static u8 magic[] = { AGBSAVE_MAGIC };
// basic checks
if ((memcmp(header->magic, magic, sizeof(magic)) != 0) ||
(header->unknown0 != 1) || (header->save_start != 0x200) ||
(header->save_size > AGBSAVE_MAX_SSIZE) || !(GBASAVE_VALID(header->save_size)))
return 1;
// reserved area checks
// disabled due to a weird quirk https://github.com/d0k3/GodMode9/issues/412
// for (u32 i = 0; i < sizeof(header->reserved0); i++) if (header->reserved0[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved1); i++) if (header->reserved1[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved2); i++) if (header->reserved2[i] != 0xFF) return 1;
// for (u32 i = 0; i < sizeof(header->reserved3); i++) if (header->reserved3[i] != 0xFF) return 1;
// all fine if arriving here
return 0;
}
// http://problemkaputt.de/gbatek.htm#gbacartridgeheader
u32 ValidateAgbHeader(AgbHeader* agb) {
static const u8 logo_sha[0x20] = { AGBLOGO_SHA256 };
u8 logo[0x9C] __attribute__((aligned(4)));
// check fixed value
if (agb->fixed != 0x96) return 1;
// header checksum
u8* hdr = (u8*) agb;
u8 checksum = 0x00 - 0x19;
for (u32 i = 0xA0; i < 0xBD; i++)
checksum -= hdr[i];
if (agb->checksum != checksum) return 1;
// logo SHA check
memcpy(logo, agb->logo, 0x9C);
logo[0x98] &= ~0x84;
logo[0x9A] &= ~0x03;
if (sha_cmp(logo_sha, logo, 0x9C, SHA256_MODE) != 0)
return 1;
return 0;
}
/* u32 ValidateAgbVc(void* data, u32 len) {
const u8 magic[] = { GBAVC_MAGIC };
if (len < sizeof(AgbHeader) + sizeof(AgbVcFooter))
return 1;
AgbHeader* header = (AgbHeader*) data;
AgbVcFooter* footer = (AgbVcFooter*) (((u8*) data) + len - sizeof(AgbVcFooter));
if ((ValidateAgbHeader(header) != 0) || (memcmp(footer->magic, magic, sizeof(magic)) != 0) ||
(footer->rom_size != len - sizeof(AgbVcFooter)))
return 1;
return 0;
} */
// basically reverse ValidateAgbSaveHeader()
/* u32 BuildAgbSaveHeader(AgbSaveHeader* header, u64 title_id, u32 save_size) {
const u8 magic[] = { AGBSAVE_MAGIC };
memset(header, 0x00, sizeof(AgbSaveHeader));
memset(header->reserved0, 0xFF, sizeof(header->reserved0));
memset(header->reserved1, 0xFF, sizeof(header->reserved1));
memset(header->reserved2, 0xFF, sizeof(header->reserved2));
memset(header->reserved3, 0xFF, sizeof(header->reserved3));
memcpy(header->magic, magic, sizeof(magic));
header->unknown0 = 0x01;
header->title_id = title_id;
header->save_start = 0x200;
header->save_size = save_size;
sdmmc_get_cid(0, (u32*) (void*) &(header->sd_cid));
return 0;
} */
// see: http://problemkaputt.de/gbatek.htm#gbacartridgeheader
const char* AgbDestStr(const char* code) {
switch(code[3]) {
case 'J': return STR_REGION_JAPAN;
case 'E': return STR_REGION_AMERICAS;
case 'P': return STR_REGION_EUROPE;
case 'D': return STR_REGION_GERMANY;
case 'F': return STR_REGION_FRANCE;
case 'I': return STR_REGION_ITALY;
case 'S': return STR_REGION_SPAIN;
default: return STR_REGION_UNKNOWN;
}
}