OXYGENHUB
# ea9efff4e540 x16-emulator / src / makecart.c
15.5 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
03a3459e Andy McCall 1
#define _POSIX_C_SOURCE 200809L
2
3
#include "cartridge.h"
4
#include "files.h"
5
6
#include <stdlib.h>
7
#include <stdio.h>
8
#include <string.h>
9
#include <ctype.h>
10
11
extern uint8_t *CART;
12
13
uint8_t fill_value = 0;
14
15
static void parse_cmdline(int argc, char **argv);
16
17
static int x16_getline(char **line, size_t *n, FILE *f)
18
{
19
	if(line == NULL) {
20
		return -1;
21
	}
22
	if(n == NULL) {
23
		return -1;
24
	}
25
	if(f == NULL) {
26
		return -1;
27
	}
28
	if(*line == NULL) {
29
		*n = 64;
30
		*line = malloc(*n);
31
	} else if(*n == 0) {
32
		*n = 64;
33
		*line = realloc(*line, *n);
34
	}
35
36
	int i=EOF;
37
	int c;
38
	while((c = fgetc(f)) > EOF) {
39
		++i;
40
		if(i >= *n) {
41
			*n <<= 1;
42
			*line = realloc(*line, *n);
43
		}
44
		(*line)[i] = c;
45
		if(c == '\n') {
46
			break;
47
		}
48
	}
49
	if(c < 0 && i >= 0) {
50
		++i;
51
		if(i >= *n) {
52
			*n <<= 1;
53
			*line = realloc(*line, *n);
54
		}
55
		(*line)[i] = '\0';
56
	}
57
	if(i > 0) {
58
		if((*line)[i-1] == '\r') {
59
			(*line)[i-1] = '\0';
60
		} else {
61
			(*line)[i] = '\0';
62
		}
63
	} else if(i == 0) {
64
		(*line)[i] = '\0';
65
	}
66
	return i;
67
}
68
69
void
70
usage()
71
{
72
	printf("-help\n");
73
	printf("\tPrint this message and exit.\n");
74
	printf("\n");
75
	printf("-cfg <filename.cfg>\n");
76
	printf("\tUse this file to pack the cartridge data. Config file is simply the\n");
77
	printf("\tcommand line switches, one per line.\n");
78
	printf("\n");
79
	printf("-desc \"Name/Description\"\n");
80
	printf("\tSet the description field of the cartridge file. This is limited to 32\n");
81
	printf("\tbytes and padded with null characters ('\\0').\n");
82
	printf("\n");
83
	printf("-author \"Author Information\"\n");
84
	printf("\tSet the author field of the cartridge file. This is limited to 32 bytes\n");
85
	printf("\tand padded with null characters ('\\0').\n");
86
	printf("\n");
87
	printf("-copyright \"Copyright Information\"\n");
88
	printf("\tSet the copyright field of the cartridge file. This is limited to 32\n");
89
	printf("\tbytes and padded with null characters ('\\0').\n");
90
	printf("\n");
91
	printf("-version \"Version Information\"\n");
92
	printf("\tSet the version field of the cartridge file. This is limited to 32 bytes\n");
93
	printf("\tand padded with null characters ('\\0').\n");
94
	printf("\n");
95
	printf("-fill <value>\n");
96
	printf("\tSet the fill value to use with any partially-filled banks of cartridge\n");
97
	printf("\tmemory. Value can be defined in decimal, in hexadecimal with a '$' or\n");
98
	printf("\t'0x' prefix, in octal with a leading 0, or in binary with a leading 0b or %%.\n");
99
	printf("\n");
100
	printf("-rom_file <start_bank> [<filenames.bin>...]\n");
101
	printf("\tDefine rom banks from the specified list of files. File data is tightly\n");
102
	printf("\tpacked -- if a file does not end on a 16KB interval, the next file will be inserted\n");
103
	printf("\timmediately after it within the same bank. If the last file does not end on a 16KB\n");
104
	printf("\tinterval, the remainder of the rom will be filled with the value set by '-fill'.\n");
105
	printf("\n");
106
	printf("-ram <start_bank> [<end bank>]\n");
107
	printf("\tDefine one or more banks of uninitialized RAM.\n");
108
	printf("\n");
109
	printf("-ram_file <start_bank> [<filenames.bin>...]\n");
110
	printf("\tDefine pre-initialized ram banks from the specified list of files. File\n");
111
	printf("\tdata is tightly packed like with -rom. If the last file does not end on a 16KB\n");
112
	printf("\tinterval, the remainder of the rom will be filled with the value set by '-fill'.\n");
113
	printf("\n");
114
	printf("-nvram <start_bank> [<end_bank>]\n");
115
	printf("\tDefine one or more uninitialized nvram banks.\n");
116
	printf("\n");
117
	printf("-nvram_file <start_bank> [<filenames.bin>...]\n");
118
	printf("\tDefine pre-initialized nvram banks from the specified list of files. File\n");
119
	printf("\tdata is tightly packed like with -rom. If the last file does not end on a 16KB\n");
120
	printf("\tinterval, the remainder of the rom will be filled with the value set by '-fill'.\n");
121
	printf("\n");
122
	printf("-nvram_value <start_bank> <end_bank>\n");
123
	printf("\tDefine pre-initialized nvram banks with the value set by '-fill'.\n");
124
	printf("\n");
125
	printf("-none <start_bank> [<end_bank>]\n");
126
	printf("\tDefine one or more unpopulated banks of the cartridge. By default, all\n");
127
	printf("\tbanks are unpopulated unless specified by a previous command-line option.\n");
128
	printf("\n");
129
	printf("-o <output.crt>\n");
130
	printf("\tSet the filename of the output cartridge file.\n");
131
	printf("\n");
132
	printf("-unpack <input.crt> [<rom_size>]\n");
133
	printf("\tUnpacks the binary data from the cartridge file into <rom_size> slices.\n");
134
	printf("\t(for use with an EPROM programmer.) The ouput files will be the same\n");
135
	printf("\tfilename as the input file, with _### appended. This will also create a\n");
136
	printf("\t.cfg file that can be used to re-pack the files into a new CRT if needed.\n");
137
	printf("\n");
138
	printf("All options can be specified multiple times, and are applied  in-order from\n");
139
	printf("\tleft to right.\n");
140
141
	exit(1);
142
}
143
144
static void
145
unpack_cart(const char *path, uint32_t bank_size)
146
{
147
	if(bank_size & 0x3fff) {
148
		printf("Warning: rom_size specified to -unpack was not 16KB-aligned, the resulting config file will not be trustworthy.\n");
149
	}
150
151
	const char *extension = file_find_extension(path, NULL);
152
	size_t extlen = strlen(extension);
153
154
	size_t root_pathlen = strlen(path) - extlen;
155
	char *root_path = malloc(root_pathlen + 1);
156
	memcpy(root_path, path, root_pathlen);
157
	root_path[root_pathlen] = '\0';
158
159
	size_t outpath_len = root_pathlen + 8; // strlen("_000.bin");
160
	char *outpath = malloc(outpath_len + 1);
161
162
	size_t cfgpath_len = root_pathlen + 4; // strlen(".cfg");
163
	char *cfgpath = malloc(cfgpath_len + 1);
164
	sprintf(cfgpath, "%s.cfg", root_path);
165
166
	FILE *cfg = fopen(cfgpath, "wb");
167
	if(cfg == NULL) {
168
		printf("Error: Could not open config file for writing: %s\n", cfgpath);
169
	} 
170
171
	cartridge_new();
172
	if(cartridge_load(path, true)) {
173
		if(cfg != NULL) {
174
			char buffer[65];
175
176
			cartridge_get_desc(buffer, 65);
177
			fprintf(cfg, "-desc \"%s\"\n", buffer);
178
179
			cartridge_get_author(buffer, 65);
180
			fprintf(cfg, "-author \"%s\"\n", buffer);
181
182
			cartridge_get_copyright(buffer, 65);
183
			fprintf(cfg, "-copyright \"%s\"\n", buffer);
184
185
			cartridge_get_program_version(buffer, 65);
186
			fprintf(cfg, "-prg_version \"%s\"\n", buffer);
187
		}
188
		
189
		uint32_t bank_idx = 0;
190
		for(uint32_t offset = 0; offset < CART_MAX_SIZE; offset += bank_size) {
191
			sprintf(outpath, "%s_%03d.bin", root_path, bank_idx);
192
			++bank_idx;
193
194
			struct x16file *f = x16open(outpath, "wb");
195
			if(f == NULL) {
196
				printf("Error opening file for write: %s\n", outpath);
197
				continue;
198
			}
199
			size_t avail = CART_MAX_SIZE - offset;
200
			if(!x16write(f, CART + offset, bank_size > avail ? avail : bank_size, 1)) {
201
				printf("Error writing to file: %s\n", outpath);
202
			}
203
			x16close(f);
204
205
			uint32_t start_bank = offset >> 14;
206
			uint32_t end_bank = ((offset + bank_size) >> 14) - 1;
207
208
			uint8_t start_bank_type = cartridge_get_bank_type(start_bank + 32);
209
			bool conflicted = false;
210
			for(uint32_t bank = start_bank + 1; bank <= end_bank; ++bank) {
211
				uint8_t bank_type = cartridge_get_bank_type(bank + 32);
212
				if(bank_type != start_bank_type) {
213
					if(!conflicted) {
214
						printf("Warning: Discovered bank type mismatch while writing file: %s\n", outpath);
215
						printf("\tFile starts at offset $%08x, which is banks %d through %d\n", offset, start_bank, end_bank);
216
						conflicted = true;
217
					}
218
					if(start_bank == bank - 1) {
219
						printf("\tBank %d is type %d\n", start_bank + 32, start_bank_type);
220
					} else {
221
						printf("\tBanks %d through %d are type %d\n", start_bank + 32, bank + 32 - 1, start_bank_type);
222
					}
223
					start_bank_type = bank_type;
224
					start_bank = bank;
225
				}
226
			}
227
			if(conflicted) {
228
				if(start_bank == end_bank) {
229
					printf("\tBank %d is type %d\n", start_bank + 32, start_bank_type);
230
				} else {
231
					printf("\tBanks %d through %d are type %d\n", start_bank + 32, end_bank + 32, start_bank_type);
232
				}
233
				printf("\tThe resulting config file will not be trustworthy.");
234
			}
235
236
			if(cfg != NULL) {
237
				start_bank = offset >> 14;
238
				switch(cartridge_get_bank_type(offset >> 14)) {
239
					case CART_BANK_NONE: /* skip */ break;
240
					case CART_BANK_ROM: fprintf(cfg, "-rom_file %d \"%s\"\n", start_bank, outpath); break;
241
					case CART_BANK_UNINITIALIZED_RAM: fprintf(cfg, "-ram %d %d\n", start_bank, end_bank); break;
242
					case CART_BANK_INITIALIZED_RAM: fprintf(cfg, "-ram_file %d \"%s\"\n", start_bank, outpath); break;
243
					case CART_BANK_UNINITIALIZED_NVRAM: fprintf(cfg, "-nvram %d %d\n", start_bank, end_bank); break;
244
					case CART_BANK_INITIALIZED_NVRAM: fprintf(cfg, "-nvram_file %d \"%s\"\n", start_bank, outpath); break;
245
					default: /* error */ break;
246
				}
247
			}
248
		}
249
	}
250
251
	fclose(cfg);
252
253
	free(cfgpath);
254
	free(outpath);
255
	free(root_path);
256
}
257
258
static char *
259
next_token(char **token)
260
{
261
	if(**token == '\0' || **token == '\r' || **token == '\n') {
262
		return NULL;
263
	}
264
	char *start = *token;
265
	while(!isgraph(*start) && *start) {
266
		++start;
267
	}
268
269
	char *next = start;
270
	if(*next == '"') {
271
		++start;
272
		++next;
273
		while(*next != '"' && *next) {
274
			if(*next == '\\') {
275
				++next;
276
			}
277
			++next;
278
		}
279
	} else {
280
		while(isgraph(*next) && *next) {
281
			if(*next == '\\') {
282
				++next;
283
			}
284
			++next;
285
		}
286
	}
287
	if(*next) {
288
		*next = '\0';
289
		*token = next+1;
290
	} else {
291
		*token = next;
292
	}
293
	
294
	return start;
295
}
296
297
static void
298
parse_config(const char *path)
299
{
300
	FILE *cfg = fopen(path, "rb");
301
	if(cfg == NULL) {
302
		printf("Error opening file for read: %s", path);
303
		return;
304
	}
305
306
	size_t argslen = 32;
307
	char **argv = malloc(argslen * sizeof(char **));
308
309
	char *line = NULL;
310
	size_t n = 0;
311
	while(x16_getline(&line, &n, cfg) >= 0) {
312
		char *token = line;
313
		int argc = 0;
314
		char *cmd;
315
316
		while((cmd = next_token(&token))) {
317
			if(strlen(cmd) == 0) {
318
				continue;
319
			}
320
			if(argc >= argslen) {
321
				argslen <<= 1;
322
				argv = realloc(argv, argslen * sizeof(char **));
323
			}
324
			argv[argc] = cmd;
325
			++argc;
326
		}
327
328
		if(argc > 0) {
329
			parse_cmdline(argc, argv);
330
		}
331
	}
332
333
	free(line);
334
	free(argv);
335
}
336
337
static void 
338
parse_cmdline(int argc, char **argv)
339
{
340
	while(argc > 0) {
341
		if (!strcmp(argv[0], "-help")) {
342
			usage();
343
344
		} else if (!strcmp(argv[0], "-cfg")) {
345
			++argv;
346
			--argc;
347
348
			if (!argc || argv[0][0] == '-') {
349
				usage();
350
			}
351
352
			parse_config(argv[0]);
353
			++argv;
354
			--argc;
355
356
		} else if (!strcmp(argv[0], "-desc")) {
357
			++argv;
358
			--argc;
359
360
			if (!argc || argv[0][0] == '-') {
361
				usage();
362
			}
363
364
			cartridge_set_desc(argv[0]);
365
			++argv;
366
			--argc;
367
368
		} else if (!strcmp(argv[0], "-author")) {
369
			++argv;
370
			--argc;
371
372
			if (!argc || argv[0][0] == '-') {
373
				usage();
374
			}
375
376
			cartridge_set_author(argv[0]);
377
			++argv;
378
			--argc;
379
380
		} else if (!strcmp(argv[0], "-copyright")) {
381
			++argv;
382
			--argc;
383
384
			if (!argc || argv[0][0] == '-') {
385
				usage();
386
			}
387
388
			cartridge_set_copyright(argv[0]);
389
			++argv;
390
			--argc;
391
392
		} else if (!strcmp(argv[0], "-version")) {
393
			++argv;
394
			--argc;
395
396
			if (!argc || argv[0][0] == '-') {
397
				usage();
398
			}
399
400
			cartridge_set_program_version(argv[0]);
401
			++argv;
402
			--argc;
403
404
		} else if(!strcmp(argv[0], "-fill")) {
405
			++argv;
406
			--argc;
407
408
			if (!argc || argv[0][0] == '-') {
409
				usage();
410
			}
411
412
			if(argv[0][0] == '$') {
413
				fill_value = strtol(argv[0] + 1, NULL, 16);
414
			} else if(argv[0][0] == '%') {
415
				fill_value = strtol(argv[0] + 1, NULL, 2);
416
			} else if(argv[0][0] == '0') {
417
				if(argv[0][1] == 'x') {
418
					fill_value = strtol(argv[0] + 2, NULL, 16);
419
				} else if(argv[0][1] == 'b') {
420
					fill_value = strtol(argv[0] + 2, NULL, 2);
421
				} else {
422
					fill_value = strtol(argv[0] + 1, NULL, 8);
423
				}
424
			} else {
425
				fill_value = strtol(argv[0], NULL, 10);
426
			}
427
			++argv;
428
			--argc;
429
430
		} else if(!strcmp(argv[0], "-rom_file")) {
431
			++argv;
432
			--argc;
433
434
			if (!argc || argv[0][0] == '-') {
435
				usage();
436
			}
437
438
			uint8_t start_bank = atoi(argv[0]);
439
			++argv;
440
			--argc;
441
442
			if (!argc || argv[0][0] == '-') {
443
				usage();
444
			}
445
446
			char **files = &argv[0];
447
			int num_files = 1;
448
			while (argc && argv[0][0] != '-') {
449
				++argv;
450
				--argc;
451
			}
452
453
			cartridge_import_files(files, num_files, start_bank, CART_BANK_ROM, fill_value);
454
455
		} else if(!strcmp(argv[0], "-ram")) {
456
			++argv;
457
			--argc;
458
459
			if (!argc || argv[0][0] == '-') {
460
				usage();
461
			}
462
463
			uint8_t start_bank = atoi(argv[0]);
464
			++argv;
465
			--argc;
466
467
			if (!argc || argv[0][0] == '-') {
468
				cartridge_define_bank_range(start_bank, start_bank, CART_BANK_UNINITIALIZED_RAM);
469
				continue;
470
			}
471
472
			uint8_t end_bank = atoi(argv[0]);
473
			++argv;
474
			--argc;
475
476
			cartridge_define_bank_range(start_bank, end_bank, CART_BANK_UNINITIALIZED_RAM);
477
478
		} else if(!strcmp(argv[0], "-ram_file")) {
479
			++argv;
480
			--argc;
481
482
			if (!argc || argv[0][0] == '-') {
483
				usage();
484
			}
485
486
			uint8_t start_bank = atoi(argv[0]);
487
			++argv;
488
			--argc;
489
490
			if (!argc || argv[0][0] == '-') {
491
				usage();
492
			}
493
494
			char **files = &argv[0];
495
			int num_files = 1;
496
			while (argc && argv[0][0] != '-') {
497
				++argv;
498
				--argc;
499
			}
500
501
			cartridge_import_files(files, num_files, start_bank, CART_BANK_INITIALIZED_RAM, fill_value);
502
503
		} else if(!strcmp(argv[0], "-nvram")) {
504
			++argv;
505
			--argc;
506
507
			if (!argc || argv[0][0] == '-') {
508
				usage();
509
			}
510
511
			uint8_t start_bank = atoi(argv[0]);
512
			++argv;
513
			--argc;
514
515
			if (!argc || argv[0][0] == '-') {
516
				cartridge_define_bank_range(start_bank, start_bank, CART_BANK_UNINITIALIZED_NVRAM);
517
				continue;
518
			}
519
520
			uint8_t end_bank = atoi(argv[0]);
521
			++argv;
522
			--argc;
523
524
			cartridge_define_bank_range(start_bank, end_bank, CART_BANK_UNINITIALIZED_NVRAM);
525
526
		} else if(!strcmp(argv[0], "-nvram_file")) {
527
			++argv;
528
			--argc;
529
530
			if (!argc || argv[0][0] == '-') {
531
				usage();
532
			}
533
534
			uint8_t start_bank = atoi(argv[0]);
535
			++argv;
536
			--argc;
537
538
			if (!argc || argv[0][0] == '-') {
539
				usage();
540
			}
541
542
			char **files = &argv[0];
543
			int num_files = 1;
544
			while (argc && argv[0][0] != '-') {
545
				++argv;
546
				--argc;
547
			}
548
549
			cartridge_import_files(files, num_files, start_bank, CART_BANK_INITIALIZED_NVRAM, fill_value);
550
551
		} else if(!strcmp(argv[0], "-nvram_value")) {
552
			++argv;
553
			--argc;
554
555
			if (!argc || argv[0][0] == '-') {
556
				usage();
557
			}
558
559
			uint8_t start_bank = atoi(argv[0]);
560
			++argv;
561
			--argc;
562
563
			if (!argc || argv[0][0] == '-') {
564
				cartridge_fill(start_bank, start_bank, CART_BANK_INITIALIZED_NVRAM, fill_value);
565
				continue;
566
			}
567
568
			uint8_t end_bank = atoi(argv[0]);
569
			++argv;
570
			--argc;
571
572
			cartridge_fill(start_bank, end_bank, CART_BANK_INITIALIZED_NVRAM, fill_value);
573
574
		} else if(!strcmp(argv[0], "-none")) {
575
			++argv;
576
			--argc;
577
578
			if (!argc || argv[0][0] == '-') {
579
				usage();
580
			}
581
582
			uint8_t start_bank = atoi(argv[0]);
583
			++argv;
584
			--argc;
585
586
			if (!argc || argv[0][0] == '-') {
587
				cartridge_define_bank_range(start_bank, start_bank, CART_BANK_NONE);
588
				continue;
589
			}
590
591
			uint8_t end_bank = atoi(argv[0]);
592
			++argv;
593
			--argc;
594
595
			cartridge_define_bank_range(start_bank, end_bank, CART_BANK_NONE);
596
597
		} else if(!strcmp(argv[0], "-o")) {
598
			++argv;
599
			--argc;
600
601
			if (!argc || argv[0][0] == '-') {
602
				usage();
603
			}
604
605
			cartridge_save(argv[0]);
606
			++argv;
607
			--argc;
608
609
		} else if(!strcmp(argv[0], "-unpack")) {
610
			++argv;
611
			--argc;
612
613
			if (!argc || argv[0][0] == '-') {
614
				usage();
615
			}
616
617
			char *unpack_file = argv[0];
618
			++argv;
619
			--argc;
620
621
			if (!argc || argv[0][0] == '-') {
622
				unpack_cart(unpack_file, CART_BANK_SIZE);
623
				continue;
624
			}
625
626
			uint32_t unpack_size = atoi(argv[0]);
627
			++argv;
628
			--argc;
629
630
			unpack_cart(unpack_file, unpack_size);
631
632
		} else if(!strcmp(argv[0], "-testbins")) {
633
			++argv;
634
			--argc;
635
636
			char test_path[256];
637
			for(uint8_t b=0; b<CART_MAX_BANKS; ++b) {
638
				sprintf(test_path, "test-%03d.bin", b);
639
				struct x16file *f = x16open(test_path, "wb");
640
				if(f == NULL) {
641
					printf("Error opening file for write: %s\n", test_path);
642
					continue;
643
				}
644
				for(int i=0; i<CART_BANK_SIZE; ++i) {
645
					x16write(f, &b, 1, 1);
646
				}
647
				x16close(f);
648
			}
649
		} else {
650
			return usage();
651
		}
652
	}
653
}
654
655
int 
656
main(int argc, char **argv)
657
{
658
	argc--;
659
	argv++;
660
661
	cartridge_new();
662
	parse_cmdline(argc, argv);
663
664
	return 0;
665
}