git.y1.nz

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.