/* snesgfx - Basic PNG to SNES graphics converter Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ------------------------------------------------------------------------- Main */ #include #include #include #include #include static void Usage(void) { fprintf(stderr, "usage: snesgfx [-p palette_file] [-s size] [-c colours] " "source.png dest.data\n"); exit(EXIT_FAILURE); } static int GetPixel(int x, int y, png_bytep *data, int width, int height) { if (x >= width || y >= height) { return 0; } else { return data[y][x]; } } static void GetPlanes(int x, int y, int plane[16][4], int depth, int size, png_bytep *data, int width, int height) { int dx,dy; for(dy = 0; dy < size; dy++) { for(dx = 0; dx < 8; dx++) { int pix = GetPixel(x + dx, y + dy, data, width, height); int f; for(f = 0; f < depth; f++) { if ((pix & (1 << f)) == (1 << f)) { plane[dy][f] = plane[dy][f] | (1 << (7 - dx)); } } } } } static void Convert4Colour8x8(png_bytep *data, int width, int height, FILE *output) { int x,y; int dy; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x+= 8) { int plane[16][4] = {0}; int f; GetPlanes(x, y, plane, 2, 8, data, width, height); for(dy = 0; dy < 8; dy++) { for(f = 0; f < 2; f++) { putc(plane[dy][f], output); } } } } } static void Convert4Colour16x16(png_bytep *data, int width, int height, FILE *output) { int x,y; int dx,dy; int tile = 0; unsigned char *mem; size_t size; size = ((width / 16 * height / 16) / 8 + 1) * 512; if (!size) { fprintf(stderr, "No tiles to export\n"); exit(EXIT_FAILURE); } mem = malloc(size); if (!mem) { fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); } memset(mem, 0, size); for(y = 0; y < height; y += 16) { for(x = 0; x < width; x+= 16) { int plane_left[16][4] = {0}; int plane_right[16][4] = {0}; int offset; int f; offset = (tile / 8) * 512 + (tile % 8) * 32; GetPlanes(x, y, plane_left, 2, 16, data, width, height); GetPlanes(x + 8, y, plane_right, 2, 16, data, width, height); for(dy = 0; dy < 8; dy++) { mem[offset + dy * 2] = plane_left[dy][0]; mem[offset + dy * 2 + 1] = plane_left[dy][1]; mem[offset + dy * 2 + 16] = plane_right[dy][0]; mem[offset + dy * 2 + 17] = plane_right[dy][1]; mem[offset + dy * 2 + 256] = plane_left[8 + dy][0]; mem[offset + dy * 2 + 256 + 1] = plane_left[8 + dy][1]; mem[offset + dy * 2 + 256 + 16] = plane_right[8 + dy][0]; mem[offset + dy * 2 + 256 + 17] = plane_right[8 + dy][1]; } tile++; } } fwrite(mem, 1, size, output); free(mem); } static void Convert16Colour8x8(png_bytep *data, int width, int height, FILE *output) { int x,y; int dy; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x+= 8) { int plane[16][4] = {0}; int f; GetPlanes(x, y, plane, 4, 8, data, width, height); for(dy = 0; dy < 8; dy++) { for(f = 0; f < 2; f++) { putc(plane[dy][f], output); } } for(dy = 0; dy < 8; dy++) { for(f = 2; f < 4; f++) { putc(plane[dy][f], output); } } } } } static void Convert16Colour16x16(png_bytep *data, int width, int height, FILE *output) { int x,y; int dx,dy; int tile = 0; unsigned char *mem; size_t size; size = ((width / 16 * height / 16) / 8 + 1) * 1024; if (!size) { fprintf(stderr, "No tiles to export\n"); exit(EXIT_FAILURE); } mem = malloc(size); if (!mem) { fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); } memset(mem, 0, size); for(y = 0; y < height; y += 16) { for(x = 0; x < width; x+= 16) { int plane_left[16][4] = {0}; int plane_right[16][4] = {0}; int offset; int f; offset = (tile / 8) * 1024 + (tile % 8) * 64; GetPlanes(x, y, plane_left, 4, 16, data, width, height); GetPlanes(x + 8, y, plane_right, 4, 16, data, width, height); for(dy = 0; dy < 8; dy++) { mem[offset + dy * 2] = plane_left[dy][0]; mem[offset + dy * 2 + 1] = plane_left[dy][1]; mem[offset + dy * 2 + 16] = plane_left[dy][2]; mem[offset + dy * 2 + 17] = plane_left[dy][3]; mem[offset + dy * 2 + 32] = plane_right[dy][0]; mem[offset + dy * 2 + 33] = plane_right[dy][1]; mem[offset + dy * 2 + 48] = plane_right[dy][2]; mem[offset + dy * 2 + 49] = plane_right[dy][3]; mem[offset + dy * 2 + 512] = plane_left[8+dy][0]; mem[offset + dy * 2 + 512 + 1] = plane_left[8+dy][1]; mem[offset + dy * 2 + 512 + 16] = plane_left[8+dy][2]; mem[offset + dy * 2 + 512 + 17] = plane_left[8+dy][3]; mem[offset + dy * 2 + 512 + 32] = plane_right[8+dy][0]; mem[offset + dy * 2 + 512 + 33] = plane_right[8+dy][1]; mem[offset + dy * 2 + 512 + 48] = plane_right[8+dy][2]; mem[offset + dy * 2 + 512 + 49] = plane_right[8+dy][3]; } tile++; } } fwrite(mem, 1, size, output); free(mem); } static void SavePalette(const char *palette, int colours, png_color *pal, int palette_size) { FILE *fp; int f; if (!(fp = fopen(palette, "wb"))) { fprintf(stderr, "Failed to create %s\n", palette); exit(EXIT_FAILURE); } for(f = 0; f < colours; f++) { int val; if (f < palette_size) { int r,g,b; r = pal[f].red / 8; g = pal[f].green / 8; b = pal[f].blue / 8; val = b * 1024 + g * 32 + r; } else { val = 0; } putc(val & 0xff, fp); val = val>>8; putc(val & 0xff, fp); } fclose(fp); } int main(int argc, char *argv[]) { char *palette = NULL; int size = 8; int colours = 4; char *source = NULL; char *dest = NULL; unsigned char header[8] = {0}; FILE *input; FILE *output; png_structp png; png_infop info; png_bytep *rows; int width; int height; int f; f = 1; while(f < argc && argv[f][0] == '-') { switch(argv[f][1]) { case 'p': palette = argv[++f]; break; case 's': size = atoi(argv[++f]); break; case 'c': colours = atoi(argv[++f]); break; default: Usage(); break; } f++; } if (f < argc) { source = argv[f++]; } if (f < argc) { dest = argv[f++]; } if (!source || !dest) { Usage(); } if (size != 8 && size != 16) { fprintf(stderr, "Size must be 8 or 16\n"); return EXIT_FAILURE; } if (colours != 4 && colours != 16) { fprintf(stderr, "Colours must be 4 or 16\n"); return EXIT_FAILURE; } if (!(input = fopen(source, "rb"))) { fprintf(stderr, "Failed to open %s\n", source); return EXIT_FAILURE; } fread(header, 1, 8, input); if (png_sig_cmp(header, 0, 8)) { fprintf(stderr, "%s not a PNG\n", source); return EXIT_FAILURE; } if (!(output = fopen(dest, "wb"))) { fprintf(stderr, "Failed to create %s\n", dest); return EXIT_FAILURE; } png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png) { fprintf(stderr, "Failed to initialise PNG\n"); return EXIT_FAILURE; } info = png_create_info_struct(png); if (!info) { fprintf(stderr, "Failed to initialise PNG\n"); return EXIT_FAILURE; } if (setjmp(png_jmpbuf(png))) { fprintf(stderr, "Error in PNG library\n"); return EXIT_FAILURE; } png_init_io(png, input); png_set_sig_bytes(png, 8); png_set_packing(png); png_read_png(png, info, PNG_TRANSFORM_STRIP_ALPHA, NULL); if (png_get_color_type(png, info) != PNG_COLOR_TYPE_PALETTE) { fprintf(stderr, "Expecting a PNG with a palette\n"); } rows = png_get_rows(png, info); width = png_get_image_width(png, info); height = png_get_image_height(png, info); if (colours == 4 && size == 8) { Convert4Colour8x8(rows, width, height, output); } else if (colours == 16 && size == 8) { Convert16Colour8x8(rows, width, height, output); } else if (colours == 4 && size == 16) { Convert4Colour16x16(rows, width, height, output); } else if (colours == 16 && size == 16) { Convert16Colour16x16(rows, width, height, output); } fclose(input); fclose(output); if (palette) { png_color *pal; int palette_size; png_get_PLTE(png, info, &pal, &palette_size); SavePalette(palette, colours, pal, palette_size); } return EXIT_SUCCESS; } /* vim: ai sw=4 ts=8 expandtab */