#pragma once

#include "common.h"
#include "region.h"

// size of the icon struct:
// see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle
// v0x0001 -> 0x0840 byte (contains JPN, USA, FRE, GER, ITA, ESP titles)
// v0x0002 -> 0x0940 byte (adds CHN title)
// v0x0003 -> 0x0A40 byte (adds KOR title)
// v0x0103 -> 0x23C0 byte (adds TWL animated icon data)
#define TWLICON_SIZE_DATA(v) ((v == 0x0001) ? 0x0840 : (v == 0x0002) ? 0x0940 : \
                              (v == 0x0003) ? 0x1240 : (v == 0x0103) ? 0x23C0 : 0x0000)
#define TWLICON_SIZE_DESC   128
#define TWLICON_DIM_ICON    32
#define TWLICON_SIZE_ICON   (TWLICON_DIM_ICON * TWLICON_DIM_ICON * 2) // w * h * bpp (rgb565)
#define NDS_LOGO_CRC16      0xCF56

#define TWL_UNITCODE_NTR    0x00
#define TWL_UNITCODE_TWLNTR 0x02
#define TWL_UNITCODE_TWL    0x03

// see https://github.com/devkitPro/ndstool/blob/313b0be6440d3087b6d433c3ea057605d2c95197/source/header.cpp#L585-L595
#define NDS_ARM9_FOOTER_MAGIC 0xDEC00621

// see: http://problemkaputt.de/gbatek.htm#dscartridgeicontitle
typedef struct {
    u16 version;
    u16 crc_0x0020_0x0840;
    u16 crc_0x0020_0x0940;
    u16 crc_0x0020_0x0A40;
    u16 crc_0x1240_0x23C0;
    u8  reserved[0x16];
    u8  icon[0x200]; // 32x32x4bpp / 4x4 tiles
    u16 palette[0x10]; // palette[0] is transparent
    u16 title_jap[0x80];
    u16 title_eng[0x80];
    u16 title_fre[0x80];
    u16 title_ger[0x80];
    u16 title_ita[0x80];
    u16 title_spa[0x80];
    u16 title_chn[0x80];
    u16 title_kor[0x80];
    u16 title_reserved[0x8 * 0x80];
    u8  icon_anim[0x200 * 0x8]; // 32x32x4bpp / 8 frames
    u16 palette_anim[0x10 * 0x8]; // 8 frames
    u16 sequence_anim[0x40];
} PACKED_STRUCT TwlIconData;

// very limited, information taken from here:
// https://github.com/devkitPro/ndstool/blob/dsi-support/source/header.h
// http://problemkaputt.de/gbatek.htm#dscartridgeheader
// http://problemkaputt.de/gbatek.htm#dsicartridgeheader
typedef struct {
    // common stuff (DS + DSi)
    char game_title[12];
    char game_code[4];
    char maker_code[2];
    u8  unit_code; // (0x00=NDS, 0x02=NDS+DSi, 0x03=DSi)
    u8  seed_select;
    u8  device_capacity; // cartridge size: (128 * 1024) << this
    u8  reserved0[7];
    u8  dsi_flags;
    u8  nds_region;
    u8  rom_version;
    u8  autostart; // bit2: skip "press button" after Health & Safety
    u32 arm9_rom_offset;
    u32 arm9_entry_address;
    u32 arm9_ram_address;
    u32 arm9_size;
    u32 arm7_rom_offset;
    u32 arm7_entry_address;
    u32 arm7_ram_address;
    u32 arm7_size;
    u32 fnt_offset;
    u32 fnt_size;
    u32 fat_offset;
    u32 fat_size;
    u32 arm9_overlay_offset;
    u32 arm9_overlay_size;
    u32 arm7_overlay_offset;
    u32 arm7_overlay_size;
    u32 rom_control_normal; // 0x00416657 for OneTimePROM
    u32 rom_control_key1; // 0x081808F8 for OneTimePROM
    u32 icon_offset;
    u16 secure_area_crc;
    u16 secure_area_delay;
    u32 arm9_auto_load;
    u32 arm7_auto_load;
    u64 secure_area_disable;
    u32 ntr_rom_size; // in byte
    u32 header_size;
    u32 arm9_param_tbl_offset;
    u32 arm7_param_tbl_offset;
    u16 ntr_rom_region_end;
    u16 twl_rom_region_start;
    u8  reserved1[44];
    u8  logo[156];
    u16 logo_crc;
    u16 header_crc;
    u8  debugger_reserved[0x20];
    // extended mode stuff (DSi only)
    u8  ignored0[0x30]; // ignored
    u32 region_flags;
    u32 access_control;
    u32 arm7_scfg_ext7;
    u8  reserved2[3];
    u8  srl_flag;
    u32 arm9i_rom_offset;
    u32 reserved3;
    u32 arm9i_load_adress;
    u32 arm9i_size;
    u32 arm7i_rom_offset;
    u32 unknown1;
    u32 arm7i_load_adress;
    u32 arm7i_size;
    u8  ignored1[0x30]; // ignored
    u32 ntr_twl_rom_size;
    u8  unknown2[12];
    u8  ignored2[0x10]; // ignored
    u64 title_id;
    u32 pubsav_size;
    u32 prvsav_size;
    u8  reserved4[176];
    u8  unknown3[0x10];
    u8  ignored3[0xD00]; // ignored
} PACKED_STRUCT TwlHeader;

u32 ValidateTwlHeader(TwlHeader* twl);
u32 VerifyTwlIconData(TwlIconData* icon, u32 size);
u32 BuildTwlSaveHeader(void* sav, u32 size);
u32 LoadTwlMetaData(const char* path, TwlHeader* hdr, TwlIconData* icon);
u32 GetTwlTitle(char* desc, const TwlIconData* twl_icon);
u32 GetTwlIcon(u16* icon, const TwlIconData* twl_icon);

u32 FindNitroRomDir(u32 dirid, u32* fileid, u8** fnt_entry, TwlHeader* hdr, u8* fnt, u8* fat);
u32 NextNitroRomEntry(u32* fileid, u8** fnt_entry);
u32 ReadNitroRomEntry(u64* offset, u64* size, bool* is_dir, u32 fileid, u8* fnt_entry, u8* fat);
