diff options
Diffstat (limited to 'mkcart.c')
-rw-r--r-- | mkcart.c | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/mkcart.c b/mkcart.c new file mode 100644 index 0000000..28ba5d5 --- /dev/null +++ b/mkcart.c @@ -0,0 +1,678 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* + mkcart.c, by B. Watson, part of DASM Atari 8-bit support. + + DASM and atari800 are both GPLv2 so I've lifted code straight + from the emulator. + + This is not great C code. It's what happens when I try to write + C after spending a week hacking assembly code. + */ + +/* nobody needs more input files than this, right? I should + define it to 640K :) */ +#define MAX_INPUT_FILES 1024 + +/* this is the smallest supported cart size */ +#define CARTBUFLEN 2048 + +typedef struct { + char *name; + int size; +} cart_t; + +/* from atari800-3.1.0/src/cartridge.h */ +enum { + CARTRIDGE_UNKNOWN = -1, + CARTRIDGE_NONE = 0, + CARTRIDGE_STD_8 = 1, + CARTRIDGE_STD_16 = 2, + CARTRIDGE_OSS_034M_16 = 3, + CARTRIDGE_5200_32 = 4, + CARTRIDGE_DB_32 = 5, + CARTRIDGE_5200_EE_16 = 6, + CARTRIDGE_5200_40 = 7, + CARTRIDGE_WILL_64 = 8, + CARTRIDGE_EXP_64 = 9, + CARTRIDGE_DIAMOND_64 = 10, + CARTRIDGE_SDX_64 = 11, + CARTRIDGE_XEGS_32 = 12, + CARTRIDGE_XEGS_07_64 = 13, + CARTRIDGE_XEGS_128 = 14, + CARTRIDGE_OSS_M091_16 = 15, + CARTRIDGE_5200_NS_16 = 16, + CARTRIDGE_ATRAX_128 = 17, + CARTRIDGE_BBSB_40 = 18, + CARTRIDGE_5200_8 = 19, + CARTRIDGE_5200_4 = 20, + CARTRIDGE_RIGHT_8 = 21, + CARTRIDGE_WILL_32 = 22, + CARTRIDGE_XEGS_256 = 23, + CARTRIDGE_XEGS_512 = 24, + CARTRIDGE_XEGS_1024 = 25, + CARTRIDGE_MEGA_16 = 26, + CARTRIDGE_MEGA_32 = 27, + CARTRIDGE_MEGA_64 = 28, + CARTRIDGE_MEGA_128 = 29, + CARTRIDGE_MEGA_256 = 30, + CARTRIDGE_MEGA_512 = 31, + CARTRIDGE_MEGA_1024 = 32, + CARTRIDGE_SWXEGS_32 = 33, + CARTRIDGE_SWXEGS_64 = 34, + CARTRIDGE_SWXEGS_128 = 35, + CARTRIDGE_SWXEGS_256 = 36, + CARTRIDGE_SWXEGS_512 = 37, + CARTRIDGE_SWXEGS_1024 = 38, + CARTRIDGE_PHOENIX_8 = 39, + CARTRIDGE_BLIZZARD_16 = 40, + CARTRIDGE_ATMAX_128 = 41, + CARTRIDGE_ATMAX_1024 = 42, + CARTRIDGE_SDX_128 = 43, + CARTRIDGE_OSS_8 = 44, + CARTRIDGE_OSS_043M_16 = 45, + CARTRIDGE_BLIZZARD_4 = 46, + CARTRIDGE_AST_32 = 47, + CARTRIDGE_ATRAX_SDX_64 = 48, + CARTRIDGE_ATRAX_SDX_128 = 49, + CARTRIDGE_TURBOSOFT_64 = 50, + CARTRIDGE_TURBOSOFT_128 = 51, + CARTRIDGE_ULTRACART_32 = 52, + CARTRIDGE_LOW_BANK_8 = 53, + CARTRIDGE_SIC_128 = 54, + CARTRIDGE_SIC_256 = 55, + CARTRIDGE_SIC_512 = 56, + CARTRIDGE_STD_2 = 57, + CARTRIDGE_STD_4 = 58, + CARTRIDGE_RIGHT_4 = 59, + CARTRIDGE_BLIZZARD_32 = 60, + CARTRIDGE_MEGAMAX_2048 = 61, + CARTRIDGE_THECART_128M = 62, + CARTRIDGE_MEGA_4096 = 63, + CARTRIDGE_MEGA_2048 = 64, + CARTRIDGE_THECART_32M = 65, + CARTRIDGE_THECART_64M = 66, + CARTRIDGE_XEGS_8F_64 = 67, + CARTRIDGE_LAST_SUPPORTED = 67 +}; + +#define CARTRIDGE_MAX_SIZE (128 * 1024 * 1024) + +#define CARTRIDGE_STD_8_DESC "Standard 8 KB cartridge" +#define CARTRIDGE_STD_16_DESC "Standard 16 KB cartridge" +#define CARTRIDGE_OSS_034M_16_DESC "OSS two chip 16 KB cartridge (034M)" +#define CARTRIDGE_5200_32_DESC "Standard 32 KB 5200 cartridge" +#define CARTRIDGE_DB_32_DESC "DB 32 KB cartridge" +#define CARTRIDGE_5200_EE_16_DESC "Two chip 16 KB 5200 cartridge" +#define CARTRIDGE_5200_40_DESC "Bounty Bob 40 KB 5200 cartridge" +#define CARTRIDGE_WILL_64_DESC "64 KB Williams cartridge" +#define CARTRIDGE_EXP_64_DESC "Express 64 KB cartridge" +#define CARTRIDGE_DIAMOND_64_DESC "Diamond 64 KB cartridge" +#define CARTRIDGE_SDX_64_DESC "SpartaDOS X 64 KB cartridge" +#define CARTRIDGE_XEGS_32_DESC "XEGS 32 KB cartridge" +#define CARTRIDGE_XEGS_07_64_DESC "XEGS 64 KB cartridge (banks 0-7)" +#define CARTRIDGE_XEGS_128_DESC "XEGS 128 KB cartridge" +#define CARTRIDGE_OSS_M091_16_DESC "OSS one chip 16 KB cartridge" +#define CARTRIDGE_5200_NS_16_DESC "One chip 16 KB 5200 cartridge" +#define CARTRIDGE_ATRAX_128_DESC "Atrax 128 KB cartridge" +#define CARTRIDGE_BBSB_40_DESC "Bounty Bob 40 KB cartridge" +#define CARTRIDGE_5200_8_DESC "Standard 8 KB 5200 cartridge" +#define CARTRIDGE_5200_4_DESC "Standard 4 KB 5200 cartridge" +#define CARTRIDGE_RIGHT_8_DESC "Right slot 8 KB cartridge" +#define CARTRIDGE_WILL_32_DESC "32 KB Williams cartridge" +#define CARTRIDGE_XEGS_256_DESC "XEGS 256 KB cartridge" +#define CARTRIDGE_XEGS_512_DESC "XEGS 512 KB cartridge" +#define CARTRIDGE_XEGS_1024_DESC "XEGS 1 MB cartridge" +#define CARTRIDGE_MEGA_16_DESC "MegaCart 16 KB cartridge" +#define CARTRIDGE_MEGA_32_DESC "MegaCart 32 KB cartridge" +#define CARTRIDGE_MEGA_64_DESC "MegaCart 64 KB cartridge" +#define CARTRIDGE_MEGA_128_DESC "MegaCart 128 KB cartridge" +#define CARTRIDGE_MEGA_256_DESC "MegaCart 256 KB cartridge" +#define CARTRIDGE_MEGA_512_DESC "MegaCart 512 KB cartridge" +#define CARTRIDGE_MEGA_1024_DESC "MegaCart 1 MB cartridge" +#define CARTRIDGE_SWXEGS_32_DESC "Switchable XEGS 32 KB cartridge" +#define CARTRIDGE_SWXEGS_64_DESC "Switchable XEGS 64 KB cartridge" +#define CARTRIDGE_SWXEGS_128_DESC "Switchable XEGS 128 KB cartridge" +#define CARTRIDGE_SWXEGS_256_DESC "Switchable XEGS 256 KB cartridge" +#define CARTRIDGE_SWXEGS_512_DESC "Switchable XEGS 512 KB cartridge" +#define CARTRIDGE_SWXEGS_1024_DESC "Switchable XEGS 1 MB cartridge" +#define CARTRIDGE_PHOENIX_8_DESC "Phoenix 8 KB cartridge" +#define CARTRIDGE_BLIZZARD_16_DESC "Blizzard 16 KB cartridge" +#define CARTRIDGE_ATMAX_128_DESC "Atarimax 128 KB Flash cartridge" +#define CARTRIDGE_ATMAX_1024_DESC "Atarimax 1 MB Flash cartridge" +#define CARTRIDGE_SDX_128_DESC "SpartaDOS X 128 KB cartridge" +#define CARTRIDGE_OSS_8_DESC "OSS 8 KB cartridge" +#define CARTRIDGE_OSS_043M_16_DESC "OSS two chip 16 KB cartridge (043M)" +#define CARTRIDGE_BLIZZARD_4_DESC "Blizzard 4 KB cartridge" +#define CARTRIDGE_AST_32_DESC "AST 32 KB cartridge" +#define CARTRIDGE_ATRAX_SDX_64_DESC "Atrax SDX 64 KB cartridge" +#define CARTRIDGE_ATRAX_SDX_128_DESC "Atrax SDX 128 KB cartridge" +#define CARTRIDGE_TURBOSOFT_64_DESC "Turbosoft 64 KB cartridge" +#define CARTRIDGE_TURBOSOFT_128_DESC "Turbosoft 128 KB cartridge" +#define CARTRIDGE_ULTRACART_32_DESC "Ultracart 32 KB cartridge" +#define CARTRIDGE_LOW_BANK_8_DESC "Low bank 8 KB cartridge" +#define CARTRIDGE_SIC_128_DESC "SIC! 128 KB cartridge" +#define CARTRIDGE_SIC_256_DESC "SIC! 256 KB cartridge" +#define CARTRIDGE_SIC_512_DESC "SIC! 512 KB cartridge" +#define CARTRIDGE_STD_2_DESC "Standard 2 KB cartridge" +#define CARTRIDGE_STD_4_DESC "Standard 4 KB cartridge" +#define CARTRIDGE_RIGHT_4_DESC "Right slot 4 KB cartridge" +#define CARTRIDGE_BLIZZARD_32_DESC "Blizzard 32 KB cartridge" +#define CARTRIDGE_MEGAMAX_2048_DESC "MegaMax 2 MB cartridge" +#define CARTRIDGE_THECART_128M_DESC "The!Cart 128 MB cartridge" +#define CARTRIDGE_MEGA_4096_DESC "Flash MegaCart 4 MB cartridge" +#define CARTRIDGE_MEGA_2048_DESC "MegaCart 2 MB cartridge" +#define CARTRIDGE_THECART_32M_DESC "The!Cart 32 MB cartridge" +#define CARTRIDGE_THECART_64M_DESC "The!Cart 64 MB cartridge" +#define CARTRIDGE_XEGS_8F_64_DESC "XEGS 64 KB cartridge (banks 8-15)" + +/* this bit didn't come from atari800 */ +static cart_t cart_types[CARTRIDGE_LAST_SUPPORTED + 1]; +#define UI_MENU_ACTION(index, desc) \ + cart_types[index].size = CARTRIDGE_kb[index]*1024; \ + cart_types[index].name = desc; + +/* from atari800-3.1.0/src/cartridge.c */ +int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1] = { + 0, + 8, /* CARTRIDGE_STD_8 */ + 16, /* CARTRIDGE_STD_16 */ + 16, /* CARTRIDGE_OSS_034M_16 */ + 32, /* CARTRIDGE_5200_32 */ + 32, /* CARTRIDGE_DB_32 */ + 16, /* CARTRIDGE_5200_EE_16 */ + 40, /* CARTRIDGE_5200_40 */ + 64, /* CARTRIDGE_WILL_64 */ + 64, /* CARTRIDGE_EXP_64 */ + 64, /* CARTRIDGE_DIAMOND_64 */ + 64, /* CARTRIDGE_SDX_64 */ + 32, /* CARTRIDGE_XEGS_32 */ + 64, /* CARTRIDGE_XEGS_64_07 */ + 128, /* CARTRIDGE_XEGS_128 */ + 16, /* CARTRIDGE_OSS_M091_16 */ + 16, /* CARTRIDGE_5200_NS_16 */ + 128, /* CARTRIDGE_ATRAX_128 */ + 40, /* CARTRIDGE_BBSB_40 */ + 8, /* CARTRIDGE_5200_8 */ + 4, /* CARTRIDGE_5200_4 */ + 8, /* CARTRIDGE_RIGHT_8 */ + 32, /* CARTRIDGE_WILL_32 */ + 256, /* CARTRIDGE_XEGS_256 */ + 512, /* CARTRIDGE_XEGS_512 */ + 1024, /* CARTRIDGE_XEGS_1024 */ + 16, /* CARTRIDGE_MEGA_16 */ + 32, /* CARTRIDGE_MEGA_32 */ + 64, /* CARTRIDGE_MEGA_64 */ + 128, /* CARTRIDGE_MEGA_128 */ + 256, /* CARTRIDGE_MEGA_256 */ + 512, /* CARTRIDGE_MEGA_512 */ + 1024, /* CARTRIDGE_MEGA_1024 */ + 32, /* CARTRIDGE_SWXEGS_32 */ + 64, /* CARTRIDGE_SWXEGS_64 */ + 128, /* CARTRIDGE_SWXEGS_128 */ + 256, /* CARTRIDGE_SWXEGS_256 */ + 512, /* CARTRIDGE_SWXEGS_512 */ + 1024, /* CARTRIDGE_SWXEGS_1024 */ + 8, /* CARTRIDGE_PHOENIX_8 */ + 16, /* CARTRIDGE_BLIZZARD_16 */ + 128, /* CARTRIDGE_ATMAX_128 */ + 1024, /* CARTRIDGE_ATMAX_1024 */ + 128, /* CARTRIDGE_SDX_128 */ + 8, /* CARTRIDGE_OSS_8 */ + 16, /* CARTRIDGE_OSS_043M_16 */ + 4, /* CARTRIDGE_BLIZZARD_4 */ + 32, /* CARTRIDGE_AST_32 */ + 64, /* CARTRIDGE_ATRAX_SDX_64 */ + 128, /* CARTRIDGE_ATRAX_SDX_128 */ + 64, /* CARTRIDGE_TURBOSOFT_64 */ + 128, /* CARTRIDGE_TURBOSOFT_128 */ + 32, /* CARTRIDGE_ULTRACART_32 */ + 8, /* CARTRIDGE_LOW_BANK_8 */ + 128, /* CARTRIDGE_SIC_128 */ + 256, /* CARTRIDGE_SIC_256 */ + 512, /* CARTRIDGE_SIC_512 */ + 2, /* CARTRIDGE_STD_2 */ + 4, /* CARTRIDGE_STD_4 */ + 4, /* CARTRIDGE_RIGHT_4 */ + 32, /* CARTRIDGE_TURBO_HIT_32 */ + 2048, /* CARTRIDGE_MEGA_2048 */ + 128*1024, /* CARTRIDGE_THECART_128M */ + 4096, /* CARTRIDGE_MEGA_4096 */ + 2048, /* CARTRIDGE_MEGA_2048 */ + 32*1024, /* CARTRIDGE_THECART_32M */ + 64*1024, /* CARTRIDGE_THECART_64M */ + 64 /* CARTRIDGE_XEGS_64_8F */ +}; + +/* Adapted from from atari800-3.1.0/src/ui.c, by s/,$/;/ */ +void init() { + UI_MENU_ACTION(CARTRIDGE_STD_8, CARTRIDGE_STD_8_DESC); + UI_MENU_ACTION(CARTRIDGE_STD_16, CARTRIDGE_STD_16_DESC); + UI_MENU_ACTION(CARTRIDGE_OSS_034M_16, CARTRIDGE_OSS_034M_16_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_32, CARTRIDGE_5200_32_DESC); + UI_MENU_ACTION(CARTRIDGE_DB_32, CARTRIDGE_DB_32_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_EE_16, CARTRIDGE_5200_EE_16_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_40, CARTRIDGE_5200_40_DESC); + UI_MENU_ACTION(CARTRIDGE_WILL_64, CARTRIDGE_WILL_64_DESC); + UI_MENU_ACTION(CARTRIDGE_EXP_64, CARTRIDGE_EXP_64_DESC); + UI_MENU_ACTION(CARTRIDGE_DIAMOND_64, CARTRIDGE_DIAMOND_64_DESC); + UI_MENU_ACTION(CARTRIDGE_SDX_64, CARTRIDGE_SDX_64_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_32, CARTRIDGE_XEGS_32_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_07_64, CARTRIDGE_XEGS_07_64_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_128, CARTRIDGE_XEGS_128_DESC); + UI_MENU_ACTION(CARTRIDGE_OSS_M091_16, CARTRIDGE_OSS_M091_16_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_NS_16, CARTRIDGE_5200_NS_16_DESC); + UI_MENU_ACTION(CARTRIDGE_ATRAX_128, CARTRIDGE_ATRAX_128_DESC); + UI_MENU_ACTION(CARTRIDGE_BBSB_40, CARTRIDGE_BBSB_40_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_8, CARTRIDGE_5200_8_DESC); + UI_MENU_ACTION(CARTRIDGE_5200_4, CARTRIDGE_5200_4_DESC); + UI_MENU_ACTION(CARTRIDGE_RIGHT_8, CARTRIDGE_RIGHT_8_DESC); + UI_MENU_ACTION(CARTRIDGE_WILL_32, CARTRIDGE_WILL_32_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_256, CARTRIDGE_XEGS_256_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_512, CARTRIDGE_XEGS_512_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_1024, CARTRIDGE_XEGS_1024_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_16, CARTRIDGE_MEGA_16_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_32, CARTRIDGE_MEGA_32_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_64, CARTRIDGE_MEGA_64_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_128, CARTRIDGE_MEGA_128_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_256, CARTRIDGE_MEGA_256_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_512, CARTRIDGE_MEGA_512_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_1024, CARTRIDGE_MEGA_1024_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_32, CARTRIDGE_SWXEGS_32_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_64, CARTRIDGE_SWXEGS_64_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_128, CARTRIDGE_SWXEGS_128_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_256, CARTRIDGE_SWXEGS_256_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_512, CARTRIDGE_SWXEGS_512_DESC); + UI_MENU_ACTION(CARTRIDGE_SWXEGS_1024, CARTRIDGE_SWXEGS_1024_DESC); + UI_MENU_ACTION(CARTRIDGE_PHOENIX_8, CARTRIDGE_PHOENIX_8_DESC); + UI_MENU_ACTION(CARTRIDGE_BLIZZARD_16, CARTRIDGE_BLIZZARD_16_DESC); + UI_MENU_ACTION(CARTRIDGE_ATMAX_128, CARTRIDGE_ATMAX_128_DESC); + UI_MENU_ACTION(CARTRIDGE_ATMAX_1024, CARTRIDGE_ATMAX_1024_DESC); + UI_MENU_ACTION(CARTRIDGE_SDX_128, CARTRIDGE_SDX_128_DESC); + UI_MENU_ACTION(CARTRIDGE_OSS_8, CARTRIDGE_OSS_8_DESC); + UI_MENU_ACTION(CARTRIDGE_OSS_043M_16, CARTRIDGE_OSS_043M_16_DESC); + UI_MENU_ACTION(CARTRIDGE_BLIZZARD_4, CARTRIDGE_BLIZZARD_4_DESC); + UI_MENU_ACTION(CARTRIDGE_AST_32, CARTRIDGE_AST_32_DESC); + UI_MENU_ACTION(CARTRIDGE_ATRAX_SDX_64, CARTRIDGE_ATRAX_SDX_64_DESC); + UI_MENU_ACTION(CARTRIDGE_ATRAX_SDX_128, CARTRIDGE_ATRAX_SDX_128_DESC); + UI_MENU_ACTION(CARTRIDGE_TURBOSOFT_64, CARTRIDGE_TURBOSOFT_64_DESC); + UI_MENU_ACTION(CARTRIDGE_TURBOSOFT_128, CARTRIDGE_TURBOSOFT_128_DESC); + UI_MENU_ACTION(CARTRIDGE_ULTRACART_32, CARTRIDGE_ULTRACART_32_DESC); + UI_MENU_ACTION(CARTRIDGE_LOW_BANK_8, CARTRIDGE_LOW_BANK_8_DESC); + UI_MENU_ACTION(CARTRIDGE_SIC_128, CARTRIDGE_SIC_128_DESC); + UI_MENU_ACTION(CARTRIDGE_SIC_256, CARTRIDGE_SIC_256_DESC); + UI_MENU_ACTION(CARTRIDGE_SIC_512, CARTRIDGE_SIC_512_DESC); + UI_MENU_ACTION(CARTRIDGE_STD_2, CARTRIDGE_STD_2_DESC); + UI_MENU_ACTION(CARTRIDGE_STD_4, CARTRIDGE_STD_4_DESC); + UI_MENU_ACTION(CARTRIDGE_RIGHT_4, CARTRIDGE_RIGHT_4_DESC); + UI_MENU_ACTION(CARTRIDGE_BLIZZARD_32, CARTRIDGE_BLIZZARD_32_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGAMAX_2048, CARTRIDGE_MEGAMAX_2048_DESC); + UI_MENU_ACTION(CARTRIDGE_THECART_128M, CARTRIDGE_THECART_128M_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_4096, CARTRIDGE_MEGA_4096_DESC); + UI_MENU_ACTION(CARTRIDGE_MEGA_2048, CARTRIDGE_MEGA_2048_DESC); + UI_MENU_ACTION(CARTRIDGE_THECART_32M, CARTRIDGE_THECART_32M_DESC); + UI_MENU_ACTION(CARTRIDGE_THECART_64M, CARTRIDGE_THECART_64M_DESC); + UI_MENU_ACTION(CARTRIDGE_XEGS_8F_64, CARTRIDGE_XEGS_8F_64_DESC); +} + +static int type = -1; /* -1 = guess */ +static int extracting = 0; +static const char *outfile = NULL; +static FILE *output; +static const char *inputfiles[MAX_INPUT_FILES+1]; +static int inputcount = 0; +static int checksum = 0; +static int keep_outfile = 0; +static unsigned char buf[CARTBUFLEN]; + +void list_types() { + int i; + for(i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++) { + printf("%d %d %s\n", i, cart_types[i].size, cart_types[i].name); + } +} + +void usage() { + puts("mkcart v20150421 - create atari800 CART image from raw binaries"); + puts("\nUsage: mkcart -oCARTFILE -tTYPE RAWFILE [RAWFILE ...]"); + puts( " mkcart -cCARTFILE"); + puts( " mkcart -xRAWFILE CARTFILE"); + puts( " mkcart -l"); + printf("\n -tTYPE Cartridge type (1-%d), default = guess (poorly!)\n", + CARTRIDGE_LAST_SUPPORTED); + puts( " -oCARTFILE Create CARTFILE from RAWFILE(s)"); + puts( " -cCARTFILE Check integrity of file (checksum and size)"); + puts( " -xRAWFILE Create raw binary from CARTFILE (remove header)"); + puts( " -l List all supported -t types and exit"); + puts( " -h, -? This help message"); +} + +void open_output() { + if(!outfile) { + fprintf(stderr, "No output file given, use -o option\n"); + exit(1); + } + if( !(output = fopen(outfile, "wb")) ) { + perror(outfile); + exit(1); + } +} + +FILE *open_input(const char *fname) { + FILE *f; + + f = fopen(fname, "rb"); + if(!f) { + perror(fname); + exit(1); + } + return f; +} + +int has_cart_header(const unsigned char *b) { + return ( (buf[0] == 'C') && + (buf[1] == 'A') && + (buf[2] == 'R') && + (buf[3] == 'T') ); +} + +void write_header() { + int i, j, size = 0, checkhdr; + FILE *f; + size_t got; + + for(i = 0; i < inputcount; i++) { + f = open_input(inputfiles[i]); + + if(extracting) { + /* read and check header insead of writing one */ + if(fread(buf, 1, 16, f) < 16) { + perror(inputfiles[i]); + exit(-1); + } + if(!has_cart_header(buf)) { + fprintf(stderr, "%s doesn't have a CART header\n", inputfiles[i]); + exit(-1); + } + return; + } + + checkhdr = 1; + + while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) { + if(checkhdr) { /* only do this on first chunk read */ + if(has_cart_header(buf)) { + fprintf(stderr, + "warning: raw file %s appears to have a CART header\n", + inputfiles[i]); + } + checkhdr = 0; + } + if(got < CARTBUFLEN) { + fprintf(stderr, "warning: %s size not a multiple of %d bytes\n", + inputfiles[i], CARTBUFLEN); + } + for(j = 0; j < got; j++) checksum += buf[j]; + size += got; + } + if(ferror(f)) { + perror(inputfiles[i]); + exit(1); + } + fclose(f); + } + + if(type > 0 && size != cart_types[type].size) { + fprintf(stderr, + "warning: cart type %d (%s) must be %d bytes, " + "but we read %d from our input files\n", + type, cart_types[type].name, cart_types[type].size, size); + } + + if(type < 1) { + for(i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++) { + if(size == (cart_types[i].size)) { + type = i; + fprintf(stderr, "warning: no -t option, guessing type %d (%s)\n", + i, cart_types[i].name); + break; + } + } + if(type < 1) { + fprintf(stderr, + "fatal: no -t option, no type matches file size %d bytes\n", + size); + exit(-1); + } + } + + /* more like assembly than C, but it avoids endian issues */ + buf[0] = 'C'; + buf[1] = 'A'; + buf[2] = 'R'; + buf[3] = 'T'; + buf[4] = buf[5] = buf[6] = 0; + buf[7] = type; + buf[8] = (checksum >> 24) & 0xff; + buf[9] = (checksum >> 16) & 0xff; + buf[10] = (checksum >> 8) & 0xff; + buf[11] = checksum & 0xff; + buf[12] = buf[13] = buf[14] = buf[15] = 0; + + i = fwrite(buf, 1, 16, output); + if(i < 0) { + perror(outfile); + exit(-1); + } else if(i < 16) { + fprintf(stderr, "short write on %s\n", outfile); + exit(-1); + } + /* leave output open here */ +} + +void write_data() { + int i; + FILE *f; + size_t got; + + for(i = 0; i < inputcount; i++) { + f = open_input(inputfiles[i]); + if(extracting) fread(buf, 1, 16, f); /* skip header */ + while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) { + if( (fwrite(buf, 1, got, output)) < got ) { + perror(outfile); + exit(-1); + } + } + if(ferror(f)) { + perror(inputfiles[i]); + exit(1); + } + fclose(f); + } + + /* if we made it here with no errors, the output file is good */ + keep_outfile = 1; +} + +void add_file(const char *filename) { + if(inputcount > MAX_INPUT_FILES) { + fprintf(stderr, "Too many input files (limit is %d, sorry)\n", + MAX_INPUT_FILES); + exit(1); + } + inputfiles[inputcount++] = filename; +} + +int extract4(const unsigned char *b) { + return ( (b[0] << 24) | + (b[1] << 16) | + (b[2] << 8) | + (b[3] ) ); +} + +void check_file(const char *filename) { + int j, hdr_checksum, hdr_type, hdr_unused, ok = 1; + FILE *f; + int got, size, hdr_size; + + f = open_input(filename); + got = fread(buf, 1, 16, f); + if(got < 0) { + perror(filename); + exit(1); + } else if(got < 16) { + fprintf(stderr, "%s is only %d bytes long, not a valid CART\n", + filename, (int)got); + exit(1); + } + + if(!has_cart_header(buf)) { + fprintf(stderr, "%s missing CART header\n", filename); + exit(1); + } + + printf("%s has CART header\n", filename); + + hdr_type = extract4(buf + 4); + hdr_checksum = extract4(buf + 8); + hdr_unused = extract4(buf + 12); + + if(hdr_type < 1 || hdr_type > CARTRIDGE_LAST_SUPPORTED) { + fprintf(stderr, "%s has invalid cart type %d (should be 1-%d)\n", + filename, hdr_type, CARTRIDGE_LAST_SUPPORTED); + exit(1); + } + + printf("%s is type %d: %s (%d bytes)\n", + filename, hdr_type, cart_types[hdr_type].name, cart_types[hdr_type].size); + + if(hdr_unused) { + fprintf(stderr, "warning: %s unused area in CART header is non-zero\n", filename); + } + + hdr_size = CARTRIDGE_kb[hdr_type] * 1024; + + while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) { + if(got < CARTBUFLEN) { + fprintf(stderr, "warning: %s data size not a multiple of %d bytes\n", + filename, CARTBUFLEN); + } + for(j = 0; j < got; j++) checksum += buf[j]; + size += got; + } + if(ferror(f)) { + perror(filename); + exit(1); + } + fclose(f); + + if(size != hdr_size) { + ok = 0; + fprintf(stderr, + "%s header says the data size should be %d bytes, but we read %d, ", + filename, hdr_size, size); + } + + if(size > hdr_size) { + fprintf(stderr, "junk at the end? downloaded in ASCII mode?\n"); + } else if(size < hdr_size) { + fprintf(stderr, "truncated?\n"); + } else { + printf("%s has correct data size, %d bytes\n", filename, size); + } + + if(hdr_checksum == checksum) { + printf("%s has valid checksum\n", filename); + } else { + ok = 0; + fprintf(stderr, "%s has BAD checksum\n", filename); + } + + printf("%s results: %s\n", filename, (ok ? "OK" : "FAILED")); + exit(!ok); +} + +void cleanup() { + if(outfile && !keep_outfile) unlink(outfile); /* ignore error here */ +} + +int main(int argc, char **argv) { + init(); + atexit(cleanup); + + if(argc < 2) { + usage(); + exit(0); + } + + while(++argv, --argc > 0) { + if(argv[0][0] == '-') { + switch(argv[0][1]) { + case 'l': + list_types(); + exit(0); + break; + + case 't': + type = atoi(&argv[0][2]); + if(type < 1 || type > CARTRIDGE_LAST_SUPPORTED) { + fprintf(stderr, "Invalid -t, use -t1 thru -t%d (not -t 1)\n\n", + CARTRIDGE_LAST_SUPPORTED); + usage(); + exit(1); + } + break; + + case 'o': + if(argv[0][2]) { + outfile = &argv[0][2]; + } else { + fprintf(stderr, "Invalid -o, use -ofilename (not -o filename)\n\n"); + exit(1); + } + break; + + case 'x': + if(argv[0][2]) { + outfile = &argv[0][2]; + extracting = 1; + } else { + fprintf(stderr, "Invalid -x, use -xfilename (not -x filename)\n\n"); + exit(1); + } + break; + + case 'c': + if(argv[0][2]) { + check_file(&argv[0][2]); /* exits */ + } else { + fprintf(stderr, "Invalid -c, use -cfilename (not -c filename)\n\n"); + exit(1); + } + break; + + case 'h': + case '?': + usage(); + exit(0); + break; + + default: + fprintf(stderr, "Invalid option %s\n\n", *argv); + usage(); + exit(1); + break; + } + } else { /* argv[0][0] != '-' */ + add_file(*argv); + } + } + + open_output(); + write_header(); + write_data(); + exit(0); +} |