#pragma author WerWolv
#pragma description Gameboy ROM

#include <type/size.pat>
#include <std/io.pat>
#include <std/string.pat>

#pragma MIME application/x-gameboy-rom

namespace format {
    
    fn rom_type(u8 romType) {
        u32 romSize = 32768 * (1 << romType);
        u16 romBanks = 2 * (1 << romType);
        if(romType == 0x52) {
            romSize = 1153434;
            romBanks = 72;
        } 
        if(romType == 0x53) {
            romSize = 1258291;
            romBanks = 80;
        } 
        return std::format("size: {}, banks: {}", type::impl::size_formatter(romSize), romBanks);
    };
    
    fn ram_type(u8 ramType) {
        u16 ramSize;
        u16 ramBanks;
        if(ramType == 2) {
            ramSize = 8192;
            ramBanks = 1;
        }
        if(ramType == 3) {
            ramSize = 32768;
            ramBanks = 4;
        }
        if(ramType == 4) {
            ramSize = 131072;
            ramBanks = 16;
        }
        if(ramType == 5) {
            ramSize = 65536;
            ramBanks = 8;
        }
        return std::format("size: {}, banks: {}", type::impl::size_formatter(ramSize), ramBanks);
    };
    
    fn rom_features(u8 type) {
        str result = "( ";
        match(type) {
            (0x00): result += "no_mbc ";
            (0x01): result += "mbc1 ";
            (0x02): result += "mbc1 | ram "; 
            (0x03): result += "mbc1 | ram | battery ";
            (0x05): result += "mbc2 "; 
            (0x06): result += "mbc2 | battery ";
            (0x08): result += "no_mbc | ram "; 
            (0x09): result += "no_mbc | ram | battery ";
            (0x0B): result += "mmm01 "; 
            (0x0C): result += "mmm01 | ram ";
            (0x0D): result += "mmm01 | ram | battery "; 
            (0x0F): result += "mbc3 | timer | battery ";
            (0x10): result += "mbc3 | timer | ram | battery "; 
            (0x11): result += "mbc3 ";
            (0x12): result += "mbc3 | ram "; 
            (0x13): result += "mbc3 | ram | battery ";
            (0x19): result += "mbc5 "; 
            (0x1A): result += "mbc5 | ram ";
            (0x1B): result += "mbc5 | ram | battery "; 
            (0x1C): result += "mbc5 | rumble ";
            (0x1D): result += "mbc5 | rumble | ram "; 
            (0x1E): result += "mbc5 | rumble | ram | battery ";
            (0x20): result += "mbc6 "; 
            (0x22): result += "mbc7 | sensor | rumble ";
            (0xFC): result += "camera "; 
            (0xFD): result += "tama5 ";
            (0xFE): result += "huc3 "; 
            (0xFF): result += "huc1 | ram | battery ";
        }
        return result + ")"; 
    };
    
    fn licensee_code(u8 code) {
        match(code) {
            (0x00): return "None";
            (0x01 | 0x31): return "Nintendo";
            (0x08 | 0x38): return "Capcom";
            (0x09): return "Hot-B";
            (0x0A | 0xE0): return "Jaleco";
            (0x0B): return "Coconuts Japan";
            (0x0C | 0x6E): return "Elite Systems";
            (0x13 | 0x69): return "EA (Electronic Arts)";
            (0x18): return "Hudsonsoft";
            (0x19): return "ITC Entertainment";
            (0x1A): return "Yanoman";
            (0x1D): return "Japan Clary";
            (0x1F | 0x4A | 0x61): return "Virgin Interactive";
            (0x24): return "PCM Complete";
            (0x25): return "San-X";
            (0x28): return "Kotobuki Systems";
            (0x29): return "Seta";
            (0x30 | 0x70): return "Infogrames";
            (0x32 | 0xA2 | 0xB2 | 0xC4): return "Bandai";
            (0x33): return "See new licensee";
            (0x34 | 0xA4): return "Konami";
            (0x35): return "HectorSoft";
            (0x39 | 0x9D): return "Banpresto";
            (0x3C): return ".Entertainment i";
            (0x3E): return "Gremlin";
            (0x41): return "Ubisoft";
            (0x42 | 0xEB): return "Atlus";
            (0x44 | 0x4D): return "Malibu";
            (0x46 | 0xCF): return "Angel";
            (0x47): return "Spectrum Holoby";
            (0x49): return "Irem";
            (0x50): return "Absolute";
            (0x51 | 0xB0): return "Acclaim";
            (0x52): return "Activision";
            (0x53): return "American Sammy";
            (0x54): return "GameTek";
            (0x55): return "Park Place";
            (0x56 | 0xDB | 0xFF): return "LJN";
            (0x57): return "Matchbox";
            (0x59): return "Milton Bradley";
            (0x5A): return "Mindscape";
            (0x5B): return "Romstar";
            (0x5C | 0xD6): return "Naxat Soft";
            (0x5D): return "Tradewest";
            (0x60): return "Titus";
            (0x67): return "Ocean Interactive";
            (0x6F): return "Electro Brain";
            (0x71): return "Interplay";
            (0x72 | 0xAA): return "Broderbund";
            (0x73): return "Sculptered Soft";
            (0x75): return "The Sales Curve";
            (0x78): return "t.hq";
            (0x79): return "Accolade";
            (0x7A): return "Triffix Entertainment";
            (0x7C): return "Microprose";
            (0x7F | 0xC2): return "Kemco";
            (0x80): return "Misawa Entertainment";
            (0x83): return "Lozc";
            (0x86 | 0xC4): return "Tokuma Shoten Intermedia";
            (0x8B): return "Bullet-Proof Software";
            (0x8C): return "Vic Tokai";
            (0x8E): return "Ape";
            (0x8F): return "I'Max";
            (0x91): return "Chunksoft Co.";
            (0x92): return "Video System";
            (0x93): return "Tsubaraya Productions Co.";
            (0x95): return "Varie Corporation";
            (0x96): return "Yonezawa/S’Pal";
            (0x97): return "Kaneko";
            (0x99): return "Arc";
            (0x9A): return "Nihon Bussan";
            (0x9B): return "Tecmo";
            (0x9C): return "Imagineer";
            (0x9F): return "Nova";
            (0xA1): return "Hori Electric";
            (0xA6): return "Kawada";
            (0xA7): return "Takara";
            (0xA9): return "Technos Japan";
            (0xAC): return "Toei Animation";
            (0xAD): return "Toho";
            (0xAF): return "Namco";
            (0xB1): return "ASCII or Nexsoft";
            (0xB4): return "Square Enix";
            (0xB6): return "HAL Laboratory";
            (0xB7): return "SNK";
            (0xB9 | 0xCE): return "Pony Canyon";
            (0xBA): return "Culture Brain";
            (0xBB): return "Sunsoft";
            (0xBD): return "Sony Imagesoft";
            (0xBF): return "Sammy";
            (0xC0 | 0xD0): return "Taito";
            (0xC3): return "Squaresoft";
            (0xC5): return "Data East";
            (0xC6): return "Tonkinhouse";
            (0xC8): return "Koei";
            (0xC9): return "UFL";
            (0xCA): return "Ultra";
            (0xCB): return "Vap";
            (0xCC): return "Use Corporation";
            (0xCD): return "Meldac"; 
            (0xD1): return "Sofel";
            (0xD2): return "Quest";
            (0xD3): return "Sigma Enterprises";
            (0xD4): return "ASK Kodansha Co.";
            (0xD7): return "Copya System";
            (0xDA): return "Tomy";
            (0xDD): return "NCS";
            (0xDE): return "Human";
            (0xDF): return "Altron";
            (0xE1): return "Towa Chiki";
            (0xE2): return "Yutaka";
            (0xE3): return "Varie";
            (0xE5): return "Epoch";
            (0xE7): return "Athena";
            (0xE8): return "Asmik";
            (0xE9): return "Natsume";
            (0xEA): return "King Records";
            (0xEE): return "IGS";
            (0xF0): return "A Wave";
            (0xF3): return "Extreme Entertainment";
        }
        return "Unknown Licensee";
    };
    
    fn new_licensee_code(str a) {        
        if(a[0] == 0 && a[1] == 0) return "See old licensee";
        match(a) {
            ("00"): return "None";
            ("01"): return "Nintendo R&D1";
            ("08"): return "Capcom";
            ("13"): return "Electronic Arts";
            ("18"): return "Hudson Soft";
            ("19"): return "b-ai";
            ("20"): return "kss";
            ("22"): return "pow";
            ("24"): return "PCM Complete";
            ("25"): return "san-x";
            ("28"): return "Kemco Japan";
            ("29"): return "seta";
            ("30"): return "Viacom";
            ("31"): return "Nintendo";
            ("32"): return "Bandai";
            ("33"): return "Ocean/Acclaim";
            ("34"): return "Konami";
            ("35"): return "Hector";
            ("37"): return "Taito";
            ("38"): return "Hudson";
            ("39"): return "Banpresto";
            ("41"): return "Ubi Soft";
            ("42"): return "Atlus";
            ("44"): return "Malibu";
            ("46"): return "angel";
            ("47"): return "Bullet-Proof";
            ("49"): return "irem";
            ("50"): return "Absolute";
            ("51"): return "Acclaim";
            ("52"): return "Activision";
            ("53"): return "American sammy";
            ("54"): return "Konami";
            ("55"): return "Hi tech entertainment";
            ("56"): return "LJN";
            ("57"): return "Matchbox";
            ("58"): return "Mattel";
            ("59"): return "Milton Bradley";
            ("60"): return "Titus";
            ("61"): return "Virgin";
            ("64"): return "LucasArts";
            ("67"): return "Ocean";
            ("69"): return "Electronic Arts";
            ("70"): return "Infogrames";
            ("71"): return "Interplay";
            ("72"): return "Broderbund";
            ("73"): return "sculptured";
            ("75"): return "sci";
            ("78"): return "THQ";
            ("79"): return "Accolade";
            ("80"): return "misawa";
            ("83"): return "lozc";
            ("86"): return "Tokuma Shoten Intermedia";
            ("87"): return "Tsukuda Original";
            ("91"): return "Chunksoft";
            ("92"): return "Video system";
            ("93"): return "Ocean/Acclaim";
            ("95"): return "Varie";
            ("96"): return "Yonezawa/s’pal";
            ("97"): return "Kaneko";
            ("99"): return "Pack in soft";
            ("A4"): return "Konami (Yu-Gi-Oh!)";
        }
        return "Unknown";
    };
}

bitfield pixel_packed {
    p1 : 1;
    p2 : 1;
    p3 : 1;
    p4 : 1;
    p5 : 1;
    p6 : 1;
    p7 : 1;
    p8 : 1;
};

enum CGB : u8 {
    NO_CGB = 0x00 ... 0x7F,
    BACKWARDS_COMPATIBLE = 0x80,
    CGB_ONLY = 0xC0
};

  
struct Logo {
    pixel_packed rawData[0x30];
};

struct JumpVectors {
    u8 rst1[8] [[comment("rst $1")]];
    u8 rst2[8] [[comment("rst $2")]];
    u8 rst3[8] [[comment("rst $3")]];
    u8 rst4[8] [[comment("rst $4")]];
    u8 rst5[8] [[comment("rst $5")]];;
    u8 rst6[8] [[comment("rst $6")]];
    u8 rst7[8] [[comment("rst $7")]];
    u8 rst8[8] [[comment("rst $8")]];
    u8 int1[8] [[comment("vblank")]];
    u8 int2[8] [[comment("lcd stat")]];
    u8 int3[8] [[comment("timer")]];
    u8 int4[8] [[comment("serial")]];
    u8 int5[8] [[comment("joypad")]];
};
    
struct Header {
    u32 jmpInstruction;
    Logo logo;
    char title[0xF];
    CGB cgb;
    char newLicenseeCode[2] [[format("format::new_licensee_code")]];
    bool sgbFlag;
    u8 type [[format("format::rom_features")]];
    u8 romType [[format("format::rom_type")]];
    u8 ramType [[format("format::ram_type")]];
    bool japanOnly;
    u8 licenseeCode [[format("format::licensee_code")]];
    u8 version;
    u8 checksum [[comment("Checksum for: $134-$14C")]];
    u16 gloalChecksum [[comment("Checksum for entire range")]];
};

struct Cartrige {
    JumpVectors jumpVectors [[comment("Instructions called on interupts or rst instructions")]];
    padding[0x100 - sizeof(JumpVectors)];
    Header header;
};


Cartrige cart @ 0x00;