gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/makebin/makebin.c
1 /*
2 makebin - turn a .ihx file into a binary image or GameBoy format binaryimage
3
4 Copyright (c) 2000 Michael Hope
5 Copyright (c) 2010 Borut Razem
6 Copyright (c) 2012 Noel Lemouel
7 Copyright (c) 2020-2021 Sebastian 'basxto' Riedel
8 Copyright (c) 2020 'bbbbbr'
9
10 This software is provided 'as-is', without any express or implied
11 warranty. In no event will the authors be held liable for any damages
12 arising from the use of this software.
13
14 Permission is granted to anyone to use this software for any purpose,
15 including commercial applications, and to alter it and redistribute it
16 freely, subject to the following restrictions:
17
18 1. The origin of this software must not be misrepresented; you must not
19 claim that you wrote the original software. If you use this software
20 in a product, an acknowledgment in the product documentation would be
21 appreciated but is not required.
22 2. Altered source versions must be plainly marked as such, and must not be
23 misrepresented as being the original software.
24 3. This notice may not be removed or altered from any source distribution.
25 */
26
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #if defined(_WIN32)
36 #include <fcntl.h>
37 #include <io.h>
38 #else
39 #include <unistd.h>
40 #endif
41
42
43 typedef unsigned char BYTE;
44
45 #define BANK_SIZE 16384
46 #define FILL_BYTE 0xff
47
48 int
49 getnibble (FILE *fin)
50 {
51 int ret;
52 int c = getc (fin);
53
54 if (feof (fin) || ferror (fin))
55 {
56 fprintf (stderr, "error: unexpected end of file.\n");
57 exit (6);
58 }
59
60 ret = c - '0';
61 if (ret > 9)
62 {
63 ret -= 'A' - '9' - 1;
64 }
65
66 if (ret > 0xf)
67 {
68 ret -= 'a' - 'A';
69 }
70
71 if (ret < 0 || ret > 0xf)
72 {
73 fprintf (stderr, "error: character %02x.\n", ret);
74 exit (7);
75 }
76 return ret;
77 }
78
79 int
80 getbyte (FILE *fin, int *sum)
81 {
82 int b = (getnibble (fin) << 4) | getnibble (fin);
83 *sum += b;
84 return b;
85 }
86
87 void
88 usage (void)
89 {
90 fprintf (stderr,
91 "makebin: convert a Intel IHX file to binary or GameBoy format binary.\n"
92 "Usage: makebin [options] [<in_file> [<out_file>]]\n"
93 "Options:\n"
94 " -p pack mode: the binary file size will be truncated to the last occupied byte\n"
95 " -s romsize size of the binary file (default: rom banks * 16384)\n"
96 " -Z generate GameBoy format binary file\n"
97 " -S generate Sega Master System format binary file\n"
98 " -N generate Famicom/NES format binary file\n"
99 " -o bytes skip amount of bytes in binary file\n"
100
101 "SMS format options (applicable only with -S option):\n"
102 " -xo n header rom size (0xa-0x2) (default: 0xc)\n"
103 " -xj n set region code (3-7) (default: 4)\n"
104 //" -xc n product code (0-159999)\n"
105 " -xv n version number (0-15) (default: 0)\n"
106 " -yo n number of rom banks (default: 2) (autosize: A)\n"
107 " -ya n number of ram banks (default: 0)\n"
108 //" -xV n SDSC version number\n"
109 //" -xd n SDSC date\n"
110 //" -xA n SDSC author pointer\n"
111 //" -xn n SDSC program name pointer\n"
112 //" -xD n SDSC description pointer\n"
113
114 "GameBoy format options (applicable only with -Z option):\n"
115 " -yo n number of rom banks (default: 2) (autosize: A)\n"
116 " -ya n number of ram banks (default: 0)\n"
117 " -yt n MBC type (default: no MBC)\n"
118 " -yl n old licensee code (default: 0x33)\n"
119 " -yk cc new licensee string (default: 00)\n"
120 " -yn name cartridge name (default: none)\n"
121 " -yc GameBoy Color compatible\n"
122 " -yC GameBoy Color only\n"
123 " -ys Super GameBoy\n"
124 " -yS Convert .noi file named like input file to .sym\n"
125 " -yj set non-Japanese region flag\n"
126 " -yN do not copy big N validation logo into ROM header\n"
127 " -yp addr=value Set address in ROM to given value (address 0x100-0x1FE)\n"
128 "Arguments:\n"
129 " <in_file> optional IHX input file, '-' means stdin. (default: stdin)\n"
130 " <out_file> optional output file, '-' means stdout. (default: stdout)\n");
131 }
132
133 #define CART_NAME_LEN 16
134
135 struct gb_opt_s
136 {
137 char cart_name[CART_NAME_LEN]; /* cartridge name buffer */
138 char licensee_str[2]; /* new licensee string */
139 BYTE mbc_type; /* MBC type (default: no MBC) */
140 short nb_rom_banks; /* Number of rom banks (default: 2) */
141 BYTE nb_ram_banks; /* Number of ram banks (default: 0) */
142 BYTE licensee_id; /* old licensee code */
143 BYTE is_gbc; /* 1 if GBC compatible, 2 if GBC only, false for all other*/
144 BYTE is_sgb; /* True if SGB, false for all other*/
145 BYTE sym_conversion; /* True if .noi file should be converted to .sym (default false)*/
146 BYTE non_jp; /* True if non-Japanese region, false for all other*/
147 BYTE rom_banks_autosize; /* True if rom banks should be auto-sized (default false)*/
148 bool do_logo_copy; /* True if the nintendo logo should be copied into the ROM (default true) */
149 BYTE address_overwrite[16]; /* For limited compatibility with very old versions */
150 };
151
152 struct sms_opt_s
153 {
154 uint8_t rom_size; /* Doesn't have to be the real size, needed for checksum */
155 uint8_t region_code; /* Region code Japan/Export/International and SMS/GG */
156 uint8_t version; /* Game version */
157 uint8_t nb_ram_banks; /* Number of ram banks (default: 0) */
158 };
159
160 void
161 gb_postproc (BYTE * rom, int size, int *real_size, struct gb_opt_s *o)
162 {
163 int i, chk;
164 static const BYTE gb_logo[] =
165 {
166 0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b,
167 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
168 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
169 0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99,
170 0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
171 0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e
172 };
173
174 /* $0104-$0133: Nintendo logo
175 * If missing, an actual Game Boy won't run the ROM.
176 */
177
178 if (o->do_logo_copy)
179 {
180 memcpy (&rom[0x104], gb_logo, sizeof (gb_logo));
181 }
182
183 rom[0x144] = o->licensee_str[0];
184 rom[0x145] = o->licensee_str[1];
185
186 /*
187 * 0134-0142: Title of the game in UPPER CASE ASCII. If it
188 * is less than 16 characters then the
189 * remaining bytes are filled with 00's.
190 */
191
192 /* capitalize cartridge name */
193 for (i = 0; i < CART_NAME_LEN; ++i)
194 {
195 rom[0x134 + i] = toupper (o->cart_name[i]);
196 }
197
198 if (o->is_gbc == 1)
199 {
200 rom[0x143] = 0x80;
201 }
202
203 if (o->is_gbc == 2)
204 {
205 rom[0x143] = 0xC0;
206 }
207
208 if (o->is_sgb)
209 {
210 rom[0x146] = 0x03;
211 }
212
213 /*
214 0147: Cartridge type:
215
216 | Hex Code | MBC Type | SRAM | Battery | RTC | Extra Feature | Max ROM Size (1)|Max SRAM Size |
217 | -------- | ------------- | ---- | ------- | --- | --------------- | --------------- |--------------- |
218 | 0x00 | ROM ONLY | | | | | 32 K |0 |
219 | 0x01 | MBC-1 (2) | | | | | 2 MB |0 |
220 | 0x02 | MBC-1 (2) | SRAM | | | | 2 MB |32 K (5) |
221 | 0x03 | MBC-1 (2) | SRAM | BATTERY | | | 2 MB |32 K (5) |
222 | 0x05 | MBC-2 | | | | | 256 K |512 x 4 bits (6)|
223 | 0x06 | MBC-2 | | BATTERY | | | 256 K |512 x 4 bits (6)|
224 | 0x08 | ROM (3) | SRAM | | | | 32 K |8 K |
225 | 0x09 | ROM (3) | SRAM | BATTERY | | | 32 K |8 K |
226 | 0x0B | MMM01 | | | | | 8 MB / N | |
227 | 0x0C | MMM01 | SRAM | | | | 8 MB / N |128K / N |
228 | 0x0D | MMM01 | SRAM | BATTERY | | | 8 MB / N |128K / N |
229 | 0x0F | MBC-3 | | BATTERY | RTC | | 2 MB | |
230 | 0x10 | MBC-3 (4) | SRAM | BATTERY | RTC | | 2 MB |32 K |
231 | 0x11 | MBC-3 | | | | | 2 MB | |
232 | 0x12 | MBC-3 (4) | SRAM | | | | 2 MB |32 K |
233 | 0x13 | MBC-3 (4) | SRAM | BATTERY | | | 2 MB |32 K |
234 | 0x19 | MBC-5 | | | | | 8 MB | |
235 | 0x1A | MBC-5 | SRAM | | | | 8 MB |128 K |
236 | 0x1B | MBC-5 | SRAM | BATTERY | | | 8 MB |128 K |
237 | 0x1C | MBC-5 | | | | RUMBLE | 8 MB | |
238 | 0x1D | MBC-5 | SRAM | | | RUMBLE | 8 MB |128 K |
239 | 0x1E | MBC-5 | SRAM | BATTERY | | RUMBLE | 8 MB |128 K |
240 | 0x20 | MBC-6 | | | | | ~2MB | |
241 | 0x22 | MBC-7 |EEPROM| | | ACCELEROMETER | 2MB |256 byte EEPROM |
242 | 0xFC | POCKET CAMERA | | | | | 1MB |128KB RAM |
243 | 0xFD | BANDAI TAMA5 | | | | | To Do |To Do |
244 | 0xFE | HuC3 | | | RTC | | To Do |To Do |
245 | 0xFF | HuC1 | SRAM | BATTERY | | IR | To Do |To Do |
246 */
247 rom[0x147] = o->mbc_type;
248
249 /*
250 * 0148 ROM size:
251 * 0 - 256Kbit = 32KByte = 2 banks
252 * 1 - 512Kbit = 64KByte = 4 banks
253 * 2 - 1Mbit = 128KByte = 8 banks
254 * 3 - 2Mbit = 256KByte = 16 banks
255 * 4 - 4Mbit = 512KByte = 32 banks
256 * 5 - 8Mbit = 1MByte = 64 banks
257 * 6 - 16Mbit = 2MByte = 128 banks
258 * $52 - 9Mbit = 1.1MByte = 72 banks
259 * $53 - 10Mbit = 1.2MByte = 80 banks
260 * $54 - 12Mbit = 1.5MByte = 96 banks
261 */
262 switch (o->nb_rom_banks)
263 {
264 case 2:
265 rom[0x148] = 0;
266 break;
267
268 case 4:
269 rom[0x148] = 1;
270 break;
271
272 case 8:
273 rom[0x148] = 2;
274 break;
275
276 case 16:
277 rom[0x148] = 3;
278 break;
279
280 case 32:
281 rom[0x148] = 4;
282 break;
283
284 case 64:
285 rom[0x148] = 5;
286 break;
287
288 case 128:
289 rom[0x148] = 6;
290 break;
291
292 case 256:
293 rom[0x148] = 7;
294 break;
295
296 case 512:
297 rom[0x148] = 8;
298 break;
299
300 default:
301 fprintf (stderr, "warning: unsupported number of ROM banks (%d)\n", o->nb_rom_banks);
302 rom[0x148] = 0;
303 break;
304 }
305
306 /*
307 * 0149 RAM size:
308 * 0 - None
309 * 1 - 16kBit = 2kB = 1 bank
310 * 2 - 64kBit = 8kB = 1 bank
311 * 3 - 256kBit = 32kB = 4 banks
312 * 4 - 1MBit =128kB =16 banks
313 */
314 switch (o->nb_ram_banks)
315 {
316 case 0:
317 rom[0x149] = 0;
318 break;
319
320 case 1:
321 rom[0x149] = 2;
322 break;
323
324 case 4:
325 rom[0x149] = 3;
326 break;
327
328 case 16:
329 rom[0x149] = 4;
330 break;
331
332 default:
333 fprintf (stderr, "warning: unsupported number of RAM banks (%d)\n", o->nb_ram_banks);
334 rom[0x149] = 0;
335 break;
336 }
337
338 rom[0x14A] = o->non_jp;
339
340 rom[0x14B] = o->licensee_id;
341
342 for (i = 0; i < 16; i+=2)
343 {
344 if(o->address_overwrite[i] != 0xFF)
345 {
346 rom[0x0100 | o->address_overwrite[i]] = o->address_overwrite[i+1];
347 // warnings for builds ported from ancient GBDK
348 fprintf (stderr, "caution: -yp0x01%02x=0x%02x is outdated", o->address_overwrite[i], o->address_overwrite[i+1]);
349 if(o->address_overwrite[i] == 0x43)
350 switch(o->address_overwrite[i+1]&0xC0)
351 {
352 case 0x80:
353 fprintf (stderr, ", please use -yc instead");
354 break;
355 case 0xC0:
356 fprintf (stderr, ", please use -yC instead");
357 break;
358 default:
359 o->address_overwrite[i] = 0xFF;
360 }
361 if(o->address_overwrite[i] == 0x44 || o->address_overwrite[i] == 0x45)
362 fprintf (stderr, ", please use -yk cc instead");
363 if(o->address_overwrite[i] == 0x46)
364 if(o->address_overwrite[i+1] == 0x03)
365 fprintf (stderr, ", please use -ys instead");
366 else
367 o->address_overwrite[i] = 0xFF;
368 if(o->address_overwrite[i] == 0x47)
369 fprintf (stderr, ", please use -yt 0x%02x instead", o->address_overwrite[i+1]);
370 if(o->address_overwrite[i] == 0x4A)
371 fprintf (stderr, ", please use -yl 0x%02x instead", o->address_overwrite[i+1]);
372 if(o->address_overwrite[i] == 0x4B && o->address_overwrite[i+1] == 1)
373 fprintf (stderr, ", please use -yj instead");
374 if(o->address_overwrite[i] == 0xFF)
375 fprintf (stderr, ", this setting is the default");
376 fprintf (stderr, ".\n");
377 }
378 }
379
380 /* Update complement checksum */
381 chk = 0;
382 for (i = 0x134; i < 0x14d; ++i)
383 chk += rom[i];
384 rom[0x014d] = (unsigned char) (0xe7 - (chk & 0xff));
385
386 /* Update checksum */
387 chk = 0;
388 rom[0x14e] = 0;
389 rom[0x14f] = 0;
390 for (i = 0; i < size; ++i)
391 chk += rom[i];
392 rom[0x14e] = (unsigned char) ((chk >> 8) & 0xff);
393 rom[0x14f] = (unsigned char) (chk & 0xff);
394
395 if (*real_size < 0x150)
396 *real_size = 0x150;
397 }
398
399
400 void
401 sms_preproc (struct gb_opt_s * gb_opt, struct sms_opt_s * sms_opt, int *size) {
402
403 // If auto-banking is enabled, and RAM banks specified, auto-fix up min ROM size for mapper support if needed
404 //
405 // Emulators use ROM file size (var:size) (64K or greater) to determine whether a mapper is present, and RAM banks only work if a mapper is present.
406 // This is separate from the ROM size indicated in the header (var:o->rom_size) which reportedly is _not_ used for that detection.
407 if (gb_opt->rom_banks_autosize && (sms_opt->nb_ram_banks > 0) && (*size < 0x10000)) {
408 *size = 0x10000; // force size to 64K
409 }
410 }
411
412 void
413 sms_postproc (BYTE * rom, int size, int *real_size, struct sms_opt_s *o)
414 {
415 // based on https://www.smspower.org/Development/ROMHeader
416 // 0x1ff0 and 0x3ff0 are also possible, but never used
417 static const char tmr_sega[] = "TMR SEGA ";
418 short header_base = 0x7ff0;
419 int chk = 0;
420 unsigned long i;
421
422 // Emulators use ROM file size (var:size) (64K or greater) to determine whether a mapper is present, and RAM banks only work if a mapper is present.
423 // So warn if that criteria is not met.
424 // This is separate from the ROM size indicated in the header (var:o->rom_size) which reportedly is _not_ used for that detection.
425 if ((o->nb_ram_banks > 0) && (size < 0x10000)) {
426 fprintf (stderr, "\nWARNING: SMS/GG ROM size (%d) must be at least 64K to enable mapper support for RAM banks (%d specified) in emulators."
427 "\n \"-yo 4\" for makebin (or \"-Wm-yo4\" for LCC) can be used to set the size to 64K\n\n", size, o->nb_ram_banks);
428 }
429
430 // choose earlier positions for smaller roms
431 if (header_base > size)
432 header_base = 0x3ff0;
433 if (header_base > size)
434 header_base = 0x1ff0;
435
436 memcpy (&rom[header_base], tmr_sega, sizeof (tmr_sega) - 1);
437 // configure amounts of bytes to check
438 switch(o->rom_size)
439 {
440 case 0xa:
441 default:
442 i = 0x1FEF;
443 break;
444 case 0xb:
445 i = 0x3FEF;
446 break;
447 case 0xc:
448 i = 0x7FEF;
449 break;
450 case 0xd:
451 i = 0xBFEF;
452 break;
453 case 0xe:
454 i = 0xFFFF;
455 break;
456 case 0xf:
457 i = 0x1FFFF;
458 break;
459 case 0x0:
460 i = 0x3FFFF;
461 break;
462 case 0x1:
463 i = 0x7FFFF;
464 break;
465 case 0x2:
466 i = 0xFFFFF;
467 break;
468 }
469 // calculate checksum
470 for(;i > 0; --i)
471 {
472 chk += rom[i];
473 // 0x7FF0 - 0x7FFF is skipped
474 if(i == 0x8000)
475 i = 0x7FF0;
476 }
477 // we skipped index 0
478 chk += rom[0];
479 // little endian
480 rom[header_base + 0xa] = chk & 0xff;
481 rom[header_base + 0xb] = (chk>>8) & 0xff;
482 // game version
483 rom[header_base + 0xe] &= 0xF0;
484 rom[header_base + 0xe] |= o->version;
485 // rom size
486 rom[header_base + 0xf] = (o->region_code << 4) | o->rom_size;
487 }
488
489 int
490 rom_autosize_grow(BYTE **rom, int test_size, int *size, struct gb_opt_s *o)
491 {
492 int last_size = *size;
493
494 while ((test_size > *size) && (o->nb_rom_banks <= 512))
495 {
496 o->nb_rom_banks *= 2;
497 // banks work differently for mbc6, they have half the size
498 // but this in general ignored by -yo
499 *size = o->nb_rom_banks * 0x4000;
500 }
501
502 if (o->nb_rom_banks > 512)
503 {
504 fprintf (stderr, "error: auto-size banks exceeded max of 512 banks.\n");
505 return 0;
506 }
507 else
508 {
509 BYTE * t_rom = *rom;
510 *rom = realloc (*rom, *size);
511 if (*rom == NULL)
512 {
513 free(t_rom);
514 fprintf (stderr, "error: couldn't re-allocate size for larger rom image.\n");
515 return 0;
516 }
517 memset (*rom + last_size, FILL_BYTE, *size - last_size);
518 }
519
520 return 1;
521 }
522
523 int
524 noi2sym (char *filename)
525 {
526 FILE *noi, *sym;
527 char *nname, *sname;
528 //ssize_t read;
529 int read = ' ';
530 // no$gmb's implementation is limited to 32 character labels
531 // we can safely throw away the rest
532 #define SYM_FILE_NAME_LEN_MAX 32
533 char label[SYM_FILE_NAME_LEN_MAX + 1];
534 // 0x + 6 digit hex number
535 // -> 65536 rom banks is the maximum homebrew cartrideges support (TPP1)
536 char value[9];
537 int name_len = strlen(filename);
538 int i = 0;
539 // copy filename's value to nname and sname
540 nname = malloc((name_len+1) * sizeof(char));
541 strcpy (nname, filename);
542 sname = malloc((name_len+1) * sizeof(char));
543 strcpy (sname, filename);
544 // change the extensions
545 nname[name_len-1]='i';
546 nname[name_len-2]='o';
547 nname[name_len-3]='n';
548 sname[name_len-1]='m';
549 sname[name_len-2]='y';
550 sname[name_len-3]='s';
551
552 if (NULL == (noi = fopen (nname, "r")))
553 {
554 fprintf (stderr, "error: can't open %s: ", nname);
555 perror(NULL);
556 return 1;
557 }
558 if (NULL == (sym = fopen (sname, "w")))
559 {
560 fprintf (stderr, "error: can't create %s: ", sname);
561 perror(NULL);
562 return 1;
563 }
564 // write header
565 fprintf (sym, "; no$gmb compatible .sym file\n; Generated automagically by makebin\n");
566 // iterate through .noi file
567 while (read != EOF && (read = fgetc(noi)) != EOF)
568 {
569 // just skip line breaks
570 if (read == '\r' || read == '\n')
571 continue;
572 // read first 4 chars
573 for (i = 0; i < 4; ++i)
574 {
575 value[i] = read;
576 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n')
577 {
578 // leave for-loop
579 break;
580 }
581 }
582 // we left loop early
583 if (i != 4)
584 continue;
585 // only accept if line starts with this
586 if (strncmp(value, "DEF ", 4) == 0)
587 {
588 // read label
589 for (i = 0; i < (SYM_FILE_NAME_LEN_MAX - 1); ++i)
590 {
591 label[i] = read;
592 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n' || read == ' ')
593 {
594 // leave for-loop
595 break;
596 }
597 }
598 // skip rest of the label
599 while (read != EOF && read != '\r' && read != '\n' && read != ' ')
600 read = fgetc(noi);
601 // it has to be end of file or line if it's not space
602 if (read != ' ')
603 continue;
604 // strings have to end with \0
605 label[i+1] = '\0';
606 // read value
607 for (i = 0; i < 8; ++i)
608 {
609 value[i] = read;
610 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n')
611 {
612 // leave for-loop
613 break;
614 }
615 }
616 // number is too long; ignore
617 if (read != EOF && read != '\r' && read != '\n')
618 continue;
619 value[i+1] = '\0';
620 // we successfully read label and value
621
622 // but filter out some invalid symbols
623 if (strcmp(label, ".__.ABS.") != 0)
624 fprintf (sym, "%02X:%04X %s\n", (unsigned int)(strtoul(value, NULL, 0)>>16), (unsigned int)strtoul(value, NULL, 0)&0xFFFF, label);
625 }
626 else
627 // skip until file/line end
628 while ((read = fgetc(noi))!= EOF && read != '\r' && read != '\n');
629 }
630
631 // free close files
632 fclose (noi);
633 fclose (sym);
634
635 fprintf (stderr, "Converted %s to %s.\n", nname, sname);
636 return 0;
637 }
638
639 int
640 read_ihx (FILE *fin, BYTE **rom, int *size, int *real_size, struct gb_opt_s *o)
641 {
642 int record_type;
643
644 int extaddr = 0;
645 do
646 {
647 int nbytes;
648 int addr;
649 int checksum, sum = 0;
650
651 if (getc (fin) != ':')
652 {
653 fprintf (stderr, "error: invalid IHX line.\n");
654 return 0;
655 }
656 nbytes = getbyte (fin, &sum);
657 addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
658 record_type = getbyte (fin, &sum);
659 if(record_type == 4)
660 {
661 extaddr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
662 extaddr <<= 16; // those are the upper 16 bits
663 checksum = getbyte (fin, &sum);
664 // move to the next record
665 if (0 != (sum & 0xff))
666 {
667 fprintf (stderr, "error: bad checksum: %02x.\n", checksum);
668 return 0;
669 }
670 while (isspace (sum = getc (fin))) /* skip all kind of spaces */
671 ;
672 ungetc (sum, fin);
673 if (getc (fin) != ':')
674 {
675 fprintf (stderr, "error: invalid IHX line.\n");
676 return 0;
677 }
678 // parse real data part
679 checksum = sum = 0;
680 nbytes = getbyte (fin, &sum);
681 // lower 16 bits
682 addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
683 record_type = getbyte (fin, &sum);
684 }
685 // add linear address extension
686 addr |= extaddr;
687 // TODO: warn for unreachable banks according to chosen MBC
688 if (record_type > 1)
689 {
690 fprintf (stderr, "error: unsupported record type: %02x.\n", record_type);
691 return 0;
692 }
693
694 if (nbytes > 0 && (addr + nbytes > *size || (addr < 0 && nbytes > 0)))
695 {
696 // If auto-size is enabled, grow rom bank size by power of 2 when needed
697 if (o->rom_banks_autosize)
698 {
699 if (rom_autosize_grow(rom, addr + nbytes, size, o) == 0)
700 return 0;
701 }
702 else
703 {
704 fprintf (stderr, "error: ROM is too large for number of banks specified\n");
705 return 0;
706 }
707 }
708 while (nbytes--)
709 {
710 if (addr < *size)
711 (*rom)[addr++] = getbyte (fin, &sum);
712 }
713
714 if (addr > *real_size)
715 *real_size = addr;
716
717 checksum = getbyte (fin, &sum);
718 if (0 != (sum & 0xff))
719 {
720 fprintf (stderr, "error: bad checksum: %02x.\n", checksum);
721 return 0;
722 }
723
724 while (isspace (sum = getc (fin))) /* skip all kind of spaces */
725 ;
726 ungetc (sum, fin);
727 }
728 while (1 != record_type); /* EOF record */
729
730 return 1;
731 }
732
733 int
734 main (int argc, char **argv)
735 {
736 int size = 32768, offset = 0, pack = 0, real_size = 0, i = 0;
737 char *token;
738 BYTE *rom;
739 FILE *fin, *fout;
740 char *filename = NULL;
741 int ret;
742 int gb = 0;
743 int sms = 0;
744 int nes = 0;
745
746 struct gb_opt_s gb_opt = {.cart_name="",
747 .licensee_str={'0', '0'},
748 .mbc_type=0,
749 .nb_rom_banks=2,
750 .nb_ram_banks=0,
751 .licensee_id=0x33,
752 .is_gbc=0,
753 .is_sgb=0,
754 .sym_conversion=0,
755 .non_jp=0,
756 .rom_banks_autosize=0,
757 .do_logo_copy=true,
758 .address_overwrite={0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0} };
759
760 // 32KiB, SMS Export, version 0 <- should work with most emulaters (<32K was never used, GG accepts SMS)
761 struct sms_opt_s sms_opt = {.rom_size=0xc,
762 .region_code=4,
763 .version=0,
764 .nb_ram_banks=0 };
765
766 #if defined(_WIN32)
767 setmode (fileno (stdout), O_BINARY);
768 #endif
769
770 while (*++argv && '-' == argv[0][0] && '\0' != argv[0][1])
771 {
772 switch (argv[0][1])
773 {
774 case 's':
775 if (!*++argv)
776 {
777 usage ();
778 return 1;
779 }
780 size = strtoul (*argv, NULL, 0);
781 break;
782
783 case 'o':
784 if (!*++argv)
785 {
786 usage ();
787 return 1;
788 }
789 offset = strtoul (*argv, NULL, 0);
790 break;
791
792 case 'h':
793 usage ();
794 return 0;
795
796 case 'p':
797 pack = 1;
798 break;
799
800 case 'Z':
801 /* generate GameBoy binary file */
802 gb = 1, sms = 0, nes = 0;
803 break;
804
805 case 'y':
806 /* GameBoy options:
807 * -yo Number of rom banks (default: 2)
808 * -ya Number of ram banks (default: 0)
809 * -yt MBC type (default: no MBC)
810 * -yn Name of program (default: name of output file)
811 */
812 switch (argv[0][2])
813 {
814 case 'o':
815 if (!*++argv)
816 {
817 usage ();
818 return 1;
819 }
820 // Use auto-size for rom banks if -yto size param is 'A'
821 if ((*argv)[0] == 'A' || (*argv)[0] == 'a')
822 gb_opt.rom_banks_autosize = 1;
823 else
824 {
825 gb_opt.nb_rom_banks = strtoul (*argv, NULL, 0);
826 size = gb_opt.nb_rom_banks * 0x4000;
827 }
828 break;
829
830 case 'a':
831 if (!++argv)
832 {
833 usage ();
834 return 1;
835 }
836 gb_opt.nb_ram_banks = strtoul (*argv, NULL, 0);
837 sms_opt.nb_ram_banks = strtoul (*argv, NULL, 0);
838 break;
839
840 case 't':
841 if (!*++argv)
842 {
843 usage ();
844 return 1;
845 }
846 gb_opt.mbc_type = strtoul (*argv, NULL, 0);
847 break;
848
849 case 'n':
850 if (!*++argv)
851 {
852 usage ();
853 return 1;
854 }
855 strncpy (gb_opt.cart_name, *argv, CART_NAME_LEN-1);
856 gb_opt.cart_name[CART_NAME_LEN-1] = '\0';
857 break;
858
859 case 'k':
860 if (!*++argv)
861 {
862 usage ();
863 return 1;
864 }
865 // we don't need \0
866 strncpy (gb_opt.licensee_str, *argv, 2);
867 break;
868
869 case 'l':
870 if (!*++argv)
871 {
872 usage ();
873 return 1;
874 }
875 gb_opt.licensee_id = strtoul (*argv, NULL, 0);
876 break;
877
878 case 'c':
879 gb_opt.is_gbc = 1;
880 break;
881
882 case 'C':
883 gb_opt.is_gbc = 2;
884 break;
885
886 case 'N':
887 gb_opt.do_logo_copy = false; // when switch is present, turn off logo copy
888 break;
889
890 case 's':
891 gb_opt.is_sgb = 1;
892 break;
893
894 case 'S':
895 gb_opt.sym_conversion = 1;
896 break;
897
898 case 'j':
899 gb_opt.non_jp = 1;
900 break;
901
902 // like -yp0x143=0x80
903 case 'p':
904 // remove "-yp"
905 *argv += 3;
906
907 // also support -yp 0x143=0x80
908 if (!(*argv)[0])
909 if (!*++argv)
910 {
911 usage ();
912 return 1;
913 }
914
915 // effectively split string into argv and token
916 strtok(*argv, "=");
917 token = strtok(NULL, "=");
918 for (i = 0; i < 16; i+=2)
919 {
920 if (gb_opt.address_overwrite[i] == 0xFF)
921 {
922 gb_opt.address_overwrite[i] = strtoul (*argv, NULL, 0);
923 gb_opt.address_overwrite[i+1] = strtoul (token, NULL, 0);
924 break;
925 }
926 }
927 break;
928
929 default:
930 usage ();
931 return 1;
932 }
933 break;
934
935 case 'S':
936 /* generate SMS binary file */
937 gb = 0, sms = 1, nes = 0;
938 break;
939
940 case 'N':
941 /* generate binary file for NES, with .ihx file banks rotated to turn first bank into last bank */
942 gb = 0, sms = 0, nes = 1;
943 break;
944
945 case 'x':
946
947 switch (argv[0][2])
948 {
949 case 'o':
950 if (!*++argv)
951 {
952 usage ();
953 return 1;
954 }
955 sms_opt.rom_size = strtoul (*argv, NULL, 0);
956 if ( sms_opt.rom_size > 2 && (sms_opt.rom_size < 0xa || sms_opt.rom_size > 0xf ) )
957 {
958 fprintf (stderr, "error: invalid rom size (0x%X)", sms_opt.rom_size);
959 perror(NULL);
960 return 1;
961 }
962 if ( sms_opt.rom_size == 0xd || sms_opt.rom_size == 0x2 )
963 {
964 fprintf (stderr, "warning: this rom size (0x%X) is bugged in some BIOSes\n", sms_opt.rom_size);
965 }
966 break;
967
968 case 'j':
969 if (!*++argv)
970 {
971 usage ();
972 return 1;
973 }
974 sms_opt.region_code = strtoul (*argv, NULL, 0);
975 if ( sms_opt.region_code < 3 && sms_opt.region_code > 7 )
976 {
977 fprintf (stderr, "error: invalid region code (0x%X)", sms_opt.region_code);
978 perror(NULL);
979 return 1;
980 }
981 break;
982
983 case 'v':
984 if (!*++argv)
985 {
986 usage ();
987 return 1;
988 }
989 sms_opt.version = strtoul (*argv, NULL, 0);
990 if ( sms_opt.version > 0xf )
991 {
992 fprintf (stderr, "error: invalid version (0x%X)", sms_opt.version);
993 perror(NULL);
994 return 1;
995 }
996 break;
997
998 default:
999 usage ();
1000 return 1;
1001 }
1002 break;
1003
1004 default:
1005 usage ();
1006 return 1;
1007 }
1008 }
1009
1010 fin = stdin;
1011 fout = stdout;
1012 if (*argv)
1013 {
1014 if ('-' != argv[0][0] || '\0' != argv[0][1])
1015 {
1016 if (NULL == (fin = fopen (*argv, "r")))
1017 {
1018 fprintf (stderr, "error: can't open %s: ", *argv);
1019 perror(NULL);
1020 return 1;
1021 }
1022 filename = *argv;
1023 }
1024 ++argv;
1025 }
1026
1027 if (NULL != argv[0] && NULL != argv[1])
1028 {
1029 usage ();
1030 return 1;
1031 }
1032
1033 // If auto-banking is enabled, and sms/gg has RAM banks specified, fix up min ROM size if needed
1034 if (sms) sms_preproc(&gb_opt, &sms_opt, &size);
1035
1036 rom = malloc (size);
1037 if (rom == NULL)
1038 {
1039 fclose (fin);
1040 fprintf (stderr, "error: couldn't allocate room for the image.\n");
1041 return 1;
1042 }
1043 memset (rom, FILL_BYTE, size);
1044
1045 if (gb_opt.sym_conversion == 1)
1046 {
1047 if (filename)
1048 noi2sym(filename);
1049 else
1050 {
1051 fprintf (stderr, "error: .noi to .sym conversion needs an input file.\n");
1052 }
1053 }
1054
1055 ret = read_ihx (fin, &rom, &size, &real_size, &gb_opt);
1056
1057 fclose (fin);
1058
1059 if (ret)
1060 {
1061 if (gb)
1062 gb_postproc (rom, size, &real_size, &gb_opt);
1063 else if (sms)
1064 sms_postproc (rom, size, &real_size, &sms_opt);
1065
1066 if (*argv)
1067 {
1068 if ('-' != argv[0][0] || '\0' != argv[0][1])
1069 {
1070 if (NULL == (fout = fopen (*argv, "wb")))
1071 {
1072 fprintf (stderr, "error: can't create %s: ", *argv);
1073 perror(NULL);
1074 return 1;
1075 }
1076 }
1077 }
1078 // skip offset
1079 if (offset > 0)
1080 {
1081 memmove (rom, rom + offset, size - offset);
1082 memset (rom + size - offset, FILL_BYTE, offset);
1083 }
1084
1085 if (nes)
1086 {
1087 // .ihx file has fixed bank incorrectly placed as first - we fix this when writing out the .bin file.
1088 // Write the N-1 switchable banks at .nes file start, skipping the first (fixed) bank
1089 fwrite (rom + BANK_SIZE, 1, (pack ? real_size : size) - offset - BANK_SIZE, fout);
1090 // Write the fixed bank to end of .nes file
1091 fwrite (rom, 1, BANK_SIZE, fout);
1092 }
1093 else
1094 {
1095 fwrite (rom, 1, (pack ? real_size : size) - offset, fout);
1096 }
1097
1098 fclose (fout);
1099
1100 return 0;
1101 }
1102 else
1103 return 1;
1104 }
1105
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.