aboutsummaryrefslogtreecommitdiff
path: root/mkcart.c
blob: 28ba5d58e0b94053099a37514e47703a51aea785 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
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);
}