Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/n64graphics.c
7854 views
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <strings.h>
4
5
#define STBI_NO_LINEAR
6
#define STBI_NO_HDR
7
#define STBI_NO_TGA
8
#define STB_IMAGE_IMPLEMENTATION
9
#include <stb/stb_image.h>
10
#define STB_IMAGE_WRITE_IMPLEMENTATION
11
#include <stb/stb_image_write.h>
12
13
#include "n64graphics.h"
14
#include "utils.h"
15
16
// SCALE_M_N: upscale/downscale M-bit integer to N-bit
17
#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F)
18
#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF)
19
#define SCALE_4_8(VAL_) ((VAL_) * 0x11)
20
#define SCALE_8_4(VAL_) ((VAL_) / 0x11)
21
#define SCALE_3_8(VAL_) ((VAL_) * 0x24)
22
#define SCALE_8_3(VAL_) ((VAL_) / 0x24)
23
24
25
typedef struct
26
{
27
enum
28
{
29
IMG_FORMAT_RGBA,
30
IMG_FORMAT_IA,
31
IMG_FORMAT_I,
32
IMG_FORMAT_CI,
33
} format;
34
int depth;
35
} img_format;
36
37
//---------------------------------------------------------
38
// N64 RGBA/IA/I/CI -> internal RGBA/IA
39
//---------------------------------------------------------
40
41
rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth)
42
{
43
rgba *img;
44
int img_size;
45
46
img_size = width * height * sizeof(*img);
47
img = malloc(img_size);
48
if (!img) {
49
ERROR("Error allocating %d bytes\n", img_size);
50
return NULL;
51
}
52
53
if (depth == 16) {
54
for (int i = 0; i < width * height; i++) {
55
img[i].red = SCALE_5_8((raw[i*2] & 0xF8) >> 3);
56
img[i].green = SCALE_5_8(((raw[i*2] & 0x07) << 2) | ((raw[i*2+1] & 0xC0) >> 6));
57
img[i].blue = SCALE_5_8((raw[i*2+1] & 0x3E) >> 1);
58
img[i].alpha = (raw[i*2+1] & 0x01) ? 0xFF : 0x00;
59
}
60
} else if (depth == 32) {
61
for (int i = 0; i < width * height; i++) {
62
img[i].red = raw[i*4];
63
img[i].green = raw[i*4+1];
64
img[i].blue = raw[i*4+2];
65
img[i].alpha = raw[i*4+3];
66
}
67
}
68
69
return img;
70
}
71
72
ia *raw2ia(const uint8_t *raw, int width, int height, int depth)
73
{
74
ia *img;
75
int img_size;
76
77
img_size = width * height * sizeof(*img);
78
img = malloc(img_size);
79
if (!img) {
80
ERROR("Error allocating %u bytes\n", img_size);
81
return NULL;
82
}
83
84
switch (depth) {
85
case 16:
86
for (int i = 0; i < width * height; i++) {
87
img[i].intensity = raw[i*2];
88
img[i].alpha = raw[i*2+1];
89
}
90
break;
91
case 8:
92
for (int i = 0; i < width * height; i++) {
93
img[i].intensity = SCALE_4_8((raw[i] & 0xF0) >> 4);
94
img[i].alpha = SCALE_4_8(raw[i] & 0x0F);
95
}
96
break;
97
case 4:
98
for (int i = 0; i < width * height; i++) {
99
uint8_t bits;
100
bits = raw[i/2];
101
if (i % 2) {
102
bits &= 0xF;
103
} else {
104
bits >>= 4;
105
}
106
img[i].intensity = SCALE_3_8((bits >> 1) & 0x07);
107
img[i].alpha = (bits & 0x01) ? 0xFF : 0x00;
108
}
109
break;
110
case 1:
111
for (int i = 0; i < width * height; i++) {
112
uint8_t bits;
113
uint8_t mask;
114
bits = raw[i/8];
115
mask = 1 << (7 - (i % 8)); // MSb->LSb
116
bits = (bits & mask) ? 0xFF : 0x00;
117
img[i].intensity = bits;
118
img[i].alpha = bits;
119
}
120
break;
121
default:
122
ERROR("Error invalid depth %d\n", depth);
123
break;
124
}
125
126
return img;
127
}
128
129
ia *raw2i(const uint8_t *raw, int width, int height, int depth)
130
{
131
ia *img = NULL;
132
int img_size;
133
134
img_size = width * height * sizeof(*img);
135
img = malloc(img_size);
136
if (!img) {
137
ERROR("Error allocating %u bytes\n", img_size);
138
return NULL;
139
}
140
141
switch (depth) {
142
case 8:
143
for (int i = 0; i < width * height; i++) {
144
img[i].intensity = raw[i];
145
img[i].alpha = 0xFF;
146
}
147
break;
148
case 4:
149
for (int i = 0; i < width * height; i++) {
150
uint8_t bits;
151
bits = raw[i/2];
152
if (i % 2) {
153
bits &= 0xF;
154
} else {
155
bits >>= 4;
156
}
157
img[i].intensity = SCALE_4_8(bits);
158
img[i].alpha = img[i].intensity; // alpha copy intensity
159
// TODO: modes
160
// img[i].alpha = 0xFF; // alpha = 1
161
// img[i].alpha = img[i].intensity ? 0xFF : 0x00; // binary
162
}
163
break;
164
default:
165
ERROR("Error invalid depth %d\n", depth);
166
break;
167
}
168
169
return img;
170
}
171
172
// convert CI raw data and palette to raw data (either RGBA16 or IA16)
173
uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth)
174
{
175
uint8_t *raw;
176
int raw_size;
177
178
// first convert to raw RGBA
179
raw_size = sizeof(uint16_t) * width * height;
180
raw = malloc(raw_size);
181
if (!raw) {
182
ERROR("Error allocating %u bytes\n", raw_size);
183
return NULL;
184
}
185
186
for (int i = 0; i < width * height; i++) {
187
int pal_idx = rawci[i];
188
if (ci_depth == 4) {
189
int byte_idx = i / 2;
190
int nibble = 1 - (i % 2);
191
int shift = 4 * nibble;
192
pal_idx = (rawci[byte_idx] >> shift) & 0xF;
193
}
194
raw[2*i] = palette[2*pal_idx];
195
raw[2*i+1] = palette[2*pal_idx+1];
196
}
197
198
return raw;
199
}
200
201
202
//---------------------------------------------------------
203
// internal RGBA/IA -> N64 RGBA/IA/I/CI
204
// returns length written to 'raw' used or -1 on error
205
//---------------------------------------------------------
206
207
int rgba2raw(uint8_t *raw, const rgba *img, int width, int height, int depth)
208
{
209
int size = (width * height * depth + 7) / 8;
210
INFO("Converting RGBA%d %dx%d to raw\n", depth, width, height);
211
212
if (depth == 16) {
213
for (int i = 0; i < width * height; i++) {
214
uint8_t r, g, b, a;
215
r = SCALE_8_5(img[i].red);
216
g = SCALE_8_5(img[i].green);
217
b = SCALE_8_5(img[i].blue);
218
a = img[i].alpha ? 0x1 : 0x0;
219
raw[i*2] = (r << 3) | (g >> 2);
220
raw[i*2+1] = ((g & 0x3) << 6) | (b << 1) | a;
221
}
222
} else if (depth == 32) {
223
for (int i = 0; i < width * height; i++) {
224
raw[i*4] = img[i].red;
225
raw[i*4+1] = img[i].green;
226
raw[i*4+2] = img[i].blue;
227
raw[i*4+3] = img[i].alpha;
228
}
229
} else {
230
ERROR("Error invalid depth %d\n", depth);
231
size = -1;
232
}
233
234
return size;
235
}
236
237
int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth)
238
{
239
int size = (width * height * depth + 7) / 8;
240
INFO("Converting IA%d %dx%d to raw\n", depth, width, height);
241
memset(raw, 0, size);
242
243
switch (depth) {
244
case 16:
245
for (int i = 0; i < width * height; i++) {
246
raw[i*2] = img[i].intensity;
247
raw[i*2+1] = img[i].alpha;
248
}
249
break;
250
case 8:
251
for (int i = 0; i < width * height; i++) {
252
uint8_t val = SCALE_8_4(img[i].intensity);
253
uint8_t alpha = SCALE_8_4(img[i].alpha);
254
raw[i] = (val << 4) | alpha;
255
}
256
break;
257
case 4:
258
for (int i = 0; i < width * height; i++) {
259
uint8_t val = SCALE_8_3(img[i].intensity);
260
uint8_t alpha = img[i].alpha ? 0x01 : 0x00;
261
uint8_t old = raw[i/2];
262
if (i % 2) {
263
raw[i/2] = (old & 0xF0) | (val << 1) | alpha;
264
} else {
265
raw[i/2] = (old & 0x0F) | (((val << 1) | alpha) << 4);
266
}
267
}
268
break;
269
case 1:
270
for (int i = 0; i < width * height; i++) {
271
uint8_t val = img[i].intensity;
272
uint8_t old = raw[i/8];
273
uint8_t bit = 1 << (7 - (i % 8));
274
if (val) {
275
raw[i/8] = old | bit;
276
} else {
277
raw[i/8] = old & (~bit);
278
}
279
}
280
break;
281
default:
282
ERROR("Error invalid depth %d\n", depth);
283
size = -1;
284
break;
285
}
286
287
return size;
288
}
289
290
int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth)
291
{
292
int size = (width * height * depth + 7) / 8;
293
INFO("Converting I%d %dx%d to raw\n", depth, width, height);
294
memset(raw, 0, size);
295
296
switch (depth) {
297
case 8:
298
for (int i = 0; i < width * height; i++) {
299
raw[i] = img[i].intensity;
300
}
301
break;
302
case 4:
303
for (int i = 0; i < width * height; i++) {
304
uint8_t val = SCALE_8_4(img[i].intensity);
305
uint8_t old = raw[i/2];
306
if (i % 2) {
307
raw[i/2] = (old & 0xF0) | val;
308
} else {
309
raw[i/2] = (old & 0x0F) | (val << 4);
310
}
311
}
312
break;
313
default:
314
ERROR("Error invalid depth %d\n", depth);
315
size = -1;
316
break;
317
}
318
319
return size;
320
}
321
322
323
//---------------------------------------------------------
324
// internal RGBA/IA -> PNG
325
//---------------------------------------------------------
326
327
int rgba2png(const char *png_filename, const rgba *img, int width, int height)
328
{
329
int ret = 0;
330
INFO("Saving RGBA %dx%d to \"%s\"\n", width, height, png_filename);
331
332
// convert to format stb_image_write expects
333
uint8_t *data = malloc(4*width*height);
334
if (data) {
335
for (int j = 0; j < height; j++) {
336
for (int i = 0; i < width; i++) {
337
int idx = j*width + i;
338
data[4*idx] = img[idx].red;
339
data[4*idx + 1] = img[idx].green;
340
data[4*idx + 2] = img[idx].blue;
341
data[4*idx + 3] = img[idx].alpha;
342
}
343
}
344
345
ret = stbi_write_png(png_filename, width, height, 4, data, 0);
346
347
free(data);
348
}
349
350
return ret;
351
}
352
353
int ia2png(const char *png_filename, const ia *img, int width, int height)
354
{
355
int ret = 0;
356
INFO("Saving IA %dx%d to \"%s\"\n", width, height, png_filename);
357
358
// convert to format stb_image_write expects
359
uint8_t *data = malloc(2*width*height);
360
if (data) {
361
for (int j = 0; j < height; j++) {
362
for (int i = 0; i < width; i++) {
363
int idx = j*width + i;
364
data[2*idx] = img[idx].intensity;
365
data[2*idx + 1] = img[idx].alpha;
366
}
367
}
368
369
ret = stbi_write_png(png_filename, width, height, 2, data, 0);
370
371
free(data);
372
}
373
374
return ret;
375
}
376
377
//---------------------------------------------------------
378
// PNG -> internal RGBA/IA
379
//---------------------------------------------------------
380
381
rgba *png2rgba(const char *png_filename, int *width, int *height)
382
{
383
rgba *img = NULL;
384
int w = 0;
385
int h = 0;
386
int channels = 0;
387
int img_size;
388
389
stbi_uc *data = stbi_load(png_filename, &w, &h, &channels, STBI_default);
390
if (!data || w <= 0 || h <= 0) {
391
ERROR("Error loading \"%s\"\n", png_filename);
392
return NULL;
393
}
394
INFO("Read \"%s\" %dx%d channels: %d\n", png_filename, w, h, channels);
395
396
img_size = w * h * sizeof(*img);
397
img = malloc(img_size);
398
if (!img) {
399
ERROR("Error allocating %u bytes\n", img_size);
400
return NULL;
401
}
402
403
switch (channels) {
404
case 3: // red, green, blue
405
case 4: // red, green, blue, alpha
406
for (int j = 0; j < h; j++) {
407
for (int i = 0; i < w; i++) {
408
int idx = j*w + i;
409
img[idx].red = data[channels*idx];
410
img[idx].green = data[channels*idx + 1];
411
img[idx].blue = data[channels*idx + 2];
412
if (channels == 4) {
413
img[idx].alpha = data[channels*idx + 3];
414
} else {
415
img[idx].alpha = 0xFF;
416
}
417
}
418
}
419
break;
420
case 2: // grey, alpha
421
for (int j = 0; j < h; j++) {
422
for (int i = 0; i < w; i++) {
423
int idx = j*w + i;
424
img[idx].red = data[2*idx];
425
img[idx].green = data[2*idx];
426
img[idx].blue = data[2*idx];
427
img[idx].alpha = data[2*idx + 1];
428
}
429
}
430
break;
431
default:
432
ERROR("Don't know how to read channels: %d\n", channels);
433
free(img);
434
img = NULL;
435
}
436
437
// cleanup
438
stbi_image_free(data);
439
440
*width = w;
441
*height = h;
442
return img;
443
}
444
445
ia *png2ia(const char *png_filename, int *width, int *height)
446
{
447
ia *img = NULL;
448
int w = 0, h = 0;
449
int channels = 0;
450
int img_size;
451
452
stbi_uc *data = stbi_load(png_filename, &w, &h, &channels, STBI_default);
453
if (!data || w <= 0 || h <= 0) {
454
ERROR("Error loading \"%s\"\n", png_filename);
455
return NULL;
456
}
457
INFO("Read \"%s\" %dx%d channels: %d\n", png_filename, w, h, channels);
458
459
img_size = w * h * sizeof(*img);
460
img = malloc(img_size);
461
if (!img) {
462
ERROR("Error allocating %d bytes\n", img_size);
463
return NULL;
464
}
465
466
switch (channels) {
467
case 3: // red, green, blue
468
case 4: // red, green, blue, alpha
469
ERROR("Warning: averaging RGB PNG to create IA\n");
470
for (int j = 0; j < h; j++) {
471
for (int i = 0; i < w; i++) {
472
int idx = j*w + i;
473
int sum = data[channels*idx] + data[channels*idx + 1] + data[channels*idx + 2];
474
img[idx].intensity = (sum + 1) / 3; // add 1 to round up where appropriate
475
if (channels == 4) {
476
img[idx].alpha = data[channels*idx + 3];
477
} else {
478
img[idx].alpha = 0xFF;
479
}
480
}
481
}
482
break;
483
case 2: // grey, alpha
484
for (int j = 0; j < h; j++) {
485
for (int i = 0; i < w; i++) {
486
int idx = j*w + i;
487
img[idx].intensity = data[2*idx];
488
img[idx].alpha = data[2*idx + 1];
489
}
490
}
491
break;
492
default:
493
ERROR("Don't know how to read channels: %d\n", channels);
494
free(img);
495
img = NULL;
496
}
497
498
// cleanup
499
stbi_image_free(data);
500
501
*width = w;
502
*height = h;
503
return img;
504
}
505
506
// find index of palette color
507
// return -1 if not found
508
static int pal_find_color(const palette_t *pal, uint16_t val)
509
{
510
for (int i = 0; i < pal->used; i++) {
511
if (pal->data[i] == val) {
512
return i;
513
}
514
}
515
return -1;
516
}
517
518
// find value in palette, or add if not there
519
// returns palette index entered or -1 if palette full
520
static int pal_add_color(palette_t *pal, uint16_t val)
521
{
522
int idx;
523
idx = pal_find_color(pal, val);
524
if (idx < 0) {
525
if (pal->used == pal->max) {
526
ERROR("Error: trying to use more than %d\n", pal->max);
527
} else {
528
idx = pal->used;
529
pal->data[pal->used] = val;
530
pal->used++;
531
}
532
}
533
return idx;
534
}
535
536
// convert from raw (RGBA16 or IA16) format to CI + palette
537
// returns 1 on success
538
int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth)
539
{
540
// assign colors to palette
541
pal->used = 0;
542
memset(pal->data, 0, sizeof(pal->data));
543
int ci_idx = 0;
544
for (int i = 0; i < raw_len; i += sizeof(uint16_t)) {
545
uint16_t val = read_u16_be(&raw[i]);
546
int pal_idx = pal_add_color(pal, val);
547
if (pal_idx < 0) {
548
ERROR("Error adding color @ (%d): %d (used: %d/%d)\n", i, pal_idx, pal->used, pal->max);
549
return 0;
550
} else {
551
switch (ci_depth) {
552
case 8:
553
rawci[ci_idx] = (uint8_t)pal_idx;
554
break;
555
case 4:
556
{
557
int byte_idx = ci_idx / 2;
558
int nibble = 1 - (ci_idx % 2);
559
uint8_t mask = 0xF << (4 * (1 - nibble));
560
rawci[byte_idx] = (rawci[byte_idx] & mask) | (pal_idx << (4 * nibble));
561
break;
562
}
563
}
564
ci_idx++;
565
}
566
}
567
return 1;
568
}
569
570
const char *n64graphics_get_read_version(void)
571
{
572
return "stb_image 2.19";
573
}
574
575
const char *n64graphics_get_write_version(void)
576
{
577
return "stb_image_write 1.09";
578
}
579
580
#ifdef N64GRAPHICS_STANDALONE
581
#define N64GRAPHICS_VERSION "0.4"
582
#include <string.h>
583
584
typedef enum
585
{
586
MODE_EXPORT,
587
MODE_IMPORT,
588
} tool_mode;
589
590
typedef struct
591
{
592
char *img_filename;
593
char *bin_filename;
594
char *pal_filename;
595
tool_mode mode;
596
write_encoding encoding;
597
unsigned int bin_offset;
598
unsigned int pal_offset;
599
img_format format;
600
img_format pal_format;
601
int width;
602
int height;
603
int bin_truncate;
604
int pal_truncate;
605
} graphics_config;
606
607
static const graphics_config default_config =
608
{
609
.img_filename = NULL,
610
.bin_filename = NULL,
611
.pal_filename = NULL,
612
.mode = MODE_EXPORT,
613
.encoding = ENCODING_RAW,
614
.bin_offset = 0,
615
.pal_offset = 0,
616
.format = {IMG_FORMAT_RGBA, 16},
617
.pal_format = {IMG_FORMAT_RGBA, 16},
618
.width = 32,
619
.height = 32,
620
.bin_truncate = 1,
621
.pal_truncate = 1,
622
};
623
624
typedef struct
625
{
626
const char *name;
627
img_format format;
628
} format_entry;
629
630
static const format_entry format_table[] =
631
{
632
{"rgba16", {IMG_FORMAT_RGBA, 16}},
633
{"rgba32", {IMG_FORMAT_RGBA, 32}},
634
{"ia1", {IMG_FORMAT_IA, 1}},
635
{"ia4", {IMG_FORMAT_IA, 4}},
636
{"ia8", {IMG_FORMAT_IA, 8}},
637
{"ia16", {IMG_FORMAT_IA, 16}},
638
{"i4", {IMG_FORMAT_I, 4}},
639
{"i8", {IMG_FORMAT_I, 8}},
640
{"ci8", {IMG_FORMAT_CI, 8}},
641
{"ci4", {IMG_FORMAT_CI, 4}},
642
};
643
644
static const char *format2str(const img_format *format)
645
{
646
for (unsigned i = 0; i < DIM(format_table); i++) {
647
if (format->format == format_table[i].format.format && format->depth == format_table[i].format.depth) {
648
return format_table[i].name;
649
}
650
}
651
return "unknown";
652
}
653
654
static int parse_format(img_format *format, const char *str)
655
{
656
for (unsigned i = 0; i < DIM(format_table); i++) {
657
if (!strcasecmp(str, format_table[i].name)) {
658
format->format = format_table[i].format.format;
659
format->depth = format_table[i].format.depth;
660
return 1;
661
}
662
}
663
return 0;
664
}
665
666
typedef struct
667
{
668
const char *name;
669
write_encoding encoding;
670
} scheme_entry;
671
672
static const scheme_entry encoding_table[] =
673
{
674
{"raw", ENCODING_RAW},
675
{"u8", ENCODING_U8},
676
{"u16", ENCODING_U16},
677
{"u32", ENCODING_U32},
678
{"u64", ENCODING_U64},
679
};
680
681
static const char *encoding2str(write_encoding encoding)
682
{
683
for (unsigned i = 0; i < DIM(encoding_table); i++) {
684
if (encoding == encoding_table[i].encoding) {
685
return encoding_table[i].name;
686
}
687
}
688
return "unknown";
689
}
690
691
static int parse_encoding(write_encoding *encoding, const char *str)
692
{
693
for (unsigned i = 0; i < DIM(encoding_table); i++) {
694
if (!strcasecmp(str, encoding_table[i].name)) {
695
*encoding = encoding_table[i].encoding;
696
return 1;
697
}
698
}
699
return 0;
700
}
701
702
static void print_usage(void)
703
{
704
ERROR("Usage: n64graphics -e/-i BIN_FILE -g IMG_FILE [-p PAL_FILE] [-o BIN_OFFSET] [-P PAL_OFFSET] [-f FORMAT] [-c CI_FORMAT] [-w WIDTH] [-h HEIGHT] [-V]\n"
705
"\n"
706
"n64graphics v" N64GRAPHICS_VERSION ": N64 graphics manipulator\n"
707
"\n"
708
"Required arguments:\n"
709
" -e BIN_FILE export from BIN_FILE to PNG_FILE\n"
710
" -i BIN_FILE import from PNG_FILE to BIN_FILE\n"
711
" -g IMG_FILE graphics file to import/export (.png)\n"
712
"Optional arguments:\n"
713
" -o BIN_OFFSET starting offset in BIN_FILE (prevents truncation during import)\n"
714
" -f FORMAT texture format: rgba16, rgba32, ia1, ia4, ia8, ia16, i4, i8, ci4, ci8 (default: %s)\n"
715
" -s SCHEME output scheme: raw, u8 (hex), u64 (hex) (default: %s)\n"
716
" -w WIDTH export texture width (default: %d)\n"
717
" -h HEIGHT export texture height (default: %d)\n"
718
"CI arguments:\n"
719
" -c CI_FORMAT CI palette format: rgba16, ia16 (default: %s)\n"
720
" -p PAL_FILE palette binary file to import/export from/to\n"
721
" -P PAL_OFFSET starting offset in PAL_FILE (prevents truncation during import)\n"
722
"Other arguments:\n"
723
" -v verbose logging\n"
724
" -V print version information\n",
725
format2str(&default_config.format),
726
encoding2str(default_config.encoding),
727
default_config.width,
728
default_config.height,
729
format2str(&default_config.pal_format));
730
}
731
732
static void print_version(void)
733
{
734
ERROR("n64graphics v" N64GRAPHICS_VERSION ", using:\n"
735
" %s\n"
736
" %s\n",
737
n64graphics_get_read_version(), n64graphics_get_write_version());
738
}
739
740
// parse command line arguments
741
static int parse_arguments(int argc, char *argv[], graphics_config *config)
742
{
743
for (int i = 1; i < argc; i++) {
744
if (argv[i][0] == '-') {
745
switch (argv[i][1]) {
746
case 'c':
747
if (++i >= argc) return 0;
748
if (!parse_format(&config->pal_format, argv[i])) {
749
return 0;
750
}
751
break;
752
case 'e':
753
if (++i >= argc) return 0;
754
config->bin_filename = argv[i];
755
config->mode = MODE_EXPORT;
756
break;
757
case 'f':
758
if (++i >= argc) return 0;
759
if (!parse_format(&config->format, argv[i])) {
760
return 0;
761
}
762
break;
763
case 'g':
764
if (++i >= argc) return 0;
765
config->img_filename = argv[i];
766
break;
767
case 'h':
768
if (++i >= argc) return 0;
769
config->height = strtoul(argv[i], NULL, 0);
770
break;
771
case 'i':
772
if (++i >= argc) return 0;
773
config->bin_filename = argv[i];
774
config->mode = MODE_IMPORT;
775
break;
776
case 'o':
777
if (++i >= argc) return 0;
778
config->bin_offset = strtoul(argv[i], NULL, 0);
779
config->bin_truncate = 0;
780
break;
781
case 'p':
782
if (++i >= argc) return 0;
783
config->pal_filename = argv[i];
784
break;
785
case 'P':
786
if (++i >= argc) return 0;
787
config->pal_offset = strtoul(argv[i], NULL, 0);
788
config->pal_truncate = 0;
789
break;
790
case 's':
791
if (++i >= argc) return 0;
792
if (!parse_encoding(&config->encoding, argv[i])) {
793
return 0;
794
}
795
break;
796
case 'v':
797
g_verbosity = 1;
798
break;
799
case 'V':
800
print_version();
801
exit(0);
802
break;
803
case 'w':
804
if (++i >= argc) return 0;
805
config->width = strtoul(argv[i], NULL, 0);
806
break;
807
default:
808
return 0;
809
break;
810
}
811
} else {
812
return 0;
813
}
814
}
815
return 1;
816
}
817
818
// returns 1 if config is valid
819
static int valid_config(const graphics_config *config)
820
{
821
if (!config->bin_filename || !config->img_filename) {
822
return 0;
823
}
824
if (config->format.format == IMG_FORMAT_CI) {
825
if (!config->pal_filename || (config->pal_format.depth != 16) ||
826
(config->pal_format.format != IMG_FORMAT_RGBA && config->pal_format.format != IMG_FORMAT_IA)) {
827
return 0;
828
}
829
}
830
return 1;
831
}
832
833
int main(int argc, char *argv[])
834
{
835
graphics_config config = default_config;
836
rgba *imgr;
837
ia *imgi;
838
FILE *bin_fp;
839
uint8_t *raw;
840
int raw_size;
841
int length = 0;
842
int flength;
843
int res;
844
845
int valid = parse_arguments(argc, argv, &config);
846
if (!valid || !valid_config(&config)) {
847
print_usage();
848
exit(EXIT_FAILURE);
849
}
850
851
if (config.mode == MODE_IMPORT) {
852
if (0 == strcmp("-", config.bin_filename)) {
853
bin_fp = stdout;
854
} else {
855
if (config.bin_truncate) {
856
bin_fp = fopen(config.bin_filename, "wb");
857
} else {
858
bin_fp = fopen(config.bin_filename, "r+b");
859
}
860
}
861
if (!bin_fp) {
862
ERROR("Error opening \"%s\"\n", config.bin_filename);
863
return -1;
864
}
865
if (!config.bin_truncate) {
866
fseek(bin_fp, config.bin_offset, SEEK_SET);
867
}
868
switch (config.format.format) {
869
case IMG_FORMAT_RGBA:
870
imgr = png2rgba(config.img_filename, &config.width, &config.height);
871
raw_size = (config.width * config.height * config.format.depth + 7) / 8;
872
raw = malloc(raw_size);
873
if (!raw) {
874
ERROR("Error allocating %u bytes\n", raw_size);
875
}
876
length = rgba2raw(raw, imgr, config.width, config.height, config.format.depth);
877
break;
878
case IMG_FORMAT_IA:
879
imgi = png2ia(config.img_filename, &config.width, &config.height);
880
raw_size = (config.width * config.height * config.format.depth + 7) / 8;
881
raw = malloc(raw_size);
882
if (!raw) {
883
ERROR("Error allocating %u bytes\n", raw_size);
884
}
885
length = ia2raw(raw, imgi, config.width, config.height, config.format.depth);
886
break;
887
case IMG_FORMAT_I:
888
imgi = png2ia(config.img_filename, &config.width, &config.height);
889
raw_size = (config.width * config.height * config.format.depth + 7) / 8;
890
raw = malloc(raw_size);
891
if (!raw) {
892
ERROR("Error allocating %u bytes\n", raw_size);
893
}
894
length = i2raw(raw, imgi, config.width, config.height, config.format.depth);
895
break;
896
case IMG_FORMAT_CI:
897
{
898
palette_t pal = {0};
899
FILE *pal_fp;
900
uint8_t *raw16;
901
int raw16_size;
902
int raw16_length;
903
uint8_t *ci;
904
int ci_length;
905
int pal_success;
906
int pal_length;
907
908
if (config.pal_truncate) {
909
pal_fp = fopen(config.pal_filename, "wb");
910
} else {
911
pal_fp = fopen(config.pal_filename, "r+b");
912
}
913
if (!pal_fp) {
914
ERROR("Error opening \"%s\"\n", config.pal_filename);
915
return EXIT_FAILURE;
916
}
917
if (!config.pal_truncate) {
918
fseek(pal_fp, config.bin_offset, SEEK_SET);
919
}
920
921
raw16_size = config.width * config.height * config.pal_format.depth / 8;
922
raw16 = malloc(raw16_size);
923
if (!raw16) {
924
ERROR("Error allocating %d bytes\n", raw16_size);
925
return EXIT_FAILURE;
926
}
927
switch (config.pal_format.format) {
928
case IMG_FORMAT_RGBA:
929
imgr = png2rgba(config.img_filename, &config.width, &config.height);
930
raw16_length = rgba2raw(raw16, imgr, config.width, config.height, config.pal_format.depth);
931
break;
932
case IMG_FORMAT_IA:
933
imgi = png2ia(config.img_filename, &config.width, &config.height);
934
raw16_length = ia2raw(raw16, imgi, config.width, config.height, config.pal_format.depth);
935
break;
936
default:
937
ERROR("Unsupported palette format: %s\n", format2str(&config.pal_format));
938
exit(EXIT_FAILURE);
939
}
940
941
// convert raw to palette
942
pal.max = (1 << config.format.depth);
943
ci_length = config.width * config.height * config.format.depth / 8;
944
ci = malloc(ci_length);
945
pal_success = raw2ci(ci, &pal, raw16, raw16_length, config.format.depth);
946
if (!pal_success) {
947
ERROR("Error converting palette\n");
948
exit(EXIT_FAILURE);
949
}
950
951
// pack the bytes
952
uint8_t raw_pal[sizeof(pal.data)];
953
for (int i = 0; i < pal.max; i++) {
954
write_u16_be(&raw_pal[2*i], pal.data[i]);
955
}
956
pal_length = pal.max * sizeof(pal.data[0]);
957
INFO("Writing 0x%X bytes to offset 0x%X of \"%s\"\n", pal_length, config.pal_offset, config.pal_filename);
958
flength = fprint_write_output(pal_fp, config.encoding, raw_pal, pal_length);
959
if (config.encoding == ENCODING_RAW && flength != pal_length) {
960
ERROR("Error writing %d bytes to \"%s\"\n", pal_length, config.pal_filename);
961
}
962
INFO("Wrote 0x%X bytes to \"%s\"\n", flength, config.pal_filename);
963
964
raw = ci;
965
length = ci_length;
966
967
free(raw16);
968
fclose(pal_fp);
969
break;
970
}
971
default:
972
return EXIT_FAILURE;
973
}
974
if (length <= 0) {
975
ERROR("Error converting to raw format\n");
976
return EXIT_FAILURE;
977
}
978
INFO("Writing 0x%X bytes to offset 0x%X of \"%s\"\n", length, config.bin_offset, config.bin_filename);
979
flength = fprint_write_output(bin_fp, config.encoding, raw, length);
980
if (config.encoding == ENCODING_RAW && flength != length) {
981
ERROR("Error writing %d bytes to \"%s\"\n", length, config.bin_filename);
982
}
983
INFO("Wrote 0x%X bytes to \"%s\"\n", flength, config.bin_filename);
984
if (bin_fp != stdout) {
985
fclose(bin_fp);
986
}
987
988
} else {
989
if (config.width <= 0 || config.height <= 0 || config.format.depth <= 0) {
990
ERROR("Error: must set position width and height for export\n");
991
return EXIT_FAILURE;
992
}
993
bin_fp = fopen(config.bin_filename, "rb");
994
if (!bin_fp) {
995
ERROR("Error opening \"%s\"\n", config.bin_filename);
996
return -1;
997
}
998
raw_size = (config.width * config.height * config.format.depth + 7) / 8;
999
raw = malloc(raw_size);
1000
if (config.bin_offset > 0) {
1001
fseek(bin_fp, config.bin_offset, SEEK_SET);
1002
}
1003
flength = fread(raw, 1, raw_size, bin_fp);
1004
if (flength != raw_size) {
1005
ERROR("Error reading %d bytes from \"%s\"\n", raw_size, config.bin_filename);
1006
}
1007
switch (config.format.format) {
1008
case IMG_FORMAT_RGBA:
1009
imgr = raw2rgba(raw, config.width, config.height, config.format.depth);
1010
res = rgba2png(config.img_filename, imgr, config.width, config.height);
1011
break;
1012
case IMG_FORMAT_IA:
1013
imgi = raw2ia(raw, config.width, config.height, config.format.depth);
1014
res = ia2png(config.img_filename, imgi, config.width, config.height);
1015
break;
1016
case IMG_FORMAT_I:
1017
imgi = raw2i(raw, config.width, config.height, config.format.depth);
1018
res = ia2png(config.img_filename, imgi, config.width, config.height);
1019
break;
1020
case IMG_FORMAT_CI:
1021
{
1022
FILE *pal_fp;
1023
uint8_t *pal;
1024
uint8_t *raw_fmt;
1025
int pal_size;
1026
1027
INFO("Extracting %s offset 0x%X, pal.offset 0x%0X, pal.format %s\n", format2str(&config.format),
1028
config.bin_offset, config.pal_offset, format2str(&config.pal_format));
1029
1030
pal_fp = fopen(config.pal_filename, "rb");
1031
if (!pal_fp) {
1032
ERROR("Error opening \"%s\"\n", config.bin_filename);
1033
return EXIT_FAILURE;
1034
}
1035
if (config.pal_offset > 0) {
1036
fseek(pal_fp, config.pal_offset, SEEK_SET);
1037
}
1038
1039
pal_size = sizeof(uint16_t) * (1 << config.format.depth);
1040
INFO("Palette size: %d\n", pal_size);
1041
pal = malloc(pal_size);
1042
flength = fread(pal, 1, pal_size, pal_fp);
1043
if (flength != pal_size) {
1044
ERROR("Error reading %d bytes from \"%s\"\n", pal_size, config.pal_filename);
1045
}
1046
raw_fmt = ci2raw(raw, pal, config.width, config.height, config.format.depth);
1047
switch (config.pal_format.format) {
1048
case IMG_FORMAT_RGBA:
1049
INFO("Converting raw to RGBA16\n");
1050
imgr = raw2rgba(raw_fmt, config.width, config.height, config.pal_format.depth);
1051
res = rgba2png(config.img_filename, imgr, config.width, config.height);
1052
break;
1053
case IMG_FORMAT_IA:
1054
INFO("Converting raw to IA16\n");
1055
imgi = raw2ia(raw_fmt, config.width, config.height, config.pal_format.depth);
1056
res = ia2png(config.img_filename, imgi, config.width, config.height);
1057
break;
1058
default:
1059
ERROR("Unsupported palette format: %s\n", format2str(&config.pal_format));
1060
return EXIT_FAILURE;
1061
}
1062
free(raw_fmt);
1063
free(pal);
1064
break;
1065
}
1066
default:
1067
return EXIT_FAILURE;
1068
}
1069
if (!res) {
1070
ERROR("Error writing to \"%s\"\n", config.img_filename);
1071
return EXIT_FAILURE;
1072
}
1073
}
1074
1075
return EXIT_SUCCESS;
1076
}
1077
#endif // N64GRAPHICS_STANDALONE
1078
1079