/*
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
*/