diff --git a/tools/n64graphics/n64graphics.c b/tools/n64graphics/n64graphics.c deleted file mode 100644 index 4a16e539..00000000 --- a/tools/n64graphics/n64graphics.c +++ /dev/null @@ -1,1021 +0,0 @@ -#include -#include -#include - -#define STBI_NO_LINEAR -#define STBI_NO_HDR -#define STBI_NO_TGA -#define STB_IMAGE_IMPLEMENTATION -#include "../stb/stb_image.h" -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "../stb/stb_image_write.h" - -#include "n64graphics.h" -#include "utils.h" - -// SCALE_M_N: upscale/downscale M-bit integer to N-bit -#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F) -#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF) -#define SCALE_4_8(VAL_) ((VAL_) * 0x11) -#define SCALE_8_4(VAL_) ((VAL_) / 0x11) -#define SCALE_3_8(VAL_) ((VAL_) * 0x24) -#define SCALE_8_3(VAL_) ((VAL_) / 0x24) - - -typedef struct -{ - enum - { - IMG_FORMAT_RGBA, - IMG_FORMAT_IA, - IMG_FORMAT_I, - IMG_FORMAT_CI, - } format; - int depth; -} img_format; - -//--------------------------------------------------------- -// N64 RGBA/IA/I/CI -> internal RGBA/IA -//--------------------------------------------------------- - -rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth) -{ - rgba *img; - int img_size; - - img_size = width * height * sizeof(*img); - img = (rgba *)malloc(img_size); - if (!img) { - ERROR("Error allocating %d bytes\n", img_size); - return NULL; - } - - if (depth == 16) { - for (int i = 0; i < width * height; i++) { - img[i].red = SCALE_5_8((raw[i*2] & 0xF8) >> 3); - img[i].green = SCALE_5_8(((raw[i*2] & 0x07) << 2) | ((raw[i*2+1] & 0xC0) >> 6)); - img[i].blue = SCALE_5_8((raw[i*2+1] & 0x3E) >> 1); - img[i].alpha = (raw[i*2+1] & 0x01) ? 0xFF : 0x00; - } - } else if (depth == 32) { - for (int i = 0; i < width * height; i++) { - img[i].red = raw[i*4]; - img[i].green = raw[i*4+1]; - img[i].blue = raw[i*4+2]; - img[i].alpha = raw[i*4+3]; - } - } - - return img; -} - -ia *raw2ia(const uint8_t *raw, int width, int height, int depth) -{ - ia *img; - int img_size; - - img_size = width * height * sizeof(*img); - img = (ia *)malloc(img_size); - if (!img) { - ERROR("Error allocating %u bytes\n", img_size); - return NULL; - } - - switch (depth) { - case 16: - for (int i = 0; i < width * height; i++) { - img[i].intensity = raw[i*2]; - img[i].alpha = raw[i*2+1]; - } - break; - case 8: - for (int i = 0; i < width * height; i++) { - img[i].intensity = SCALE_4_8((raw[i] & 0xF0) >> 4); - img[i].alpha = SCALE_4_8(raw[i] & 0x0F); - } - break; - case 4: - for (int i = 0; i < width * height; i++) { - uint8_t bits; - bits = raw[i/2]; - if (i % 2) { - bits &= 0xF; - } else { - bits >>= 4; - } - img[i].intensity = SCALE_3_8((bits >> 1) & 0x07); - img[i].alpha = (bits & 0x01) ? 0xFF : 0x00; - } - break; - case 1: - for (int i = 0; i < width * height; i++) { - uint8_t bits; - uint8_t mask; - bits = raw[i/8]; - mask = 1 << (7 - (i % 8)); // MSb->LSb - bits = (bits & mask) ? 0xFF : 0x00; - img[i].intensity = bits; - img[i].alpha = bits; - } - break; - default: - ERROR("Error invalid depth %d\n", depth); - break; - } - - return img; -} - -ia *raw2i(const uint8_t *raw, int width, int height, int depth) -{ - ia *img = NULL; - int img_size; - - img_size = width * height * sizeof(*img); - img = (ia *)malloc(img_size); - if (!img) { - ERROR("Error allocating %u bytes\n", img_size); - return NULL; - } - - switch (depth) { - case 8: - for (int i = 0; i < width * height; i++) { - img[i].intensity = raw[i]; - img[i].alpha = 0xFF; - } - break; - case 4: - for (int i = 0; i < width * height; i++) { - uint8_t bits; - bits = raw[i/2]; - if (i % 2) { - bits &= 0xF; - } else { - bits >>= 4; - } - img[i].intensity = SCALE_4_8(bits); - img[i].alpha = img[i].intensity; // alpha copy intensity - // TODO: modes - // img[i].alpha = 0xFF; // alpha = 1 - // img[i].alpha = img[i].intensity ? 0xFF : 0x00; // binary - } - break; - default: - ERROR("Error invalid depth %d\n", depth); - break; - } - - return img; -} - -// convert CI raw data and palette to raw data (either RGBA16 or IA16) -uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth) -{ - uint8_t *raw; - int raw_size; - - // first convert to raw RGBA - raw_size = sizeof(uint16_t) * width * height; - raw = (uint8_t *)malloc(raw_size); - if (!raw) { - ERROR("Error allocating %u bytes\n", raw_size); - return NULL; - } - - for (int i = 0; i < width * height; i++) { - int pal_idx = rawci[i]; - if (ci_depth == 4) { - int byte_idx = i / 2; - int nibble = 1 - (i % 2); - int shift = 4 * nibble; - pal_idx = (rawci[byte_idx] >> shift) & 0xF; - } - raw[2*i] = palette[2*pal_idx]; - raw[2*i+1] = palette[2*pal_idx+1]; - } - - return raw; -} - - -//--------------------------------------------------------- -// internal RGBA/IA -> N64 RGBA/IA/I/CI -// returns length written to 'raw' used or -1 on error -//--------------------------------------------------------- - -int rgba2raw(uint8_t *raw, const rgba *img, int width, int height, int depth) -{ - int size = (width * height * depth + 7) / 8; - INFO("Converting RGBA%d %dx%d to raw\n", depth, width, height); - - if (depth == 16) { - for (int i = 0; i < width * height; i++) { - uint8_t r, g, b, a; - r = SCALE_8_5(img[i].red); - g = SCALE_8_5(img[i].green); - b = SCALE_8_5(img[i].blue); - a = img[i].alpha ? 0x1 : 0x0; - raw[i*2] = (r << 3) | (g >> 2); - raw[i*2+1] = ((g & 0x3) << 6) | (b << 1) | a; - } - } else if (depth == 32) { - for (int i = 0; i < width * height; i++) { - raw[i*4] = img[i].red; - raw[i*4+1] = img[i].green; - raw[i*4+2] = img[i].blue; - raw[i*4+3] = img[i].alpha; - } - } else { - ERROR("Error invalid depth %d\n", depth); - size = -1; - } - - return size; -} - -int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth) -{ - int size = (width * height * depth + 7) / 8; - INFO("Converting IA%d %dx%d to raw\n", depth, width, height); - - switch (depth) { - case 16: - for (int i = 0; i < width * height; i++) { - raw[i*2] = img[i].intensity; - raw[i*2+1] = img[i].alpha; - } - break; - case 8: - for (int i = 0; i < width * height; i++) { - uint8_t val = SCALE_8_4(img[i].intensity); - uint8_t alpha = SCALE_8_4(img[i].alpha); - raw[i] = (val << 4) | alpha; - } - break; - case 4: - for (int i = 0; i < width * height; i++) { - uint8_t val = SCALE_8_3(img[i].intensity); - uint8_t alpha = img[i].alpha ? 0x01 : 0x00; - uint8_t old = raw[i/2]; - if (i % 2) { - raw[i/2] = (old & 0xF0) | (val << 1) | alpha; - } else { - raw[i/2] = (old & 0x0F) | (((val << 1) | alpha) << 4); - } - } - break; - case 1: - for (int i = 0; i < width * height; i++) { - uint8_t val = img[i].intensity; - uint8_t old = raw[i/8]; - uint8_t bit = 1 << (7 - (i % 8)); - if (val) { - raw[i/8] = old | bit; - } else { - raw[i/8] = old & (~bit); - } - } - break; - default: - ERROR("Error invalid depth %d\n", depth); - size = -1; - break; - } - - return size; -} - -int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth) -{ - int size = (width * height * depth + 7) / 8; - INFO("Converting I%d %dx%d to raw\n", depth, width, height); - - switch (depth) { - case 8: - for (int i = 0; i < width * height; i++) { - raw[i] = img[i].intensity; - } - break; - case 4: - for (int i = 0; i < width * height; i++) { - uint8_t val = SCALE_8_4(img[i].intensity); - uint8_t old = raw[i/2]; - if (i % 2) { - raw[i/2] = (old & 0xF0) | val; - } else { - raw[i/2] = (old & 0x0F) | (val << 4); - } - } - break; - default: - ERROR("Error invalid depth %d\n", depth); - size = -1; - break; - } - - return size; -} - - -//--------------------------------------------------------- -// internal RGBA/IA -> PNG -//--------------------------------------------------------- - -int rgba2png(const char *png_filename, const rgba *img, int width, int height) -{ - int ret = 0; - INFO("Saving RGBA %dx%d to \"%s\"\n", width, height, png_filename); - - // convert to format stb_image_write expects - uint8_t *data = (uint8_t *)malloc(4*width*height); - if (data) { - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - int idx = j*width + i; - data[4*idx] = img[idx].red; - data[4*idx + 1] = img[idx].green; - data[4*idx + 2] = img[idx].blue; - data[4*idx + 3] = img[idx].alpha; - } - } - - ret = stbi_write_png(png_filename, width, height, 4, data, 0); - - free(data); - } - - return ret; -} - -int ia2png(const char *png_filename, const ia *img, int width, int height) -{ - int ret = 0; - INFO("Saving IA %dx%d to \"%s\"\n", width, height, png_filename); - - // convert to format stb_image_write expects - uint8_t *data = (uint8_t *)malloc(2*width*height); - if (data) { - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - int idx = j*width + i; - data[2*idx] = img[idx].intensity; - data[2*idx + 1] = img[idx].alpha; - } - } - - ret = stbi_write_png(png_filename, width, height, 2, data, 0); - - free(data); - } - - return ret; -} - -//--------------------------------------------------------- -// PNG -> internal RGBA/IA -//--------------------------------------------------------- - -rgba *png2rgba(const char *png_filename, int *width, int *height) -{ - rgba *img = NULL; - int w = 0; - int h = 0; - int channels = 0; - int img_size; - - stbi_uc *data = stbi_load(png_filename, &w, &h, &channels, STBI_default); - if (!data || w <= 0 || h <= 0) { - ERROR("Error loading \"%s\"\n", png_filename); - return NULL; - } - INFO("Read \"%s\" %dx%d channels: %d\n", png_filename, w, h, channels); - - img_size = w * h * sizeof(*img); - img = (rgba *)malloc(img_size); - if (!img) { - ERROR("Error allocating %u bytes\n", img_size); - return NULL; - } - - switch (channels) { - case 3: // red, green, blue - case 4: // red, green, blue, alpha - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - img[idx].red = data[channels*idx]; - img[idx].green = data[channels*idx + 1]; - img[idx].blue = data[channels*idx + 2]; - if (channels == 4) { - img[idx].alpha = data[channels*idx + 3]; - } else { - img[idx].alpha = 0xFF; - } - } - } - break; - case 2: // grey, alpha - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - img[idx].red = data[2*idx]; - img[idx].green = data[2*idx]; - img[idx].blue = data[2*idx]; - img[idx].alpha = data[2*idx + 1]; - } - } - break; - default: - ERROR("Don't know how to read channels: %d\n", channels); - free(img); - img = NULL; - } - - // cleanup - stbi_image_free(data); - - *width = w; - *height = h; - return img; -} - -ia *png2ia(const char *png_filename, int *width, int *height) -{ - ia *img = NULL; - int w = 0, h = 0; - int channels = 0; - int img_size; - - stbi_uc *data = stbi_load(png_filename, &w, &h, &channels, STBI_default); - if (!data || w <= 0 || h <= 0) { - ERROR("Error loading \"%s\"\n", png_filename); - return NULL; - } - INFO("Read \"%s\" %dx%d channels: %d\n", png_filename, w, h, channels); - - img_size = w * h * sizeof(*img); - img = (ia *)malloc(img_size); - if (!img) { - ERROR("Error allocating %d bytes\n", img_size); - return NULL; - } - - switch (channels) { - case 3: // red, green, blue - case 4: // red, green, blue, alpha - ERROR("Warning: averaging RGB PNG to create IA\n"); - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - int sum = data[channels*idx] + data[channels*idx + 1] + data[channels*idx + 2]; - img[idx].intensity = (sum + 1) / 3; // add 1 to round up where appropriate - if (channels == 4) { - img[idx].alpha = data[channels*idx + 3]; - } else { - img[idx].alpha = 0xFF; - } - } - } - break; - case 2: // grey, alpha - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int idx = j*w + i; - img[idx].intensity = data[2*idx]; - img[idx].alpha = data[2*idx + 1]; - } - } - break; - default: - ERROR("Don't know how to read channels: %d\n", channels); - free(img); - img = NULL; - } - - // cleanup - stbi_image_free(data); - - *width = w; - *height = h; - return img; -} - -// find index of palette color -// return -1 if not found -static int pal_find_color(const palette_t *pal, uint16_t val) -{ - for (int i = 0; i < pal->used; i++) { - if (pal->data[i] == val) { - return i; - } - } - return -1; -} - -// find value in palette, or add if not there -// returns palette index entered or -1 if palette full -static int pal_add_color(palette_t *pal, uint16_t val) -{ - int idx; - idx = pal_find_color(pal, val); - if (idx < 0) { - if (pal->used == pal->max) { - ERROR("Error: trying to use more than %d\n", pal->max); - } else { - idx = pal->used; - pal->data[pal->used] = val; - pal->used++; - } - } - return idx; -} - -// convert from raw (RGBA16 or IA16) format to CI + palette -// returns 1 on success -int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth) -{ - // assign colors to palette - pal->used = 0; - memset(pal->data, 0, sizeof(pal->data)); - int ci_idx = 0; - for (int i = 0; i < raw_len; i += sizeof(uint16_t)) { - uint16_t val = read_u16_be(&raw[i]); - int pal_idx = pal_add_color(pal, val); - if (pal_idx < 0) { - ERROR("Error adding color @ (%d): %d (used: %d/%d)\n", i, pal_idx, pal->used, pal->max); - return 0; - } else { - switch (ci_depth) { - case 8: - rawci[ci_idx] = (uint8_t)pal_idx; - break; - case 4: - { - int byte_idx = ci_idx / 2; - int nibble = 1 - (ci_idx % 2); - uint8_t mask = 0xF << (4 * (1 - nibble)); - rawci[byte_idx] = (rawci[byte_idx] & mask) | (pal_idx << (4 * nibble)); - break; - } - } - ci_idx++; - } - } - return 1; -} - -const char *n64graphics_get_read_version(void) -{ - return "stb_image 2.19"; -} - -const char *n64graphics_get_write_version(void) -{ - return "stb_image_write 1.09"; -} - -#ifdef N64GRAPHICS_STANDALONE -#define N64GRAPHICS_VERSION "0.4" -#include - -typedef enum -{ - MODE_EXPORT, - MODE_IMPORT, -} tool_mode; - -typedef struct -{ - char *img_filename; - char *bin_filename; - char *pal_filename; - tool_mode mode; - unsigned int bin_offset; - unsigned int pal_offset; - img_format format; - img_format pal_format; - int width; - int height; - int bin_truncate; - int pal_truncate; -} graphics_config; - -static const graphics_config default_config = -{ - .img_filename = NULL, - .bin_filename = NULL, - .pal_filename = NULL, - .mode = MODE_EXPORT, - .bin_offset = 0, - .pal_offset = 0, - .format = {IMG_FORMAT_RGBA, 16}, - .pal_format = {IMG_FORMAT_RGBA, 16}, - .width = 32, - .height = 32, - .bin_truncate = 1, - .pal_truncate = 1, -}; - -typedef struct -{ - const char *name; - img_format format; -} format_entry; - -static const format_entry format_table[] = -{ - {"rgba16", {IMG_FORMAT_RGBA, 16}}, - {"rgba32", {IMG_FORMAT_RGBA, 32}}, - {"ia1", {IMG_FORMAT_IA, 1}}, - {"ia4", {IMG_FORMAT_IA, 4}}, - {"ia8", {IMG_FORMAT_IA, 8}}, - {"ia16", {IMG_FORMAT_IA, 16}}, - {"i4", {IMG_FORMAT_I, 4}}, - {"i8", {IMG_FORMAT_I, 8}}, - {"ci8", {IMG_FORMAT_CI, 8}}, - {"ci4", {IMG_FORMAT_CI, 4}}, -}; - -static const char *format2str(const img_format *format) -{ - for (unsigned i = 0; i < DIM(format_table); i++) { - if (format->format == format_table[i].format.format && format->depth == format_table[i].format.depth) { - return format_table[i].name; - } - } - return "unknown"; -} - -static int parse_format(img_format *format, const char *str) -{ - for (unsigned i = 0; i < DIM(format_table); i++) { - if (!strcasecmp(str, format_table[i].name)) { - format->format = format_table[i].format.format; - format->depth = format_table[i].format.depth; - return 1; - } - } - return 0; -} - -static void print_usage(void) -{ - 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" - "\n" - "n64graphics v" N64GRAPHICS_VERSION ": N64 graphics manipulator\n" - "\n" - "Required arguments:\n" - " -e BIN_FILE export from BIN_FILE to PNG_FILE\n" - " -i BIN_FILE import from PNG_FILE to BIN_FILE\n" - " -g IMG_FILE graphics file to import/export (.png)\n" - "Optional arguments:\n" - " -o BIN_OFFSET starting offset in BIN_FILE (prevents truncation during import)\n" - " -f FORMAT texture format: rgba16, rgba32, ia1, ia4, ia8, ia16, i4, i8, ci4, ci8 (default: %s)\n" - " -w WIDTH export texture width (default: %d)\n" - " -h HEIGHT export texture height (default: %d)\n" - "CI arguments:\n" - " -c CI_FORMAT CI palette format: rgba16, ia16 (default: %s)\n" - " -p PAL_FILE palette binary file to import/export from/to\n" - " -P PAL_OFFSET starting offset in PAL_FILE (prevents truncation during import)\n" - "Other arguments:\n" - " -v verbose logging\n" - " -V print version information\n", - format2str(&default_config.format), - default_config.width, - default_config.height, - format2str(&default_config.pal_format)); -} - -static void print_version(void) -{ - ERROR("n64graphics v" N64GRAPHICS_VERSION ", using:\n" - " %s\n" - " %s\n", - n64graphics_get_read_version(), n64graphics_get_write_version()); -} - -// parse command line arguments -static int parse_arguments(int argc, char *argv[], graphics_config *config) -{ - for (int i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - switch (argv[i][1]) { - case 'c': - if (++i >= argc) return 0; - if (!parse_format(&config->pal_format, argv[i])) { - return 0; - } - break; - case 'e': - if (++i >= argc) return 0; - config->bin_filename = argv[i]; - config->mode = MODE_EXPORT; - break; - case 'f': - if (++i >= argc) return 0; - if (!parse_format(&config->format, argv[i])) { - return 0; - } - break; - case 'g': - if (++i >= argc) return 0; - config->img_filename = argv[i]; - break; - case 'h': - if (++i >= argc) return 0; - config->height = strtoul(argv[i], NULL, 0); - break; - case 'i': - if (++i >= argc) return 0; - config->bin_filename = argv[i]; - config->mode = MODE_IMPORT; - break; - case 'o': - if (++i >= argc) return 0; - config->bin_offset = strtoul(argv[i], NULL, 0); - config->bin_truncate = 0; - break; - case 'p': - if (++i >= argc) return 0; - config->pal_filename = argv[i]; - break; - case 'P': - if (++i >= argc) return 0; - config->pal_offset = strtoul(argv[i], NULL, 0); - config->pal_truncate = 0; - break; - case 'v': - g_verbosity = 1; - break; - case 'V': - print_version(); - exit(0); - break; - case 'w': - if (++i >= argc) return 0; - config->width = strtoul(argv[i], NULL, 0); - break; - default: - return 0; - break; - } - } else { - return 0; - } - } - return 1; -} - -// returns 1 if config is valid -static int valid_config(const graphics_config *config) -{ - if (!config->bin_filename || !config->img_filename) { - return 0; - } - if (config->format.format == IMG_FORMAT_CI) { - if (!config->pal_filename || (config->pal_format.depth != 16) || - (config->pal_format.format != IMG_FORMAT_RGBA && config->pal_format.format != IMG_FORMAT_IA)) { - return 0; - } - } - return 1; -} - -int main(int argc, char *argv[]) -{ - graphics_config config = default_config; - rgba *imgr; - ia *imgi; - FILE *bin_fp; - uint8_t *raw; - int raw_size; - int length = 0; - int flength; - int res; - - int valid = parse_arguments(argc, argv, &config); - if (!valid || !valid_config(&config)) { - print_usage(); - exit(EXIT_FAILURE); - } - - if (config.mode == MODE_IMPORT) { - if (config.bin_truncate) { - bin_fp = fopen(config.bin_filename, "w"); - } else { - bin_fp = fopen(config.bin_filename, "r+"); - } - if (!bin_fp) { - ERROR("Error opening \"%s\"\n", config.bin_filename); - return -1; - } - if (!config.bin_truncate) { - fseek(bin_fp, config.bin_offset, SEEK_SET); - } - switch (config.format.format) { - case IMG_FORMAT_RGBA: - imgr = png2rgba(config.img_filename, &config.width, &config.height); - raw_size = (config.width * config.height * config.format.depth + 7) / 8; - raw = (uint8_t *)malloc(raw_size); - if (!raw) { - ERROR("Error allocating %u bytes\n", raw_size); - } - length = rgba2raw(raw, imgr, config.width, config.height, config.format.depth); - break; - case IMG_FORMAT_IA: - imgi = png2ia(config.img_filename, &config.width, &config.height); - raw_size = (config.width * config.height * config.format.depth + 7) / 8; - raw = (uint8_t *)malloc(raw_size); - if (!raw) { - ERROR("Error allocating %u bytes\n", raw_size); - } - length = ia2raw(raw, imgi, config.width, config.height, config.format.depth); - break; - case IMG_FORMAT_I: - imgi = png2ia(config.img_filename, &config.width, &config.height); - raw_size = (config.width * config.height * config.format.depth + 7) / 8; - raw = (uint8_t *)malloc(raw_size); - if (!raw) { - ERROR("Error allocating %u bytes\n", raw_size); - } - length = i2raw(raw, imgi, config.width, config.height, config.format.depth); - break; - case IMG_FORMAT_CI: - { - palette_t pal; - FILE *pal_fp; - uint8_t *raw16; - int raw16_size; - int raw16_length; - uint8_t *ci; - int ci_length; - int pal_success; - int pal_length; - - if (config.pal_truncate) { - pal_fp = fopen(config.pal_filename, "w"); - } else { - pal_fp = fopen(config.pal_filename, "r+"); - } - if (!pal_fp) { - ERROR("Error opening \"%s\"\n", config.pal_filename); - return EXIT_FAILURE; - } - if (!config.pal_truncate) { - fseek(pal_fp, config.bin_offset, SEEK_SET); - } - - raw16_size = config.width * config.height * config.pal_format.depth / 8; - raw16 = (uint8_t *)malloc(raw16_size); - if (!raw16) { - ERROR("Error allocating %d bytes\n", raw16_size); - return EXIT_FAILURE; - } - switch (config.pal_format.format) { - case IMG_FORMAT_RGBA: - imgr = png2rgba(config.img_filename, &config.width, &config.height); - raw16_length = rgba2raw(raw16, imgr, config.width, config.height, config.pal_format.depth); - break; - case IMG_FORMAT_IA: - imgi = png2ia(config.img_filename, &config.width, &config.height); - raw16_length = ia2raw(raw16, imgi, config.width, config.height, config.pal_format.depth); - break; - default: - ERROR("Unsupported palette format: %s\n", format2str(&config.pal_format)); - exit(EXIT_FAILURE); - } - - // convert raw to palette - pal.max = (1 << config.format.depth); - ci_length = config.width * config.height * config.format.depth / 8; - ci = (uint8_t *)malloc(ci_length); - pal_success = raw2ci(ci, &pal, raw16, raw16_length, config.format.depth); - if (!pal_success) { - ERROR("Error converting palette\n"); - exit(EXIT_FAILURE); - } - - pal_length = pal.max * sizeof(pal.data[0]); - INFO("Writing 0x%X bytes to offset 0x%X of \"%s\"\n", pal_length, config.pal_offset, config.pal_filename); - flength = 0; - for (int i = 0; i < pal.max; i++) { - uint8_t entry[2]; - write_u16_be(entry, pal.data[i]); - flength += fwrite(entry, 1, sizeof(entry), pal_fp); - } - if (flength != pal_length) { - ERROR("Error writing %d bytes to \"%s\"\n", pal_length, config.pal_filename); - } - - raw = ci; - length = ci_length; - - free(raw16); - fclose(pal_fp); - break; - } - default: - return EXIT_FAILURE; - } - if (length <= 0) { - ERROR("Error converting to raw format\n"); - return EXIT_FAILURE; - } - INFO("Writing 0x%X bytes to offset 0x%X of \"%s\"\n", length, config.bin_offset, config.bin_filename); - flength = fwrite(raw, 1, length, bin_fp); - if (flength != length) { - ERROR("Error writing %d bytes to \"%s\"\n", length, config.bin_filename); - } - fclose(bin_fp); - - } else { - if (config.width <= 0 || config.height <= 0 || config.format.depth <= 0) { - ERROR("Error: must set position width and height for export\n"); - return EXIT_FAILURE; - } - bin_fp = fopen(config.bin_filename, "r"); - if (!bin_fp) { - ERROR("Error opening \"%s\"\n", config.bin_filename); - return -1; - } - raw_size = (config.width * config.height * config.format.depth + 7) / 8; - raw = (uint8_t *)malloc(raw_size); - if (config.bin_offset > 0) { - fseek(bin_fp, config.bin_offset, SEEK_SET); - } - flength = fread(raw, 1, raw_size, bin_fp); - if (flength != raw_size) { - ERROR("Error reading %d bytes from \"%s\"\n", raw_size, config.bin_filename); - } - switch (config.format.format) { - case IMG_FORMAT_RGBA: - imgr = raw2rgba(raw, config.width, config.height, config.format.depth); - res = rgba2png(config.img_filename, imgr, config.width, config.height); - break; - case IMG_FORMAT_IA: - imgi = raw2ia(raw, config.width, config.height, config.format.depth); - res = ia2png(config.img_filename, imgi, config.width, config.height); - break; - case IMG_FORMAT_I: - imgi = raw2i(raw, config.width, config.height, config.format.depth); - res = ia2png(config.img_filename, imgi, config.width, config.height); - break; - case IMG_FORMAT_CI: - { - FILE *pal_fp; - uint8_t *pal; - uint8_t *raw_fmt; - int pal_size; - - INFO("Extracting %s offset 0x%X, pal.offset 0x%0X, pal.format %s\n", format2str(&config.format), - config.bin_offset, config.pal_offset, format2str(&config.pal_format)); - - pal_fp = fopen(config.pal_filename, "r"); - if (!pal_fp) { - ERROR("Error opening \"%s\"\n", config.bin_filename); - return EXIT_FAILURE; - } - if (config.pal_offset > 0) { - fseek(pal_fp, config.pal_offset, SEEK_SET); - } - - pal_size = sizeof(uint16_t) * (1 << config.format.depth); - INFO("Palette size: %d\n", pal_size); - pal = (uint8_t *)malloc(pal_size); - flength = fread(pal, 1, pal_size, pal_fp); - if (flength != pal_size) { - ERROR("Error reading %d bytes from \"%s\"\n", pal_size, config.pal_filename); - } - raw_fmt = ci2raw(raw, pal, config.width, config.height, config.format.depth); - switch (config.pal_format.format) { - case IMG_FORMAT_RGBA: - INFO("Converting raw to RGBA16\n"); - imgr = raw2rgba(raw_fmt, config.width, config.height, config.pal_format.depth); - res = rgba2png(config.img_filename, imgr, config.width, config.height); - break; - case IMG_FORMAT_IA: - INFO("Converting raw to IA16\n"); - imgi = raw2ia(raw_fmt, config.width, config.height, config.pal_format.depth); - res = ia2png(config.img_filename, imgi, config.width, config.height); - break; - default: - ERROR("Unsupported palette format: %s\n", format2str(&config.pal_format)); - return EXIT_FAILURE; - } - free(raw_fmt); - free(pal); - break; - } - default: - return EXIT_FAILURE; - } - if (!res) { - ERROR("Error writing to \"%s\"\n", config.img_filename); - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} -#endif // N64GRAPHICS_STANDALONE diff --git a/tools/n64graphics/n64graphics.h b/tools/n64graphics/n64graphics.h deleted file mode 100644 index 9ced50e7..00000000 --- a/tools/n64graphics/n64graphics.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef N64GRAPHICS_H_ -#define N64GRAPHICS_H_ - -#include - -// intermediate formats -typedef struct _rgba -{ - uint8_t red; - uint8_t green; - uint8_t blue; - uint8_t alpha; -} rgba; - -typedef struct _ia -{ - uint8_t intensity; - uint8_t alpha; -} ia; - -// CI palette -typedef struct -{ - uint16_t data[256]; - int max; // max number of entries - int used; // number of entries used -} palette_t; - -//--------------------------------------------------------- -// N64 RGBA/IA/I/CI -> intermediate RGBA/IA -//--------------------------------------------------------- - -// N64 raw RGBA16/RGBA32 -> intermediate RGBA -rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth); - -// N64 raw IA1/IA4/IA8/IA16 -> intermediate IA -ia *raw2ia(const uint8_t *raw, int width, int height, int depth); - -// N64 raw I4/I8 -> intermediate IA -ia *raw2i(const uint8_t *raw, int width, int height, int depth); - -//--------------------------------------------------------- -// intermediate RGBA/IA -> N64 RGBA/IA/I/CI -// returns length written to 'raw' used or -1 on error -//--------------------------------------------------------- - -// intermediate RGBA -> N64 raw RGBA16/RGBA32 -int rgba2raw(uint8_t *raw, const rgba *img, int width, int height, int depth); - -// intermediate IA -> N64 raw IA1/IA4/IA8/IA16 -int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth); - -// intermediate IA -> N64 raw I4/I8 -int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth); - - -//--------------------------------------------------------- -// N64 CI <-> N64 RGBA16/IA16 -//--------------------------------------------------------- - -// N64 CI raw data and palette to raw data (either RGBA16 or IA16) -uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth); - -// convert from raw (RGBA16 or IA16) format to CI + palette -int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth); - - -//--------------------------------------------------------- -// intermediate RGBA/IA -> PNG -//--------------------------------------------------------- - -// intermediate RGBA write to PNG file -int rgba2png(const char *png_filename, const rgba *img, int width, int height); - -// intermediate IA write to grayscale PNG file -int ia2png(const char *png_filename, const ia *img, int width, int height); - - -//--------------------------------------------------------- -// PNG -> intermediate RGBA/IA -//--------------------------------------------------------- - -// PNG file -> intermediate RGBA -rgba *png2rgba(const char *png_filename, int *width, int *height); - -// PNG file -> intermediate IA -ia *png2ia(const char *png_filename, int *width, int *height); - - -//--------------------------------------------------------- -// version -//--------------------------------------------------------- - -// get version of underlying graphics reading library -const char *n64graphics_get_read_version(void); - -// get version of underlying graphics writing library -const char *n64graphics_get_write_version(void); - -#endif // N64GRAPHICS_H_ diff --git a/tools/n64graphics/utils.c b/tools/n64graphics/utils.c deleted file mode 100644 index e5a1c8f3..00000000 --- a/tools/n64graphics/utils.c +++ /dev/null @@ -1,276 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#if defined(_MSC_VER) || defined(__MINGW32__) - #include - #include -#else - #include - #include -#endif - -#include "utils.h" - -// global verbosity setting -int g_verbosity = 0; - -int read_s16_be(unsigned char *buf) -{ - unsigned tmp = read_u16_be(buf); - int ret; - if (tmp > 0x7FFF) { - ret = -((int)0x10000 - (int)tmp); - } else { - ret = (int)tmp; - } - return ret; -} - -float read_f32_be(unsigned char *buf) -{ - union {uint32_t i; float f;} ret; - ret.i = read_u32_be(buf); - return ret.f; -} - -int is_power2(unsigned int val) -{ - while (((val & 1) == 0) && (val > 1)) { - val >>= 1; - } - return (val == 1); -} - -void fprint_hex(FILE *fp, const unsigned char *buf, int length) -{ - int i; - for (i = 0; i < length; i++) { - fprint_byte(fp, buf[i]); - fputc(' ', fp); - } -} - -void fprint_hex_source(FILE *fp, const unsigned char *buf, int length) -{ - int i; - for (i = 0; i < length; i++) { - if (i > 0) fputs(", ", fp); - fputs("0x", fp); - fprint_byte(fp, buf[i]); - } -} - -void print_hex(const unsigned char *buf, int length) -{ - fprint_hex(stdout, buf, length); -} - -void swap_bytes(unsigned char *data, long length) -{ - long i; - unsigned char tmp; - for (i = 0; i < length; i += 2) { - tmp = data[i]; - data[i] = data[i+1]; - data[i+1] = tmp; - } -} - -void reverse_endian(unsigned char *data, long length) -{ - long i; - unsigned char tmp; - for (i = 0; i < length; i += 4) { - tmp = data[i]; - data[i] = data[i+3]; - data[i+3] = tmp; - tmp = data[i+1]; - data[i+1] = data[i+2]; - data[i+2] = tmp; - } -} - -long filesize(const char *filename) -{ - struct stat st; - - if (stat(filename, &st) == 0) { - return st.st_size; - } - - return -1; -} - -void touch_file(const char *filename) -{ - int fd; - //fd = open(filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666); - fd = open(filename, O_WRONLY|O_CREAT, 0666); - if (fd >= 0) { - utime(filename, NULL); - close(fd); - } -} - -long read_file(const char *file_name, unsigned char **data) -{ - FILE *in; - unsigned char *in_buf = NULL; - long file_size; - long bytes_read; - in = fopen(file_name, "rb"); - if (in == NULL) { - return -1; - } - - // allocate buffer to read from offset to end of file - fseek(in, 0, SEEK_END); - file_size = ftell(in); - - // sanity check - if (file_size > 256*MB) { - return -2; - } - - in_buf = (unsigned char *)malloc(file_size); - fseek(in, 0, SEEK_SET); - - // read bytes - bytes_read = fread(in_buf, 1, file_size, in); - if (bytes_read != file_size) { - return -3; - } - - fclose(in); - *data = in_buf; - return bytes_read; -} - -long write_file(const char *file_name, unsigned char *data, long length) -{ - FILE *out; - long bytes_written; - // open output file - out = fopen(file_name, "wb"); - if (out == NULL) { - perror(file_name); - return -1; - } - bytes_written = fwrite(data, 1, length, out); - fclose(out); - return bytes_written; -} - -void generate_filename(const char *in_name, char *out_name, char *extension) -{ - char tmp_name[FILENAME_MAX]; - int len; - int i; - strcpy(tmp_name, in_name); - len = strlen(tmp_name); - for (i = len - 1; i > 0; i--) { - if (tmp_name[i] == '.') { - break; - } - } - if (i <= 0) { - i = len; - } - tmp_name[i] = '\0'; - sprintf(out_name, "%s.%s", tmp_name, extension); -} - -char *basename_(const char *name) -{ - const char *base = name; - while (*name) { - if (*name++ == '/') { - base = name; - } - } - return (char *)base; -} - -void make_dir(const char *dir_name) -{ - struct stat st = {0}; - if (stat(dir_name, &st) == -1) { - mkdir(dir_name, 0755); - } -} - -long copy_file(const char *src_name, const char *dst_name) -{ - unsigned char *buf; - long bytes_written; - long bytes_read; - - bytes_read = read_file(src_name, &buf); - - if (bytes_read > 0) { - bytes_written = write_file(dst_name, buf, bytes_read); - if (bytes_written != bytes_read) { - bytes_read = -1; - } - free(buf); - } - - return bytes_read; -} - -void dir_list_ext(const char *dir, const char *extension, dir_list *list) -{ - char *pool; - char *pool_ptr; - struct dirent *entry; - DIR *dfd; - int idx; - - dfd = opendir(dir); - if (dfd == NULL) { - ERROR("Can't open '%s'\n", dir); - exit(1); - } - - pool = (char *)malloc(FILENAME_MAX * MAX_DIR_FILES); - pool_ptr = pool; - - idx = 0; - while ((entry = readdir(dfd)) != NULL && idx < MAX_DIR_FILES) { - if (!extension || str_ends_with(entry->d_name, extension)) { - sprintf(pool_ptr, "%s/%s", dir, entry->d_name); - list->files[idx] = pool_ptr; - pool_ptr += strlen(pool_ptr) + 1; - idx++; - } - } - list->count = idx; - - closedir(dfd); -} - -void dir_list_free(dir_list *list) -{ - // assume first entry in array is allocated - if (list->files[0]) { - free(list->files[0]); - list->files[0] = NULL; - } -} - -int str_ends_with(const char *str, const char *suffix) -{ - if (!str || !suffix) { - return 0; - } - size_t len_str = strlen(str); - size_t len_suffix = strlen(suffix); - if (len_suffix > len_str) { - return 0; - } - return (0 == strncmp(str + len_str - len_suffix, suffix, len_suffix)); -} diff --git a/tools/n64graphics/utils.h b/tools/n64graphics/utils.h deleted file mode 100644 index b66116a5..00000000 --- a/tools/n64graphics/utils.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef UTILS_H_ -#define UTILS_H_ - -#include - -// defines - -// printing size_t varies by compiler -#if defined(_MSC_VER) || defined(__MINGW32__) - #define SIZE_T_FORMAT "%Iu" -#else - #define SIZE_T_FORMAT "%zu" -#endif - -#define KB 1024 -#define MB (1024 * KB) - -// number of elements in statically declared array -#define DIM(S_ARR_) (sizeof(S_ARR_) / sizeof(S_ARR_[0])) - -#define MIN(A_, B_) ((A_) < (B_) ? (A_) : (B_)) -#define MAX(A_, B_) ((A_) > (B_) ? (A_) : (B_)) - -// align value to N-byte boundary -#define ALIGN(VAL_, ALIGNMENT_) (((VAL_) + ((ALIGNMENT_) - 1)) & ~((ALIGNMENT_) - 1)) - -// read/write u32/16 big/little endian -#define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3])) -#define read_u32_le(buf) (unsigned int)(((buf)[1] << 24) + ((buf)[0] << 16) + ((buf)[3] << 8) + ((buf)[2])) -#define write_u32_be(buf, val) do { \ - (buf)[0] = ((val) >> 24) & 0xFF; \ - (buf)[1] = ((val) >> 16) & 0xFF; \ - (buf)[2] = ((val) >> 8) & 0xFF; \ - (buf)[3] = (val) & 0xFF; \ -} while(0) -#define read_u16_be(buf) (((buf)[0] << 8) + ((buf)[1])) -#define write_u16_be(buf, val) do { \ - (buf)[0] = ((val) >> 8) & 0xFF; \ - (buf)[1] = ((val)) & 0xFF; \ -} while(0) - -// print nibbles and bytes -#define fprint_nibble(FP, NIB_) fputc((NIB_) < 10 ? ('0' + (NIB_)) : ('A' + (NIB_) - 0xA), FP) -#define fprint_byte(FP, BYTE_) do { \ - fprint_nibble(FP, (BYTE_) >> 4); \ - fprint_nibble(FP, (BYTE_) & 0x0F); \ - } while(0) -#define print_nibble(NIB_) fprint_nibble(stdout, NIB_) -#define print_byte(BYTE_) fprint_byte(stdout, BYTE_) - -// Windows compatibility -#if defined(_MSC_VER) || defined(__MINGW32__) - #include - #define mkdir(DIR_, PERM_) _mkdir(DIR_) - #ifndef strcasecmp - #define strcasecmp(A, B) stricmp(A, B) - #endif -#endif - -// typedefs - -#define MAX_DIR_FILES 128 -typedef struct -{ - char *files[MAX_DIR_FILES]; - int count; -} dir_list; - -// global verbosity setting -extern int g_verbosity; - -#define ERROR(...) fprintf(stderr, __VA_ARGS__) -#define INFO(...) if (g_verbosity) printf(__VA_ARGS__) -#define INFO_HEX(...) if (g_verbosity) print_hex(__VA_ARGS__) - -// functions - -// convert two bytes in big-endian to signed int -int read_s16_be(unsigned char *buf); - -// convert four bytes in big-endian to float -float read_f32_be(unsigned char *buf); - -// determine if value is power of 2 -// returns 1 if val is power of 2, 0 otherwise -int is_power2(unsigned int val); - -// print buffer as hex bytes -// fp: file pointer -// buf: buffer to read bytes from -// length: length of buffer to print -void fprint_hex(FILE *fp, const unsigned char *buf, int length); -void fprint_hex_source(FILE *fp, const unsigned char *buf, int length); -void print_hex(const unsigned char *buf, int length); - -// perform byteswapping to convert from v64 to z64 ordering -void swap_bytes(unsigned char *data, long length); - -// reverse endian to convert from n64 to z64 ordering -void reverse_endian(unsigned char *data, long length); - -// get size of file without opening it; -// returns file size or negative on error -long filesize(const char *file_name); - -// update file timestamp to now, creating it if it doesn't exist -void touch_file(const char *filename); - -// read entire contents of file into buffer -// returns file size or negative on error -long read_file(const char *file_name, unsigned char **data); - -// write buffer to file -// returns number of bytes written out or -1 on failure -long write_file(const char *file_name, unsigned char *data, long length); - -// generate an output file name from input name by replacing file extension -// in_name: input file name -// out_name: buffer to write output name in -// extension: new file extension to use -void generate_filename(const char *in_name, char *out_name, char *extension); - -// extract base filename from file path -// name: path to file -// returns just the file name after the last '/' -char *basename_(const char *name); - -// make a directory if it doesn't exist -// dir_name: name of the directory -void make_dir(const char *dir_name); - -// copy a file from src_name to dst_name. will not make directories -// src_name: source file name -// dst_name: destination file name -long copy_file(const char *src_name, const char *dst_name); - -// list a directory, optionally filtering files by extension -// dir: directory to list files in -// extension: extension to filter files by (NULL if no filtering) -// list: output list and count -void dir_list_ext(const char *dir, const char *extension, dir_list *list); - -// free associated date from a directory list -// list: directory list filled in by dir_list_ext() call -void dir_list_free(dir_list *list); - -// determine if a string ends with another string -// str: string to check if ends with 'suffix' -// suffix: string to see if 'str' ends with -// returns 1 if 'str' ends with 'suffix' -int str_ends_with(const char *str, const char *suffix); - -#endif // UTILS_H_