/* CART header support library */

#include <string.h>
#include <stdio.h>
#include "cart.h"

/* 20071227 bkw:
	cart_types list was made from atari800-2.0.3/DOC/cart.txt
	I didn't use any code from atari800, since it's GPL and this code
	is WTFPL.

	Format of CART header (from atari800 cart.txt):

 first 4 bytes containing 'C' 'A' 'R' 'T'.
 next 4 bytes containing cartridge type in MSB format (see the table below).
 next 4 bytes containing cartridge checksum in MSB format (ROM only).
 next 4 bytes are currently unused (zero).
 followed immediately with the ROM data: 4, 8, 16, 32, 40, 64, 128, 256, 512
 or 1024 kilobytes.
*/

/* 20071227 bkw: this list is complete as of Atari800 2.0.3 */
/* 20220827 bkw: list now complete as of Atari800 5.0.0 */
/* 20240421 bkw: list now complete as of Atari800 5.2.0.
   The cart type numbers now have a gap in them: type 160 is
	valid, but types 104 to 159 are not. */
cart_t cart_types[] = {
	/* Id */ /* Machine,   Size,  Name */
	/*  0 */  { M_INVALID,    0,  0 },   /* 0 is invalid type */
	/*  1 */  { M_ATARI8,     8,  "Standard 8 KB" },
	/*  2 */  { M_ATARI8,    16,  "Standard 16 KB" },
	/*  3 */  { M_ATARI8,    16,  "OSS two chip 16 KB cartridge (034M)" },
	/*  4 */  { M_5200,      32,  "Standard 32 KB 5200" },
	/*  5 */  { M_ATARI8,    32,  "DB 32 KB" },
	/*  6 */  { M_5200,      16,  "Two chip 16 KB 5200" },
	/*  7 */  { M_5200,      40,  "Bounty Bob Strikes Back 40 KB 5200" },
	/*  8 */  { M_ATARI8,    64,  "64 KB Williams" },
	/*  9 */  { M_ATARI8,    64,  "Express 64 KB" },
	/* 10 */  { M_ATARI8,    64,  "Diamond 64 KB" },
	/* 11 */  { M_ATARI8,    64,  "SpartaDos X 64 KB" },
	/* 12 */  { M_ATARI8,    32,  "XEGS 32 KB" },
	/* 13 */  { M_ATARI8,    64,  "XEGS 64 KB (banks 0-7)" },
	/* 14 */  { M_ATARI8,   128,  "XEGS 128 KB" },
	/* 15 */  { M_ATARI8,    16,  "OSS one chip 16 KB" },
	/* 16 */  { M_5200,      16,  "One chip 16 KB 5200" },
	/* 17 */  { M_ATARI8,   128,  "Decoded Atrax 128 KB" },
	/* 18 */  { M_ATARI8,    40,  "Bounty Bob Strikes Back 40 KB" },
	/* 19 */  { M_5200,       8,  "Standard 8 KB 5200" },
	/* 20 */  { M_5200,       4,  "Standard 4 KB 5200" },
	/* 21 */  { M_ATARI8,     8,  "Right slot 8 KB" },
	/* 22 */  { M_ATARI8,    32,  "32 KB Williams" },
	/* 23 */  { M_ATARI8,   256,  "XEGS 256 KB" },
	/* 24 */  { M_ATARI8,   512,  "XEGS 512 KB" },
	/* 25 */  { M_ATARI8,  1024,  "XEGS 1 MB" },
	/* 26 */  { M_ATARI8,    16,  "MegaCart 16 KB" },
	/* 27 */  { M_ATARI8,    32,  "MegaCart 32 KB" },
	/* 28 */  { M_ATARI8,    64,  "MegaCart 64 KB" },
	/* 29 */  { M_ATARI8,   128,  "MegaCart 128 KB" },
	/* 30 */  { M_ATARI8,   256,  "MegaCart 256 KB" },
	/* 31 */  { M_ATARI8,   512,  "MegaCart 512 KB" },
	/* 32 */  { M_ATARI8,  1024,  "MegaCart 1 MB" },
	/* 33 */  { M_ATARI8,    32,  "Switchable XEGS 32 KB" },
	/* 34 */  { M_ATARI8,    64,  "Switchable XEGS 64 KB" },
	/* 35 */  { M_ATARI8,   128,  "Switchable XEGS 128 KB" },
	/* 36 */  { M_ATARI8,   256,  "Switchable XEGS 256 KB" },
	/* 37 */  { M_ATARI8,   512,  "Switchable XEGS 512 KB" },
	/* 38 */  { M_ATARI8,  1024,  "Switchable XEGS 1 MB" },
	/* 39 */  { M_ATARI8,     8,  "Phoenix 8 KB" },
	/* 40 */  { M_ATARI8,    16,  "Blizzard 16 KB" },
	/* 41 */  { M_ATARI8,   128,  "Atarimax 128 KB Flash" },
	/* 42 */  { M_ATARI8,  1024,  "Atarimax 1 MB Flash (old)" },
	/* 43 */  { M_ATARI8,   128,  "SpartaDos X 128 KB" },
	/* 44 */  { M_ATARI8,     8,  "OSS 8 KB" },
	/* 45 */  { M_ATARI8,    16,  "OSS two chip 16 KB (043M)" },
	/* 46 */  { M_ATARI8,     4,  "Blizzard 4 KB" },
	/* 47 */  { M_ATARI8,    32,  "AST 32 KB" },
	/* 48 */  { M_ATARI8,    64,  "Atrax SDX 64 KB" },
	/* 49 */  { M_ATARI8,   128,  "Atrax SDX 128 KB" },
	/* 50 */  { M_ATARI8,    64,  "Turbosoft 64 KB" },
	/* 51 */  { M_ATARI8,   128,  "Turbosoft 128 KB" },
	/* 52 */  { M_ATARI8,    32,  "Ultracart 32 KB" },
	/* 53 */  { M_ATARI8,     8,  "Low bank 8 KB" },
	/* 54 */  { M_ATARI8,   128,  "SIC! 128 KB" },
	/* 55 */  { M_ATARI8,   256,  "SIC! 256 KB" },
	/* 56 */  { M_ATARI8,   512,  "SIC! 512 KB" },
	/* 57 */  { M_ATARI8,     2,  "Standard 2 KB" },
	/* 58 */  { M_ATARI8,     4,  "Standard 4 KB" },
	/* 59 */  { M_ATARI8,     4,  "Right slot 4 KB" },
	/* 60 */  { M_ATARI8,    32,  "Blizzard 32 KB" },
	/* 61 */  { M_ATARI8,  2048,  "MegaMax 2 MB" },
	/* 62 */  { M_ATARI8,131072,  "The!Cart 128 MB" },
	/* 63 */  { M_ATARI8,  4096,  "Flash MegaCart 4 MB" },
	/* 64 */  { M_ATARI8,  2048,  "MegaCart 2 MB" },
	/* 65 */  { M_ATARI8, 32768,  "The!Cart 32 MB" },
	/* 66 */  { M_ATARI8, 65536,  "The!Cart 64 MB" },
	/* 67 */  { M_ATARI8,    64,  "XEGS 64 KB (banks 8-15)" },
	/* 68 */  { M_ATARI8,   128,  "Atrax 128 KB" },
	/* 69 */  { M_ATARI8,    32,  "aDawliah 32 KB" },
	/* 70 */  { M_ATARI8,    64,  "aDawliah 64 KB" },
	/* 71 */  { M_5200,      64,  "Super Cart 64 KB 5200" },
	/* 72 */  { M_5200,     128,  "Super Cart 128 KB 5200" },
	/* 73 */  { M_5200,     256,  "Super Cart 256 KB 5200" },
	/* 74 */  { M_5200,     512,  "Super Cart 512 KB 5200" },
	/* 75 */  { M_ATARI8,  1024,  "Atarimax 1 MB Flash (new)" },
	/* 76 */  { M_ATARI8,    16, "16 KB Williams cartridge" },
	/* 77 */  { M_ATARI8,     8, "MIO diagnostics 8KB cartridge" },
	/* 78 */  { M_ATARI8,     8, "Telelink II cartridge" },
	/* 79 */  { M_ATARI8,    16, "Pronto cartridge" },
	/* 80 */  { M_ATARI8,    64, "JRC64 cartridge (linear)" },
	/* 81 */  { M_ATARI8,    64, "MDDOS cartridge" },
	/* 82 */  { M_ATARI8,    32, "COS32 cartridge" },
	/* 83 */  { M_ATARI8,  1024, "SIC+ 1024 KB cartridge" },
	/* 84 */  { M_ATARI8,  1024, "Corina 1M+8K EEPROM" },
	/* 85 */  { M_ATARI8,   512, "Corina 512K + 512K SRAM + 8K EEPROM" },
	/* 86 */  { M_ATARI8,     8, "XE Multicart (8KB)" },
	/* 87 */  { M_ATARI8,    16, "XE Multicart (16KB)" },
	/* 88 */  { M_ATARI8,    32, "XE Multicart (32KB)" },
	/* 89 */  { M_ATARI8,    64, "XE Multicart (64KB)" },
	/* 90 */  { M_ATARI8,   128, "XE Multicart (128KB)" },
	/* 91 */  { M_ATARI8,   256, "XE Multicart (256KB)" },
	/* 92 */  { M_ATARI8,   512, "XE Multicart (512KB)" },
	/* 93 */  { M_ATARI8,  1024, "XE Multicart (1024KB)" },
	/* 94 */  { M_ATARI8,    64, "Ram-Cart 64 KB cartridge" },
	/* 95 */  { M_ATARI8,   128, "Ram-Cart 128 KB cartridge" },
	/* 96 */  { M_ATARI8,   256, "Double Ram-Cart 2x128/256 KB cartridge" },
	/* 97 */  { M_ATARI8,  1024, "Ram-Cart 1 MB cartridge" },
	/* 98 */  { M_ATARI8,  2048, "Ram-Cart 2 MB cartridge" },
	/* 99 */  { M_ATARI8,  4096, "Ram-Cart 4 MB cartridge" },
	/* 100 */ { M_ATARI8,  8192, "Ram-Cart 8 MB cartridge" },
	/* 101 */ { M_ATARI8, 16384, "Ram-Cart 16 MB cartridge" },
	/* 102 */ { M_ATARI8, 32768, "Ram-Cart 32 MB cartridge" },
	/* 103 */ { M_ATARI8,    32, "SiDiCar 32 KB cartridge" },
	/* 104 */ { M_INVALID,    0, 0 },
	/* 105 */ { M_INVALID,    0, 0 },
	/* 106 */ { M_INVALID,    0, 0 },
	/* 107 */ { M_INVALID,    0, 0 },
	/* 108 */ { M_INVALID,    0, 0 },
	/* 109 */ { M_INVALID,    0, 0 },
	/* 110 */ { M_INVALID,    0, 0 },
	/* 111 */ { M_INVALID,    0, 0 },
	/* 112 */ { M_INVALID,    0, 0 },
	/* 113 */ { M_INVALID,    0, 0 },
	/* 114 */ { M_INVALID,    0, 0 },
	/* 115 */ { M_INVALID,    0, 0 },
	/* 116 */ { M_INVALID,    0, 0 },
	/* 117 */ { M_INVALID,    0, 0 },
	/* 118 */ { M_INVALID,    0, 0 },
	/* 119 */ { M_INVALID,    0, 0 },
	/* 120 */ { M_INVALID,    0, 0 },
	/* 121 */ { M_INVALID,    0, 0 },
	/* 122 */ { M_INVALID,    0, 0 },
	/* 123 */ { M_INVALID,    0, 0 },
	/* 124 */ { M_INVALID,    0, 0 },
	/* 125 */ { M_INVALID,    0, 0 },
	/* 126 */ { M_INVALID,    0, 0 },
	/* 127 */ { M_INVALID,    0, 0 },
	/* 128 */ { M_INVALID,    0, 0 },
	/* 129 */ { M_INVALID,    0, 0 },
	/* 130 */ { M_INVALID,    0, 0 },
	/* 131 */ { M_INVALID,    0, 0 },
	/* 132 */ { M_INVALID,    0, 0 },
	/* 133 */ { M_INVALID,    0, 0 },
	/* 134 */ { M_INVALID,    0, 0 },
	/* 135 */ { M_INVALID,    0, 0 },
	/* 136 */ { M_INVALID,    0, 0 },
	/* 137 */ { M_INVALID,    0, 0 },
	/* 138 */ { M_INVALID,    0, 0 },
	/* 139 */ { M_INVALID,    0, 0 },
	/* 140 */ { M_INVALID,    0, 0 },
	/* 141 */ { M_INVALID,    0, 0 },
	/* 142 */ { M_INVALID,    0, 0 },
	/* 143 */ { M_INVALID,    0, 0 },
	/* 144 */ { M_INVALID,    0, 0 },
	/* 145 */ { M_INVALID,    0, 0 },
	/* 146 */ { M_INVALID,    0, 0 },
	/* 147 */ { M_INVALID,    0, 0 },
	/* 148 */ { M_INVALID,    0, 0 },
	/* 149 */ { M_INVALID,    0, 0 },
	/* 150 */ { M_INVALID,    0, 0 },
	/* 151 */ { M_INVALID,    0, 0 },
	/* 152 */ { M_INVALID,    0, 0 },
	/* 153 */ { M_INVALID,    0, 0 },
	/* 154 */ { M_INVALID,    0, 0 },
	/* 155 */ { M_INVALID,    0, 0 },
	/* 156 */ { M_INVALID,    0, 0 },
	/* 157 */ { M_INVALID,    0, 0 },
	/* 158 */ { M_INVALID,    0, 0 },
	/* 159 */ { M_INVALID,    0, 0 },
	/* 160 */ { M_ATARI8,    64, "JRC64 cartridge (interleaved)" },
};

const int MAX_CART_TYPE = (sizeof(cart_types)/sizeof(cart_t))-1;

/* helper functions.
	get_msb_dword() and put_msb_dword() are written the way they are in
	order to avoid endian-ness issues caused by casts or calls to some
	endian-aware function like htonl().
*/

static unsigned int get_msb_dword(unsigned char *cart, int offset) {
	return
		(cart[offset]   << 24) |
		(cart[offset+1] << 16) |
		(cart[offset+2] <<  8) |
		(cart[offset+3]);
}

static void put_msb_dword(unsigned char *cart, int offset, unsigned int data) {
	int i;
	for(i=3; i>=0; i--) {
		cart[offset+i] = data & 0xff;
		data >>= 8;
	}
}

/* has_cart_signature() returns boolean */
int has_cart_signature(unsigned char *cart) {
	return memcmp(cart, CART_SIGNATURE, 4) == 0;
}

void cart_dump_header(unsigned char *cart, int calc_cksum) {
	char *type = "UNKNOWN";
	unsigned int id = get_cart_type(cart);
	unsigned int real_id = get_msb_dword(cart, CART_TYPE_OFFSET);

	if(id)
		type = cart_types[id].name;
	else
		calc_cksum = 0;

	/* Show real type, if get_cart_type() returned zero */
	fprintf(stderr, "CART signature: %s\n",
			(has_cart_signature(cart) ? "Present" : "MISSING"));
	fprintf(stderr, "Cartridge type: %s (ID %d)\n", type, real_id);
	fprintf(stderr, "ROM size:       ");
	if(id)
		fprintf(stderr, "%d bytes + 16 byte header\n", get_cart_size(cart));
	else
		fprintf(stderr, "UNKNOWN\n");

	fprintf(stderr, "Machine type:   ");
	switch(get_cart_machine(cart)) {
		case M_ATARI8:
			fprintf(stderr, "Atari 8-bit computer\n");
			break;

		case M_5200:
			fprintf(stderr, "Atari 5200\n");
			break;

		default:
			fprintf(stderr, "UNKNOWN\n");
			break;
	}

	fprintf(stderr, "CART checksum:  0x%08x", get_cart_checksum(cart));
	if(calc_cksum) {
		fputs((cart_checksum_ok(cart) ? "OK" : "BAD"), stderr);
	}
	fputc('\n', stderr);
}

/* get_cart_type() returns the cartridge ID from the CART header, or zero
	if it's an invalid/unknown type. */
unsigned int get_cart_type(unsigned char *cart) {
	unsigned int type = get_msb_dword(cart, CART_TYPE_OFFSET);

	if(type == 0 || type > MAX_CART_TYPE)
		return 0;

	return type;
}

/* get_cart_size() returns the cartridge ROM size in bytes, NOT including
	the 16-byte header, or zero if invalid type */
unsigned int get_cart_size(unsigned char *cart) {
	unsigned int type = get_cart_type(cart);
	if(type == 0)
		return 0;

	return cart_types[type].size * 1024;
}

/* get_cart_checksum() returns the checksum stored in the header. It does
	NOT calculate the checksum (use calc_rom_checksum() for that) */
unsigned int get_cart_checksum(unsigned char *cart) {
	return get_msb_dword(cart, CART_CHECKSUM_OFFSET);
}

machine_t get_cart_machine(unsigned char *cart) {
	unsigned int type = get_cart_type(cart);
	if(type == 0)
		return M_INVALID;

	return cart_types[type].machine;
}

/* calc_rom_checksum() is the odd man out: it takes a pointer to the
	raw ROM data, NOT to the header+data like all the other functions do! */
unsigned int calc_rom_checksum(unsigned char *rom, int bytes) {
	unsigned int cksum = 0;

	while(bytes-- > 0) cksum += *rom++;

	return cksum;
}

/* cart_checksum_ok() must be called with a complete .CAR image, not just
	a header! */
unsigned int cart_checksum_ok(unsigned char *cart) {
	unsigned int bytes = get_cart_size(cart);
	unsigned int got = calc_rom_checksum(cart + 16, bytes);
	unsigned int expected = get_cart_checksum(cart);

	return (got == expected);
}

/* create_cart_header() must be supplied with a buffer, which must be at least
	16 bytes in size. Returns 0 with buffer unchanged if type was invalid. */
int create_cart_header(
		unsigned char *buffer,
		unsigned char *rom,
		unsigned int type)
{
	if(type == 0 || type > MAX_CART_TYPE)
		return 0;

	memcpy(buffer, CART_SIGNATURE, 4);

	set_cart_type(buffer, type);
	set_cart_checksum(buffer,
			calc_rom_checksum(rom, cart_types[type].size * 1024));
	set_cart_unused(buffer, 0);

	return 1;
}

void set_cart_checksum(unsigned char *cart, unsigned int sum) {
	put_msb_dword(cart, CART_CHECKSUM_OFFSET, sum);
}

void set_cart_type(unsigned char *cart, unsigned int type) {
	put_msb_dword(cart, CART_TYPE_OFFSET, type);
}

void set_cart_unused(unsigned char *cart, unsigned int data) {
	put_msb_dword(cart, CART_UNUSED_OFFSET, data);
}