n64graphics.c (32751B)
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