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