git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

gbdk-support/makenes/makenes.c

      1 /*
      2   makenes - Prepend an iNES header to a binary ROM image
      3 
      4   Copyright (c) 2025 Michel Iwaniec
      5 
      6   This software is provided 'as-is', without any express or implied
      7   warranty.  In no event will the authors be held liable for any damages
      8   arising from the use of this software.
      9 
     10   Permission is granted to anyone to use this software for any purpose,
     11   including commercial applications, and to alter it and redistribute it
     12   freely, subject to the following restrictions:
     13 
     14   1. The origin of this software must not be misrepresented; you must not
     15      claim that you wrote the original software. If you use this software
     16      in a product, an acknowledgment in the product documentation would be
     17      appreciated but is not required.
     18   2. Altered source versions must be plainly marked as such, and must not be
     19      misrepresented as being the original software.
     20   3. This notice may not be removed or altered from any source distribution.
     21 */
     22 
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <stdint.h>
     26 #include <stdbool.h>
     27 #include <string.h>
     28 #include <ctype.h>
     29 
     30 #if defined(_WIN32)
     31 #include <fcntl.h>
     32 #include <io.h>
     33 #else
     34 #include <unistd.h>
     35 #endif
     36 
     37 #define BANK_SIZE 16384
     38 uint8_t bank_data[BANK_SIZE];   // Static work ram for copying banks from input to output file
     39 
     40 void usage(void)
     41 {
     42   fprintf (stderr,
     43            "makenes: Prepend an iNES header to a binary ROM image file.\n"
     44            "Usage: makenes [options] [<in_file> [<out_file>]]\n"
     45            "Options:\n"
     46            "  -m   Mapper number (default: 0)\n"
     47            "  -n   Nametable arrangement:\n"
     48            "                              0: vertical arrangement / horizontal mirroring\n"
     49            "                              1: horizontal arrangement / vertical mirroring\n"
     50            "  -b   Battery bit set (default: 0)\n"
     51            "  -a   Alternative nametable layout (default: 0)\n"
     52 
     53            "Arguments:\n"
     54            "  <in_file>      optional binary input file, '-' means stdin. (default: stdin)\n"
     55            "  <out_file>     optional .nes output file, '-' means stdout. (default: stdout)\n");
     56 }
     57 
     58 struct nes_opt_s
     59 {
     60     uint8_t mapper;
     61     uint8_t num_prg_banks;
     62     uint8_t num_chr_banks;
     63     bool nametable_layout;
     64     bool alternative_nametables;
     65     bool battery;
     66 };
     67 
     68 void write_ines_header(FILE* fout, struct nes_opt_s* nes_opt)
     69 {
     70     char id_string[] = { 0x4E, 0x45, 0x53, 0x1A };  
     71     uint8_t flags6 = ((nes_opt->mapper & 0xF) << 4) | 
     72                       (nes_opt->alternative_nametables << 3) |
     73                       (nes_opt->battery << 1) |
     74                       (nes_opt->nametable_layout << 0);
     75     uint8_t flags7 = (nes_opt->mapper & 0xF0);
     76     uint8_t flags8 = 0;
     77     uint8_t flags9 = 0;
     78     uint8_t flags10 = 0;
     79     uint8_t padding[5] = { 0, 0, 0, 0, 0, };
     80     // "NES" + end-of-file
     81     fwrite(&id_string, sizeof(char), 4, fout);
     82     fwrite(&nes_opt->num_prg_banks, sizeof(uint8_t), 1, fout);
     83     fwrite(&nes_opt->num_chr_banks, sizeof(uint8_t), 1, fout);
     84     fwrite(&flags6, sizeof(uint8_t), 1, fout);
     85     fwrite(&flags7, sizeof(uint8_t), 1, fout);
     86     fwrite(&flags8, sizeof(uint8_t), 1, fout);
     87     fwrite(&flags9, sizeof(uint8_t), 1, fout);
     88     fwrite(&flags10, sizeof(uint8_t), 1, fout);
     89     fwrite(padding, sizeof(uint8_t), 5, fout);
     90 }
     91 
     92 void parse_cmd_line(int argc, const char** argv, struct nes_opt_s* nes_opt, const char** input_filename, const char** output_filename)
     93 {
     94     int i = 1;
     95     while(i < argc)
     96     {
     97         if(argv[i][0] == '-')
     98         {
     99             switch(argv[i][1])
    100             {
    101                 case 'm':
    102                     nes_opt->mapper = strtoul(argv[i+1], NULL, 0);
    103                     break;
    104                 case 'n':
    105                     nes_opt->nametable_layout = strtoul(argv[i+1], NULL, 0);
    106                     break;
    107                 case 'b':
    108                     nes_opt->battery = strtoul(argv[i+1], NULL, 0);
    109                     break;
    110                 case 'a':
    111                     nes_opt->alternative_nametables = strtoul(argv[i+1], NULL, 0);
    112                     break;
    113                 case 'h':
    114                     usage();
    115                     exit(0);
    116                 default:
    117                     usage();
    118                     exit(1);
    119             }
    120             i++;
    121         }
    122         else
    123         {
    124             if(*input_filename == NULL)
    125             {
    126                 *input_filename = argv[i];
    127             }
    128             else if(*output_filename == NULL)
    129             {
    130                 *output_filename = argv[i];
    131             }
    132             else
    133             {
    134                 usage();
    135                 exit(1);
    136             }
    137         }
    138         i++;
    139     }
    140 }
    141 
    142 int main (int argc, char** argv)
    143 {
    144     int i = 0;
    145     size_t input_bin_size;
    146     uint8_t num_banks;
    147     FILE* f_input_bin;
    148     FILE* f_output_nes;
    149     char* input_filename = NULL;
    150     char* output_filename = NULL;
    151     struct nes_opt_s nes_opt = {
    152                               .mapper = 0,
    153                               .num_prg_banks = 0,
    154                               .num_chr_banks = 0,
    155                               .nametable_layout = 0,
    156                               .alternative_nametables = 0,
    157                               .battery = 0
    158     };
    159 #if defined(_WIN32)
    160   setmode (fileno (stdout), O_BINARY);
    161 #endif
    162     parse_cmd_line(argc, argv, &nes_opt, &input_filename, &output_filename);
    163     // Open input .bin file
    164     f_input_bin = stdin;
    165     if(input_filename)
    166     {
    167         f_input_bin = fopen(input_filename, "rb");
    168         if(f_input_bin == NULL)
    169         {
    170             fprintf(stderr, "Error: Failed to open input file `%s`\n", input_filename);
    171         }
    172     }
    173     // Get file size and number of banks
    174     fseek(f_input_bin, 0, SEEK_END);
    175     input_bin_size = ftell(f_input_bin);
    176     fseek(f_input_bin, 0, SEEK_SET);
    177     if(input_bin_size % BANK_SIZE != 0)
    178     {
    179         fprintf(stderr, "Error: input file size not a multiple of bank size %d\n", BANK_SIZE);
    180         exit(1);
    181     }
    182     nes_opt.num_prg_banks = (uint8_t)(input_bin_size / BANK_SIZE);
    183     // Open output .nes file
    184     f_output_nes = stdout;
    185     if(output_filename)
    186     {
    187         f_output_nes = fopen(output_filename, "wb");
    188         if(f_output_nes == NULL)
    189         {
    190             fprintf(stderr, "Error: Failed to open output file `%s`\n", output_filename);
    191             exit(1);
    192         }
    193     }
    194     // Write iNES header to output file
    195     write_ines_header(f_output_nes, &nes_opt);
    196     // Copy input file to output file, one 16kB bank at a time 
    197     for(uint8_t i = 0; i < nes_opt.num_prg_banks; i++)
    198     {
    199         size_t num_bytes = fread(bank_data, 1, BANK_SIZE, f_input_bin);
    200         fwrite(bank_data, 1, BANK_SIZE, f_output_nes);
    201     }
    202     fclose(f_input_bin);
    203     fclose(f_output_nes);
    204     return 0;
    205 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.