struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / support / makebin / makebin.c
blob396c0e6c5301f7f68fd1a6a7063e183a441de189
1 /*
2 makebin - turn a .ihx file into a binary image or GameBoy format binaryimage
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'
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.
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:
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.
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <ctype.h>
34 #if defined(_WIN32)
35 #include <fcntl.h>
36 #include <io.h>
37 #else
38 #include <unistd.h>
39 #endif
42 typedef unsigned char BYTE;
44 #define FILL_BYTE 0xff
46 int
47 getnibble (FILE *fin)
49 int ret;
50 int c = getc (fin);
52 if (feof (fin) || ferror (fin))
54 fprintf (stderr, "error: unexpected end of file.\n");
55 exit (6);
58 ret = c - '0';
59 if (ret > 9)
61 ret -= 'A' - '9' - 1;
64 if (ret > 0xf)
66 ret -= 'a' - 'A';
69 if (ret < 0 || ret > 0xf)
71 fprintf (stderr, "error: character %02x.\n", ret);
72 exit (7);
74 return ret;
77 int
78 getbyte (FILE *fin, int *sum)
80 int b = (getnibble (fin) << 4) | getnibble (fin);
81 *sum += b;
82 return b;
85 void
86 usage (void)
88 fprintf (stderr,
89 "makebin: convert a Intel IHX file to binary or GameBoy format binary.\n"
90 "Usage: makebin [options] [<in_file> [<out_file>]]\n"
91 "Options:\n"
92 " -p pack mode: the binary file size will be truncated to the last occupied byte\n"
93 " -s romsize size of the binary file (default: rom banks * 16384)\n"
94 " -Z generate GameBoy format binary file\n"
95 " -S generate Sega Master System format binary file\n"
96 " -o bytes skip amount of bytes in binary file\n"
98 "SMS format options (applicable only with -S option):\n"
99 " -xo n rom size (0xa-0x2) (default: 0xc)\n"
100 " -xj n set region code (3-7) (default: 4)\n"
101 //" -xc n product code (0-159999)\n"
102 " -xv n version number (0-15) (default: 0)\n"
103 //" -xV n SDSC version number\n"
104 //" -xd n SDSC date\n"
105 //" -xA n SDSC author pointer\n"
106 //" -xn n SDSC program name pointer\n"
107 //" -xD n SDSC description pointer\n"
109 "GameBoy format options (applicable only with -Z option):\n"
110 " -yo n number of rom banks (default: 2) (autosize: A)\n"
111 " -ya n number of ram banks (default: 0)\n"
112 " -yt n MBC type (default: no MBC)\n"
113 " -yl n old licensee code (default: 0x33)\n"
114 " -yk cc new licensee string (default: 00)\n"
115 " -yn name cartridge name (default: none)\n"
116 " -yc GameBoy Color compatible\n"
117 " -yC GameBoy Color only\n"
118 " -ys Super GameBoy\n"
119 " -yS Convert .noi file named like input file to .sym\n"
120 " -yj set non-Japanese region flag\n"
121 " -yN do not copy big N validation logo into ROM header\n"
122 " -yp addr=value Set address in ROM to given value (address 0x100-0x1FE)\n"
123 "Arguments:\n"
124 " <in_file> optional IHX input file, '-' means stdin. (default: stdin)\n"
125 " <out_file> optional output file, '-' means stdout. (default: stdout)\n");
128 #define CART_NAME_LEN 16
130 struct gb_opt_s
132 char cart_name[CART_NAME_LEN]; /* cartridge name buffer */
133 char licensee_str[3]; /* new licensee string */
134 BYTE mbc_type; /* MBC type (default: no MBC) */
135 short nb_rom_banks; /* Number of rom banks (default: 2) */
136 BYTE nb_ram_banks; /* Number of ram banks (default: 0) */
137 BYTE licensee_id; /* old licensee code */
138 BYTE is_gbc; /* 1 if GBC compatible, 2 if GBC only, false for all other*/
139 BYTE is_sgb; /* True if SGB, false for all other*/
140 BYTE sym_conversion; /* True if .noi file should be converted to .sym (default false)*/
141 BYTE non_jp; /* True if non-Japanese region, false for all other*/
142 BYTE rom_banks_autosize; /* True if rom banks should be auto-sized (default false)*/
143 bool do_logo_copy; /* True if the nintendo logo should be copied into the ROM (default true) */
144 BYTE address_overwrite[16]; /* For limited compatibility with very old versions */
147 struct sms_opt_s
149 BYTE rom_size; /* Doesn't have to be the real size, needed for checksum */
150 BYTE region_code; /* Region code Japan/Export/International and SMS/GG */
151 BYTE version; /* Game version */
154 void
155 gb_postproc (BYTE * rom, int size, int *real_size, struct gb_opt_s *o)
157 int i, chk;
158 static const BYTE gb_logo[] =
160 0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b,
161 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
162 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
163 0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99,
164 0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
165 0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e
168 /* $0104-$0133: Nintendo logo
169 * If missing, an actual Game Boy won't run the ROM.
172 if (o->do_logo_copy)
174 memcpy (&rom[0x104], gb_logo, sizeof (gb_logo));
177 rom[0x144] = o->licensee_str[0];
178 rom[0x145] = o->licensee_str[1];
181 * 0134-0142: Title of the game in UPPER CASE ASCII. If it
182 * is less than 16 characters then the
183 * remaining bytes are filled with 00's.
186 /* capitalize cartridge name */
187 for (i = 0; i < CART_NAME_LEN; ++i)
189 rom[0x134 + i] = toupper (o->cart_name[i]);
192 if (o->is_gbc == 1)
194 rom[0x143] = 0x80;
197 if (o->is_gbc == 2)
199 rom[0x143] = 0xC0;
202 if (o->is_sgb)
204 rom[0x146] = 0x03;
208 * 0147: Cartridge type:
209 * 0-ROM ONLY 12-ROM+MBC3+RAM
210 * 1-ROM+MBC1 13-ROM+MBC3+RAM+BATT
211 * 2-ROM+MBC1+RAM 19-ROM+MBC5
212 * 3-ROM+MBC1+RAM+BATT 1A-ROM+MBC5+RAM
213 * 5-ROM+MBC2 1B-ROM+MBC5+RAM+BATT
214 * 6-ROM+MBC2+BATTERY 1C-ROM+MBC5+RUMBLE
215 * 8-ROM+RAM 1D-ROM+MBC5+RUMBLE+SRAM
216 * 9-ROM+RAM+BATTERY 1E-ROM+MBC5+RUMBLE+SRAM+BATT
217 * B-ROM+MMM01 1F-Pocket Camera
218 * C-ROM+MMM01+SRAM FD-Bandai TAMA5
219 * D-ROM+MMM01+SRAM+BATT FE - Hudson HuC-3
220 * F-ROM+MBC3+TIMER+BATT FF - Hudson HuC-1
221 * 10-ROM+MBC3+TIMER+RAM+BATT
222 * 11-ROM+MBC3
224 rom[0x147] = o->mbc_type;
227 * 0148 ROM size:
228 * 0 - 256Kbit = 32KByte = 2 banks
229 * 1 - 512Kbit = 64KByte = 4 banks
230 * 2 - 1Mbit = 128KByte = 8 banks
231 * 3 - 2Mbit = 256KByte = 16 banks
232 * 4 - 4Mbit = 512KByte = 32 banks
233 * 5 - 8Mbit = 1MByte = 64 banks
234 * 6 - 16Mbit = 2MByte = 128 banks
235 * $52 - 9Mbit = 1.1MByte = 72 banks
236 * $53 - 10Mbit = 1.2MByte = 80 banks
237 * $54 - 12Mbit = 1.5MByte = 96 banks
239 switch (o->nb_rom_banks)
241 case 2:
242 rom[0x148] = 0;
243 break;
245 case 4:
246 rom[0x148] = 1;
247 break;
249 case 8:
250 rom[0x148] = 2;
251 break;
253 case 16:
254 rom[0x148] = 3;
255 break;
257 case 32:
258 rom[0x148] = 4;
259 break;
261 case 64:
262 rom[0x148] = 5;
263 break;
265 case 128:
266 rom[0x148] = 6;
267 break;
269 case 256:
270 rom[0x148] = 7;
271 break;
273 case 512:
274 rom[0x148] = 8;
275 break;
277 default:
278 fprintf (stderr, "warning: unsupported number of ROM banks (%d)\n", o->nb_rom_banks);
279 rom[0x148] = 0;
280 break;
284 * 0149 RAM size:
285 * 0 - None
286 * 1 - 16kBit = 2kB = 1 bank
287 * 2 - 64kBit = 8kB = 1 bank
288 * 3 - 256kBit = 32kB = 4 banks
289 * 4 - 1MBit =128kB =16 banks
291 switch (o->nb_ram_banks)
293 case 0:
294 rom[0x149] = 0;
295 break;
297 case 1:
298 rom[0x149] = 2;
299 break;
301 case 4:
302 rom[0x149] = 3;
303 break;
305 case 16:
306 rom[0x149] = 4;
307 break;
309 default:
310 fprintf (stderr, "warning: unsupported number of RAM banks (%d)\n", o->nb_ram_banks);
311 rom[0x149] = 0;
312 break;
315 rom[0x14A] = o->non_jp;
317 rom[0x14B] = o->licensee_id;
319 for (i = 0; i < 16; i+=2)
321 if(o->address_overwrite[i] != 0xFF)
323 rom[0x0100 | o->address_overwrite[i]] = o->address_overwrite[i+1];
324 // warnings for builds ported from ancient GBDK
325 fprintf (stderr, "caution: -yp0x01%02x=0x%02x is outdated", o->address_overwrite[i], o->address_overwrite[i+1]);
326 if(o->address_overwrite[i] == 0x43)
327 switch(o->address_overwrite[i+1]&0xC0)
329 case 0x80:
330 fprintf (stderr, ", please use -yc instead");
331 break;
332 case 0xC0:
333 fprintf (stderr, ", please use -yC instead");
334 break;
335 default:
336 o->address_overwrite[i] = 0xFF;
338 if(o->address_overwrite[i] == 0x44 || o->address_overwrite[i] == 0x45)
339 fprintf (stderr, ", please use -yk cc instead");
340 if(o->address_overwrite[i] == 0x46)
341 if(o->address_overwrite[i+1] == 0x03)
342 fprintf (stderr, ", please use -ys instead");
343 else
344 o->address_overwrite[i] = 0xFF;
345 if(o->address_overwrite[i] == 0x47)
346 fprintf (stderr, ", please use -yt 0x%02x instead", o->address_overwrite[i+1]);
347 if(o->address_overwrite[i] == 0x4A)
348 fprintf (stderr, ", please use -yl 0x%02x instead", o->address_overwrite[i+1]);
349 if(o->address_overwrite[i] == 0x4B && o->address_overwrite[i+1] == 1)
350 fprintf (stderr, ", please use -yj instead");
351 if(o->address_overwrite[i] == 0xFF)
352 fprintf (stderr, ", this setting is the default");
353 fprintf (stderr, ".\n");
357 /* Update complement checksum */
358 chk = 0;
359 for (i = 0x134; i < 0x14d; ++i)
360 chk += rom[i];
361 rom[0x014d] = (unsigned char) (0xe7 - (chk & 0xff));
363 /* Update checksum */
364 chk = 0;
365 rom[0x14e] = 0;
366 rom[0x14f] = 0;
367 for (i = 0; i < size; ++i)
368 chk += rom[i];
369 rom[0x14e] = (unsigned char) ((chk >> 8) & 0xff);
370 rom[0x14f] = (unsigned char) (chk & 0xff);
372 if (*real_size < 0x150)
373 *real_size = 0x150;
376 void
377 sms_postproc (BYTE * rom, int size, int *real_size, struct sms_opt_s *o)
379 // based on https://www.smspower.org/Development/ROMHeader
380 // 0x1ff0 and 0x3ff0 are also possible, but never used
381 static const char tmr_sega[] = "TMR SEGA ";
382 short header_base = 0x7ff0;
383 int chk = 0;
384 unsigned long i;
385 // choose earlier positions for smaller roms
386 if (header_base > size)
387 header_base = 0x3ff0;
388 if (header_base > size)
389 header_base = 0x1ff0;
391 memcpy (&rom[header_base], tmr_sega, sizeof (tmr_sega) - 1);
392 // configure amounts of bytes to check
393 switch(o->rom_size)
395 case 0xa:
396 default:
397 i = 0x1FEF;
398 break;
399 case 0xb:
400 i = 0x3FEF;
401 break;
402 case 0xc:
403 i = 0x7FEF;
404 break;
405 case 0xd:
406 i = 0xBFEF;
407 break;
408 case 0xe:
409 i = 0xFFFF;
410 break;
411 case 0xf:
412 i = 0x1FFFF;
413 break;
414 case 0x0:
415 i = 0x3FFFF;
416 break;
417 case 0x1:
418 i = 0x7FFFF;
419 break;
420 case 0x2:
421 i = 0xFFFFF;
422 break;
424 // calculate checksum
425 for(;i > 0; --i)
427 chk += rom[i];
428 // 0x7FF0 - 0x7FFF is skipped
429 if(i == 0x8000)
430 i = 0x7FF0;
432 // we skipped index 0
433 chk += rom[0];
434 // little endian
435 rom[header_base + 0xa] = chk & 0xff;
436 rom[header_base + 0xb] = (chk>>8) & 0xff;
437 // game version
438 rom[header_base + 0xe] &= 0xF0;
439 rom[header_base + 0xe] |= o->version;
440 // rom size
441 rom[header_base + 0xf] = (o->region_code << 4) | o->rom_size;
445 rom_autosize_grow(BYTE **rom, int test_size, int *size, struct gb_opt_s *o)
447 int last_size = *size;
449 while ((test_size > *size) && (o->nb_rom_banks <= 512))
451 o->nb_rom_banks *= 2;
452 // banks work differently for mbc6, they have half the size
453 // but this in general ignored by -yo
454 *size = o->nb_rom_banks * 0x4000;
457 if (o->nb_rom_banks > 512)
459 fprintf (stderr, "error: auto-size banks exceeded max of 512 banks.\n");
460 return 0;
462 else
464 BYTE * t_rom = *rom;
465 *rom = realloc (*rom, *size);
466 if (*rom == NULL)
468 free(t_rom);
469 fprintf (stderr, "error: couldn't re-allocate size for larger rom image.\n");
470 return 0;
472 memset (*rom + last_size, FILL_BYTE, *size - last_size);
475 return 1;
479 noi2sym (char *filename)
481 FILE *noi, *sym;
482 char *nname, *sname;
483 //ssize_t read;
484 char read = ' ';
485 // no$gmb's implementation is limited to 32 character labels
486 // we can safely throw away the rest
487 #define SYM_FILE_NAME_LEN_MAX 32
488 char label[SYM_FILE_NAME_LEN_MAX + 1];
489 // 0x + 6 digit hex number
490 // -> 65536 rom banks is the maximum homebrew cartrideges support (TPP1)
491 char value[9];
492 int name_len = strlen(filename);
493 int i = 0;
494 // copy filename's value to nname and sname
495 nname = malloc((name_len+1) * sizeof(char));
496 strcpy (nname, filename);
497 sname = malloc((name_len+1) * sizeof(char));
498 strcpy (sname, filename);
499 // change the extensions
500 nname[name_len-1]='i';
501 nname[name_len-2]='o';
502 nname[name_len-3]='n';
503 sname[name_len-1]='m';
504 sname[name_len-2]='y';
505 sname[name_len-3]='s';
507 if (NULL == (noi = fopen (nname, "r")))
509 fprintf (stderr, "error: can't open %s: ", nname);
510 perror(NULL);
511 return 1;
513 if (NULL == (sym = fopen (sname, "w")))
515 fprintf (stderr, "error: can't create %s: ", sname);
516 perror(NULL);
517 return 1;
519 // write header
520 fprintf (sym, "; no$gmb compatible .sym file\n; Generated automagically by makebin\n");
521 // iterate through .noi file
522 while (read != EOF && (read = fgetc(noi)) != EOF)
524 // just skip line breaks
525 if (read == '\r' || read == '\n')
526 continue;
527 // read first 4 chars
528 for (i = 0; i < 4; ++i)
530 value[i] = read;
531 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n')
533 // leave for-loop
534 break;
537 // we left loop early
538 if (i != 4)
539 continue;
540 // only accept if line starts with this
541 if (strncmp(value, "DEF ", 4) == 0)
543 // read label
544 for (i = 0; i < (SYM_FILE_NAME_LEN_MAX - 1); ++i)
546 label[i] = read;
547 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n' || read == ' ')
549 // leave for-loop
550 break;
553 // skip rest of the label
554 while (read != EOF && read != '\r' && read != '\n' && read != ' ')
555 read = fgetc(noi);
556 // it has to be end of file or line if it's not space
557 if (read != ' ')
558 continue;
559 // strings have to end with \0
560 label[i+1] = '\0';
561 // read value
562 for (i = 0; i < 8; ++i)
564 value[i] = read;
565 if ((read = fgetc(noi)) == EOF || read == '\r' || read == '\n')
567 // leave for-loop
568 break;
571 // number is too long; ignore
572 if (read != EOF && read != '\r' && read != '\n')
573 continue;
574 value[i+1] = '\0';
575 // we successfully read label and value
577 // but filter out some invalid symbols
578 if (strcmp(label, ".__.ABS.") != 0)
579 fprintf (sym, "%02X:%04X %s\n", (unsigned int)(strtoul(value, NULL, 0)>>16), (unsigned int)strtoul(value, NULL, 0)&0xFFFF, label);
581 else
582 // skip until file/line end
583 while ((read = fgetc(noi))!= EOF && read != '\r' && read != '\n');
586 // free close files
587 fclose (noi);
588 fclose (sym);
590 fprintf (stderr, "Converted %s to %s.\n", nname, sname);
591 return 0;
595 read_ihx (FILE *fin, BYTE **rom, int *size, int *real_size, struct gb_opt_s *o)
597 int record_type;
599 int extaddr = 0;
602 int nbytes;
603 int addr;
604 int checksum, sum = 0;
606 if (getc (fin) != ':')
608 fprintf (stderr, "error: invalid IHX line.\n");
609 return 0;
611 nbytes = getbyte (fin, &sum);
612 addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
613 record_type = getbyte (fin, &sum);
614 if(record_type == 4)
616 extaddr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
617 extaddr <<= 16; // those are the upper 16 bits
618 checksum = getbyte (fin, &sum);
619 // move to the next record
620 if (0 != (sum & 0xff))
622 fprintf (stderr, "error: bad checksum: %02x.\n", checksum);
623 return 0;
625 while (isspace (sum = getc (fin))) /* skip all kind of spaces */
627 ungetc (sum, fin);
628 if (getc (fin) != ':')
630 fprintf (stderr, "error: invalid IHX line.\n");
631 return 0;
633 // parse real data part
634 checksum = sum = 0;
635 nbytes = getbyte (fin, &sum);
636 // lower 16 bits
637 addr = getbyte (fin, &sum) << 8 | getbyte (fin, &sum);
638 record_type = getbyte (fin, &sum);
640 // add linear address extension
641 addr |= extaddr;
642 // TODO: warn for unreachable banks according to chosen MBC
643 if (record_type > 1)
645 fprintf (stderr, "error: unsupported record type: %02x.\n", record_type);
646 return 0;
649 if (addr + nbytes > *size)
651 // If auto-size is enabled, grow rom bank size by power of 2 when needed
652 if (o->rom_banks_autosize)
654 if (rom_autosize_grow(rom, addr + nbytes, size, o) == 0)
655 return 0;
657 else
659 fprintf (stderr, "error: size of the buffer is too small.\n");
660 return 0;
664 while (nbytes--)
666 if (addr < *size)
667 (*rom)[addr++] = getbyte (fin, &sum);
670 if (addr > *real_size)
671 *real_size = addr;
673 checksum = getbyte (fin, &sum);
674 if (0 != (sum & 0xff))
676 fprintf (stderr, "error: bad checksum: %02x.\n", checksum);
677 return 0;
680 while (isspace (sum = getc (fin))) /* skip all kind of spaces */
682 ungetc (sum, fin);
684 while (1 != record_type); /* EOF record */
686 return 1;
690 main (int argc, char **argv)
692 int size = 32768, offset = 0, pack = 0, real_size = 0, i = 0;
693 char *token;
694 BYTE *rom;
695 FILE *fin, *fout;
696 char *filename = NULL;
697 int ret;
698 int gb = 0;
699 int sms = 0;
701 struct gb_opt_s gb_opt = {.cart_name="",
702 .licensee_str="00",
703 .mbc_type=0,
704 .nb_rom_banks=2,
705 .nb_ram_banks=0,
706 .licensee_id=0x33,
707 .is_gbc=0,
708 .is_sgb=0,
709 .sym_conversion=0,
710 .non_jp=0,
711 .rom_banks_autosize=0,
712 .do_logo_copy=true,
713 .address_overwrite={0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0} };
715 // 32KiB, SMS Export, version 0 <- should work with most emulaters (<32K was never used, GG accepts SMS)
716 struct sms_opt_s sms_opt = {.rom_size=0xc,
717 .region_code=4,
718 .version=0 };
720 #if defined(_WIN32)
721 setmode (fileno (stdout), O_BINARY);
722 #endif
724 while (*++argv && '-' == argv[0][0])
726 switch (argv[0][1])
728 case '\0':
729 break;
731 case 's':
732 if (!*++argv)
734 usage ();
735 return 1;
737 size = strtoul (*argv, NULL, 0);
738 break;
740 case 'o':
741 if (!*++argv)
743 usage ();
744 return 1;
746 offset = strtoul (*argv, NULL, 0);
747 break;
749 case 'h':
750 usage ();
751 return 0;
753 case 'p':
754 pack = 1;
755 break;
757 case 'Z':
758 /* generate GameBoy binary file */
759 gb = 1;
760 break;
762 case 'y':
763 /* GameBoy options:
764 * -yo Number of rom banks (default: 2)
765 * -ya Number of ram banks (default: 0)
766 * -yt MBC type (default: no MBC)
767 * -yn Name of program (default: name of output file)
768 * -yk,-yl,-yc,-yC,-yN,-ys,-yS,-yj,-yp see usage()
770 switch (argv[0][2])
772 case 'o':
773 if (!*++argv)
775 usage ();
776 return 1;
778 // Use auto-size for rom banks if -yto size param is 'A'
779 if ((*argv)[0] == 'A' || (*argv)[0] == 'a')
780 gb_opt.rom_banks_autosize = 1;
781 else
783 gb_opt.nb_rom_banks = strtoul (*argv, NULL, 0);
784 size = gb_opt.nb_rom_banks * 0x4000;
786 break;
788 case 'a':
789 if (!++argv)
791 usage ();
792 return 1;
794 gb_opt.nb_ram_banks = strtoul (*argv, NULL, 0);
795 break;
797 case 't':
798 if (!*++argv)
800 usage ();
801 return 1;
803 gb_opt.mbc_type = strtoul (*argv, NULL, 0);
804 break;
806 case 'n':
807 if (!*++argv)
809 usage ();
810 return 1;
812 strncpy (gb_opt.cart_name, *argv, CART_NAME_LEN-1);
813 gb_opt.cart_name[CART_NAME_LEN-1] = '\0';
814 break;
816 case 'k':
817 if (!*++argv)
819 usage ();
820 return 1;
822 strncpy (gb_opt.licensee_str, *argv, 2);
823 break;
825 case 'l':
826 if (!*++argv)
828 usage ();
829 return 1;
831 gb_opt.licensee_id = strtoul (*argv, NULL, 0);
832 break;
834 case 'c':
835 gb_opt.is_gbc = 1;
836 break;
838 case 'C':
839 gb_opt.is_gbc = 2;
840 break;
842 case 'N':
843 gb_opt.do_logo_copy = false; // when switch is present, turn off logo copy
844 break;
846 case 's':
847 gb_opt.is_sgb = 1;
848 break;
850 case 'S':
851 gb_opt.sym_conversion = 1;
852 break;
854 case 'j':
855 gb_opt.non_jp = 1;
856 break;
858 // like -yp0x143=0x80
859 case 'p':
860 // remove "-yp"
861 *argv += 3;
863 // also support -yp 0x143=0x80
864 if (!(*argv)[0])
865 if (!*++argv)
867 usage ();
868 return 1;
871 // effectively split string into argv and token
872 strtok(*argv, "=");
873 token = strtok(NULL, "=");
874 for (i = 0; i < 16; i+=2)
876 if (gb_opt.address_overwrite[i] == 0xFF)
878 gb_opt.address_overwrite[i] = strtoul (*argv, NULL, 0);
879 gb_opt.address_overwrite[i+1] = strtoul (token, NULL, 0);
880 break;
883 break;
885 default:
886 usage ();
887 return 1;
889 break;
891 case 'S':
892 /* generate SMS binary file */
893 sms = 1;
894 break;
896 case 'x':
898 switch (argv[0][2])
900 case 'o':
901 if (!*++argv)
903 usage ();
904 return 1;
906 sms_opt.rom_size = strtoul (*argv, NULL, 0);
907 if ( sms_opt.rom_size > 2 && (sms_opt.rom_size < 0xa || sms_opt.rom_size > 0xf ) )
909 fprintf (stderr, "error: invalid rom size (0x%X)", sms_opt.rom_size);
910 perror(NULL);
911 return 1;
913 if ( sms_opt.rom_size == 0xd || sms_opt.rom_size == 0x2 )
915 fprintf (stderr, "warning: this rom size (0x%X) is bugged in some BIOSes\n", sms_opt.rom_size);
917 break;
919 case 'j':
920 if (!*++argv)
922 usage ();
923 return 1;
925 sms_opt.region_code = strtoul (*argv, NULL, 0);
926 if ( sms_opt.region_code < 3 && sms_opt.region_code > 7 )
928 fprintf (stderr, "error: invalid region code (0x%X)", sms_opt.region_code);
929 perror(NULL);
930 return 1;
932 break;
934 case 'v':
935 if (!*++argv)
937 usage ();
938 return 1;
940 sms_opt.version = strtoul (*argv, NULL, 0);
941 if ( sms_opt.version > 0xf )
943 fprintf (stderr, "error: invalid version (0x%X)", sms_opt.version);
944 perror(NULL);
945 return 1;
947 break;
949 default:
950 usage ();
951 return 1;
953 break;
955 default:
956 usage ();
957 return 1;
961 fin = stdin;
962 fout = stdout;
963 if (*argv)
965 if ('-' != argv[0][0] || '\0' != argv[0][1])
967 if (NULL == (fin = fopen (*argv, "r")))
969 fprintf (stderr, "error: can't open %s: ", *argv);
970 perror(NULL);
971 return 1;
973 filename = *argv;
975 ++argv;
978 if (NULL != argv[0] && NULL != argv[1])
980 usage ();
981 return 1;
984 rom = malloc (size);
985 if (rom == NULL)
987 fclose (fin);
988 fprintf (stderr, "error: couldn't allocate room for the image.\n");
989 return 1;
991 memset (rom, FILL_BYTE, size);
993 if (gb_opt.sym_conversion == 1)
995 if (filename)
996 noi2sym(filename);
997 else
999 fprintf (stderr, "error: .noi to .sym conversion needs an input file.\n");
1003 ret = read_ihx (fin, &rom, &size, &real_size, &gb_opt);
1005 fclose (fin);
1007 if (ret)
1009 if (gb)
1010 gb_postproc (rom, size, &real_size, &gb_opt);
1011 else if (sms)
1012 sms_postproc (rom, size, &real_size, &sms_opt);
1014 if (*argv)
1016 if ('-' != argv[0][0] || '\0' != argv[0][1])
1018 if (NULL == (fout = fopen (*argv, "wb")))
1020 fprintf (stderr, "error: can't create %s: ", *argv);
1021 perror(NULL);
1022 return 1;
1026 // skip offset
1027 if (offset > 0)
1029 memmove (rom, rom + offset, size - offset);
1030 memset (rom + size - offset, FILL_BYTE, offset);
1033 fwrite (rom, 1, (pack ? real_size : size) - offset, fout);
1035 fclose (fout);
1037 return 0;
1039 else
1040 return 1;