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.