#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;
    }
}
