gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/png2asset/export_c_file.cpp
1 // This prevents windows build errors regarding the 'fopen' function
2 // Example: 'fopen': This function or variable may be unsafe.Consider using fopen_s instead.To disable deprecation, use _CRT_SECURE_NO_WARNINGS.See online help for details.
3 // More info: https://stackoverflow.com/questions/14386/fopen-deprecated-warning
4 // From bbbbbr: `Looks like some of the benefits are concurrency protection, but that probably isn't an issue for png2asset`
5 #ifdef _WIN32
6 #define _CRT_SECURE_NO_DEPRECATE
7 #endif
8
9 #include <vector>
10 #include <string>
11 #include <algorithm>
12 #include <cstring>
13 #include <set>
14 #include <stdio.h>
15 #include <fstream>
16 #include <cstdint>
17
18 #include "lodepng.h"
19 #include "mttile.h"
20
21 #include "cmp_int_color.h"
22 #include "metasprites.h"
23
24 #include "png2asset.h"
25 #include "png_image.h"
26 #include "export.h"
27 #include "image_utils.h"
28 #include "process_arguments.h"
29 #include "rgb_to_nes_lut.h"
30
31 using namespace std;
32
33 enum {
34 ZGB_TILES_MODE_NORMAL,
35 ZGB_TILES_MODE_TILESONLY
36 };
37
38 static exportOptions_t exportOpt;
39
40 static void export_c_palette_data(PNG2AssetData* assetData, FILE* file);
41 static void export_c_tile_data(PNG2AssetData* assetData, FILE* file);
42 static void export_c_metasprite_mode(PNG2AssetData* assetData, FILE* file);
43 static void export_c_map_mode(PNG2AssetData* assetData, FILE* file);
44
45 static void export_c_zgb_metasprite_struct(PNG2AssetData* assetData, FILE* file);
46 static void export_c_zgb_per_tile_palette_ids(PNG2AssetData* assetData, FILE* file);
47 static void export_c_zgb_tiles_struct(PNG2AssetData* assetData, FILE* file, int output_mode);
48 static void export_c_zgb_map_struct(PNG2AssetData* assetData, FILE* file);
49
50
51 bool export_c_file( PNG2AssetData* assetData) {
52
53 FILE* file;
54 calc_palette_and_tileset_export_size(assetData, &exportOpt);
55
56 file = fopen(assetData->args->output_filename.c_str(), "w");
57 if(!file) {
58 printf("Error writing file: %s", assetData->args->output_filename.c_str());
59 return false;
60 }
61
62 if (assetData->args->bank != BANK_NUM_UNSET) {
63 if (assetData->args->area_specified)
64 fprintf(file, "#pragma constseg %s_%d\n\n", assetData->args->area_name.c_str(), assetData->args->bank);
65 else
66 fprintf(file, "#pragma bank %d\n\n", assetData->args->bank);
67 }
68
69 fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n");
70 fprintf(file, "// Conversion args:%s\n\n", assetData->args->args_for_logging_to_output.c_str());
71
72 fprintf(file, "#include <stdint.h>\n");
73 fprintf(file, "#include <gbdk/platform.h>\n");
74 fprintf(file, "#include <gbdk/metasprites.h>\n");
75
76 if (assetData->args->use_structs) {
77 fprintf(file, "#include \"TilesInfo.h\"\n");
78 if (assetData->args->includedMapOrMetaspriteData) {
79 if (assetData->args->export_as_map)
80 fprintf(file, "#include \"MapInfo.h\"\n");
81 else
82 fprintf(file, "#include \"MetaSpriteInfo.h\"\n");
83 }
84 }
85
86 fprintf(file, "\n");
87
88 fprintf(file, "BANKREF(%s)\n\n", assetData->args->data_name.c_str());
89
90 if (exportOpt.has_palette_data_to_export)
91 export_c_palette_data(assetData, file);
92
93 if (assetData->args->includeTileData)
94 export_c_tile_data(assetData, file);
95
96 if (assetData->args->includedMapOrMetaspriteData) {
97
98 if (assetData->args->export_as_map) {
99 export_c_map_mode(assetData, file);
100 }
101 else {
102 export_c_metasprite_mode(assetData, file);
103 }
104 }
105 else {
106 // "-use_structs -tiles_only" case
107 if ((assetData->args->includeTileData) && (assetData->args->use_structs)) {
108 export_c_zgb_per_tile_palette_ids(assetData, file);
109 export_c_zgb_tiles_struct(assetData, file, ZGB_TILES_MODE_TILESONLY);
110 }
111 }
112
113 fclose(file);
114 return true; // success
115 }
116
117
118 static void export_c_palette_data(PNG2AssetData* assetData, FILE* file) {
119
120 if (!(assetData->args->use_structs) || (assetData->args->includeTileData)) {
121 fprintf(file, "const palette_color_t %s_palettes[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)exportOpt.color_count);
122 const size_t palette_start = exportOpt.color_start / assetData->image.colors_per_pal;
123 const size_t total_palette_count = assetData->image.total_color_count / assetData->image.colors_per_pal;
124 int cur_color = exportOpt.color_start;
125
126 fprintf(file, "\t");
127 for (size_t i = palette_start; i < total_palette_count; ++i)
128 {
129 unsigned char* pal_ptr = &assetData->image.palette[i * (assetData->image.colors_per_pal * RGBA32_SZ)];
130 for (int c = 0; c < (int)assetData->image.colors_per_pal; ++c, pal_ptr += RGBA32_SZ)
131 {
132 size_t rgb222 = (((pal_ptr[2] >> 6) & 0x3) << 4) |
133 (((pal_ptr[1] >> 6) & 0x3) << 2) |
134 (((pal_ptr[0] >> 6) & 0x3) << 0);
135
136 if (assetData->args->convert_rgb_to_nes)
137 fprintf(file, "0x%0X", rgb_to_nes[rgb222]);
138 else
139 fprintf(file, "RGB8(%3d,%3d,%3d)", pal_ptr[0], pal_ptr[1], pal_ptr[2]);
140
141 // Trailing comma except for the last entry
142 if (cur_color < ((int)assetData->image.total_color_count - 1))
143 fprintf(file, ",");
144
145 // Line break every 4 colors to keep line width down,
146 // otherwise a trailing space (except on the last color/array item)
147 if (((cur_color + 1) % 4) == 0)
148 fprintf(file, "\n\t");
149 else if (cur_color < ((int)assetData->image.total_color_count - 1))
150 fprintf(file, " ");
151
152 cur_color++;
153 }
154 }
155 fprintf(file, "};\n");
156 }
157 }
158
159
160 static void export_c_tile_data(PNG2AssetData* assetData, FILE* file) {
161
162 fprintf(file, "\n");
163 fprintf(file, "const uint8_t %s_tiles[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)(exportOpt.tiles_count * assetData->image.tile_w * assetData->image.tile_h * assetData->args->bpp / 8));
164 fprintf(file, "\t");
165 for(vector< Tile >::iterator it = assetData->tiles.begin() + exportOpt.tiles_start; it != assetData->tiles.end(); ++it)
166 {
167
168 int line_break = 1; // Start with 1 to prevent line break on first iteration
169 vector< unsigned char > packed_data = (*it).GetPackedData(assetData->args->pack_mode, assetData->image.tile_w, assetData->image.tile_h, assetData->args->bpp);
170 for(vector< unsigned char >::iterator it2 = packed_data.begin(); it2 != packed_data.end(); ++it2)
171 {
172 fprintf(file, "0x%02x", (*it2));
173 if((it + 1) != assetData->tiles.end() || (it2 + 1) != packed_data.end())
174 fprintf(file, ",");
175 // Add a line break after each 8x8 tile
176 if (((line_break++) % (8 * assetData->args->bpp)) == 0)
177 fprintf(file, "\n\t");
178 }
179 }
180 fprintf(file, "};\n\n");
181
182 }
183
184
185 static void export_c_metasprite_mode(PNG2AssetData* assetData, FILE* file) {
186
187 for(vector< MetaSprite >::iterator it = assetData->sprites.begin(); it != assetData->sprites.end(); ++it)
188 {
189 fprintf(file, "const metasprite_t %s_metasprite%d[] = {\n", assetData->args->data_name.c_str(), (int)(it - assetData->sprites.begin()));
190 for(MetaSprite::iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2)
191 {
192 int pal_idx = (*it2).props & 0xF;
193 int dmg_pal = ((*it2).props >> 4) & 1;
194 int flip_x = ((*it2).props >> 5) & 1;
195 int flip_y = ((*it2).props >> 6) & 1;
196 int priority = ((*it2).props >> 7) & 1;
197 fprintf(file,
198 "\tMETASPR_ITEM(%d, %d, %d, S_PAL(%d)%s%s%s%s),\n",
199 (*it2).offset_y,
200 (*it2).offset_x,
201 (*it2).offset_idx,
202 pal_idx,
203 (dmg_pal) ? " | S_PALETTE" : "",
204 (flip_x) ? " | S_FLIPX" : "",
205 (flip_y) ? " | S_FLIPY" : "",
206 (priority) ? " | S_PRIORITY" : "");
207 }
208 fprintf(file, "\tMETASPR_TERM\n");
209 fprintf(file, "};\n\n");
210 }
211
212 fprintf(file, "const metasprite_t* const %s_metasprites[%d] = {\n\t", assetData->args->data_name.c_str(), (unsigned int)(assetData->sprites.size()));
213 for(vector< MetaSprite >::iterator it = assetData->sprites.begin(); it != assetData->sprites.end(); ++it)
214 {
215 fprintf(file, "%s_metasprite%d", assetData->args->data_name.c_str(), (int)(it - assetData->sprites.begin()));
216 if(it + 1 != assetData->sprites.end())
217 fprintf(file, ", ");
218 }
219 fprintf(file, "\n};\n");
220
221 if(assetData->args->use_structs)
222 export_c_zgb_metasprite_struct(assetData, file);
223
224 }
225
226 static void export_c_zgb_metasprite_struct(PNG2AssetData* assetData, FILE* file) {
227 // Export ZGB Metasprite Struct
228 fprintf(file, "\n");
229 fprintf(file, "const struct MetaSpriteInfo %s = {\n", assetData->args->data_name.c_str());
230 fprintf(file, "\t.width=%d," " // width\n", (unsigned int)assetData->args->pivot.width);
231 fprintf(file, "\t.height=%d," " // height\n", (unsigned int)assetData->args->pivot.height);
232 fprintf(file, "\t.num_tiles=%d," " // num tiles\n", (unsigned int)assetData->tiles.size() * (assetData->image.tile_h >> 3));
233 fprintf(file, "\t.data=%s_tiles," " // tiles\n", assetData->args->data_name.c_str());
234 fprintf(file, "\t.num_palettes=%d," " // num palettes\n", (unsigned int)(assetData->image.total_color_count / assetData->image.colors_per_pal));
235 fprintf(file, "\t.palettes=%s_palettes," " // CGB palette\n", assetData->args->data_name.c_str());
236 fprintf(file, "\t.num_sprites=%d," " // num sprites\n", (unsigned int)assetData->sprites.size());
237 fprintf(file, "\t.metasprites=%s_metasprites," " // metasprites\n", assetData->args->data_name.c_str());
238 fprintf(file, "};\n");
239 }
240
241
242 static void export_c_map_mode(PNG2AssetData* assetData, FILE* file) {
243
244 // ZGB export mode
245 if(assetData->args->use_structs)
246 {
247 // For ZGB Structs with source_tileset enabled, the primary tile data points to the EXTERNAL source tileset
248 // so add the required externs used to reference them in the struct
249 if( (assetData->args->has_source_tilesets)) {
250 fprintf(file, "\n");
251 fprintf(file, "BANKREF_EXTERN(%s)\n", extract_name(assetData->args->source_tilesets[0]).c_str());
252 fprintf(file, "extern const struct TilesInfo %s;\n", extract_name(assetData->args->source_tilesets[0]).c_str());
253 }
254
255 // Map's own tile data, if any present
256 if(assetData->args->includeTileData) {
257 export_c_zgb_per_tile_palette_ids(assetData, file);
258 export_c_zgb_tiles_struct(assetData, file, ZGB_TILES_MODE_NORMAL);
259 }
260 }
261
262 //Export map
263 fprintf(file, "\n");
264 fprintf(file, "const unsigned char %s_map[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map.size()));
265 // TODO: These hardwired "/ 8" should be using " / assetData->image.tile_w" and "_h"
266 // TODO: Should also be converted to using args.map_entry_size_bytes
267 size_t line_size = assetData->map.size() / (assetData->image.h / 8);
268 if(assetData->args->output_transposed) {
269
270 for(size_t i = 0; i < line_size; ++i)
271 {
272 fprintf(file, "\t");
273 for(size_t j = 0; j < assetData->image.h / 8; ++j)
274 {
275 fprintf(file, "0x%02x,", assetData->map[j * line_size + i]);
276 }
277 fprintf(file, "\n");
278 }
279 }
280 else {
281
282 for(size_t j = 0; j < assetData->image.h / 8; ++j)
283 {
284 fprintf(file, "\t");
285 for(size_t i = 0; i < line_size; ++i)
286 {
287 fprintf(file, "0x%02x,", assetData->map[j * line_size + i]);
288 }
289 fprintf(file, "\n");
290 }
291 }
292 fprintf(file, "};\n");
293
294
295 //Export map attributes (if any)
296 if(assetData->args->use_map_attributes && assetData->map_attributes.size())
297 {
298 fprintf(file, "\n");
299 fprintf(file, "const unsigned char %s_map_attributes[%d] = {\n", assetData->args->data_name.c_str(), (unsigned int)(assetData->map_attributes.size()));
300 if(assetData->args->output_transposed) {
301 for(size_t i = 0; i < assetData->args->map_attributes_packed_size.width; ++i)
302 {
303 fprintf(file, "\t");
304 for(size_t j = 0; j < assetData->args->map_attributes_packed_size.height; ++j)
305 {
306 fprintf(file, "0x%02x,", assetData->map_attributes[j * assetData->args->map_attributes_packed_size.width + i]);
307 }
308 fprintf(file, "\n");
309 }
310 }
311 else {
312 for(size_t j = 0; j < assetData->args->map_attributes_packed_size.height; ++j)
313 {
314 fprintf(file, "\t");
315 for(size_t i = 0; i < assetData->args->map_attributes_packed_size.width; ++i)
316 {
317 fprintf(file, "0x%02x,", assetData->map_attributes[j * assetData->args->map_attributes_packed_size.width + i]);
318 }
319 fprintf(file, "\n");
320 }
321 }
322
323 fprintf(file, "};\n");
324 }
325
326 if(assetData->args->use_structs)
327 export_c_zgb_map_struct(assetData, file);
328 }
329
330
331 static void export_c_zgb_per_tile_palette_ids(PNG2AssetData* assetData, FILE* file) {
332 //Export tiles pals (if any)
333 if(!assetData->args->use_map_attributes)
334 {
335 fprintf(file, "\n");
336 fprintf(file, "const uint8_t %s_tile_pals[%d] = {\n\t", assetData->args->data_name.c_str(), (unsigned int)exportOpt.tiles_count);
337 for(vector< Tile >::iterator it = assetData->tiles.begin() + exportOpt.tiles_start; it != assetData->tiles.end(); ++it)
338 {
339 fprintf(file, "%d", it->pal);
340 if (it + 1 != assetData->tiles.end())
341 fprintf(file, ", ");
342 }
343 fprintf(file, "\n};\n");
344 }
345 }
346
347
348 static void export_c_zgb_tiles_struct(PNG2AssetData* assetData, FILE* file, int output_mode) {
349 // Export Tiles Info
350 fprintf(file, "\n");
351 // ZGB Tiles-only mode doesn't append "_tiles_info" to the struct name and lacks a BANKREF output.
352 if (output_mode == ZGB_TILES_MODE_TILESONLY) {
353 fprintf(file, "const struct TilesInfo %s = {\n", assetData->args->data_name.c_str());
354 }
355 else { // implied (output_mode == ZGB_TILES_MODE_NORMAL)
356 fprintf(file, "BANKREF(%s_tiles_info)\n", assetData->args->data_name.c_str());
357 fprintf(file, "const struct TilesInfo %s_tiles_info = {\n", assetData->args->data_name.c_str());
358 }
359
360 fprintf(file, "\t.num_frames=%d," " // num tiles\n", (unsigned int)exportOpt.tiles_count);
361 fprintf(file, "\t.data=%s_tiles," " // tiles\n", assetData->args->data_name.c_str());
362 fprintf(file, "\t.num_pals=%d," " // num palettes\n", (unsigned int)(exportOpt.color_count / assetData->image.colors_per_pal));
363 fprintf(file, "\t.pals=%s_palettes," " // palettes\n", assetData->args->data_name.c_str());
364
365 // This var references the output of export_c_zgb_per_tile_palette_ids()
366 if(!assetData->args->use_map_attributes)
367 fprintf(file, "\t.color_data=%s_tile_pals, // tile palettes\n", assetData->args->data_name.c_str());
368 else
369 fprintf(file, "\t.color_data=0 " " // tile palettes\n");
370 fprintf(file, "};\n");
371 }
372
373
374 // "-use_structs" map output behavior
375 //
376 // "-source_tileset" NOT enabled:
377 // - tile data -> TileInfo struct, point .tiles to it
378 // - .tiles points to TileInfo struct, .extra_tiles is NULL
379 // - only map tile palettes *not* found in the source tileset will be emitted for .num_pals/.pals
380 //
381 // "-source_tileset" IS enabled
382 // - .tiles -> the external source tileset
383 // - .extra_tiles -> map TileInfo
384 // - contains unique tiles not found in source tileset (not in entity tiles)
385 // - if none of those tiles were found then
386 // - map TileInfo will not be emitted
387 // - .extra_tiles == NULL then
388 // - *all* map tile palettes will be emitted for .num_pals/.pals (those from map and source tileset)
389 //
390 static void export_c_zgb_map_struct(PNG2AssetData* assetData, FILE* file) {
391 //Export Map Info
392 fprintf(file, "\n");
393
394 if(assetData->args->includeTileData) {
395 fprintf(file, "BANKREF_EXTERN(%s_tiles_info)\n", assetData->args->data_name.c_str());
396 }
397
398 fprintf(file, "const struct MapInfo %s = {\n", assetData->args->data_name.c_str());
399 fprintf(file, "\t.data=%s_map," " // map\n", assetData->args->data_name.c_str());
400 fprintf(file, "\t.width=%d," " // width\n", assetData->image.w >> 3);
401 fprintf(file, "\t.height=%d," " // height\n", assetData->image.h >> 3);
402
403 if(assetData->args->use_map_attributes && assetData->map_attributes.size())
404 fprintf(file, "\t.attributes=%s_map_attributes," " // map attributes\n", assetData->args->data_name.c_str());
405 else
406 fprintf(file, "\t.attributes=0," " // map attributes\n");
407
408 // For ZGB Structs with source_tileset enabled, the tile data points to the EXTERNAL source tileset
409 if (assetData->args->has_source_tilesets == true) {
410 fprintf(file, "\t.tiles_bank=BANK(%s)," " // source tiles bank\n", extract_name(assetData->args->source_tilesets[0]).c_str());
411 fprintf(file, "\t.tiles=&%s," " // source tiles info\n", extract_name(assetData->args->source_tilesets[0]).c_str());
412
413 // If map tiles were found that were not in the source tileset (or entity tileset)
414 // then point ".extra_tiles" to them. Otherwise set it to null to indicate none found.
415 if (exportOpt.tiles_count > 0) {
416 fprintf(file, "\t.extra_tiles=&%s_tiles_info," " // pointer to Tilesinfo struct with map tiles not found in the source tileset\n", assetData->args->data_name.c_str());
417 fprintf(file, "\t.extra_tiles_bank=BANK(%s_tiles_info), // bank for above Tilesinfo struct\n", assetData->args->data_name.c_str());
418 }
419 else {
420 fprintf(file, "\t.extra_tiles=NULL," " // pointer to Tilesinfo struct with map tiles not found in the source tileset (no extra tiles found)\n");
421 fprintf(file, "\t.extra_tiles_bank=0," " // bank for above Tilesinfo struct\n");
422 }
423
424 }
425 else if(assetData->args->includeTileData) {
426 fprintf(file, "\t.tiles_bank=BANK(%s_tiles_info), // tiles bank\n", assetData->args->data_name.c_str());
427 fprintf(file, "\t.tiles=&%s_tiles_info," " // tiles info\n", assetData->args->data_name.c_str());
428 }
429 fprintf(file, "};\n");
430 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.