pokemon PNGs and palettes

This commit is contained in:
YamaArashi
2015-11-13 21:57:22 -08:00
parent 3d1b6597f3
commit 4c733f3811
2871 changed files with 19547 additions and 2830 deletions

1
tools/gbagfx/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
gbagfx

15
tools/gbagfx/Makefile Normal file
View File

@@ -0,0 +1,15 @@
CC = gcc
CFLAGS = -Wall -std=c11 -O2
LIBS = -lz -lpng
SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c util.c
.PHONY: clean
gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h util.h
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LIBS)
clean:
$(RM) gbagfx gbagfx.exe

167
tools/gbagfx/convert_png.c Normal file
View File

@@ -0,0 +1,167 @@
#include <stdio.h>
#include <setjmp.h>
#include <png.h>
#include "global.h"
#include "convert_png.h"
#include "gfx.h"
void ReadPng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
unsigned char sig[8];
if (fread(sig, 8, 1, fp) != 1)
FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
if (png_sig_cmp(sig, 0, 8))
FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG read struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
if (bit_depth != image->bitDepth)
FATAL_ERROR("\"%s\" has a bit depth of %d, but the expected bit depth is %d.\n", path, bit_depth, image->bitDepth);
int color_type = png_get_color_type(png_ptr, info_ptr);
if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("\"%s\" has an unsupported color type.\n", path);
// Check if the image has a palette so that we can tell if the colors need to be inverted later.
// Don't read the palette because it's not needed for now.
image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
image->width = png_get_image_width(png_ptr, info_ptr);
image->height = png_get_image_height(png_ptr, info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image->pixels = malloc(image->height * rowbytes);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate pixel buffer.\n");
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error reading from \"%s\".\n", path);
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(row_pointers);
fclose(fp);
}
void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette)
{
png_colorp colors = malloc(palette->numColors * sizeof(png_color));
if (colors == NULL)
FATAL_ERROR("Failed to allocate PNG palette.\n");
for (int i = 0; i < palette->numColors; i++) {
colors[i].red = palette->colors[i].red;
colors[i].green = palette->colors[i].green;
colors[i].blue = palette->colors[i].blue;
}
png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors);
free(colors);
}
void WritePng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG write struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing header for \"%s\".\n", path);
int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
image->bitDepth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (image->hasPalette) {
SetPngPalette(png_ptr, info_ptr, &image->palette);
if (image->isObject) {
png_byte trans = 0;
png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0);
}
}
png_write_info(png_ptr, info_ptr);
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing \"%s\".\n", path);
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error ending write of \"%s\".\n", path);
png_write_end(png_ptr, NULL);
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) 2015 YamaArashi
#ifndef CONVERT_PNG_H
#define CONVERT_PNG_H
#include "gfx.h"
void ReadPng(char *path, struct Image *image);
void WritePng(char *path, struct Image *image);
#endif // CONVERT_PNG_H

332
tools/gbagfx/gfx.c Normal file
View File

@@ -0,0 +1,332 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F)
#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F)
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
#define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r))
#define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31)
#define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8)
static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = tilesY * 8 + j;
int destX = tilesX;
unsigned char srcPixelOctet = *src++;
unsigned char *destPixelOctet = &dest[destY * pitch + destX];
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = tilesY * 8 + j;
for (int k = 0; k < 4; k++) {
int destX = tilesX * 4 + k;
unsigned char srcPixelPair = *src++;
unsigned char leftPixel = srcPixelPair & 0xF;
unsigned char rightPixel = srcPixelPair >> 4;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
dest[destY * pitch + destX] = (leftPixel << 4) | rightPixel;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = tilesY * 8 + j;
for (int k = 0; k < 8; k++) {
int destX = tilesX * 8 + k;
unsigned char srcPixel = *src++;
if (invertColors)
srcPixel = 255 - srcPixel;
dest[destY * pitch + destX] = srcPixel;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = tilesY * 8 + j;
int srcX = tilesX;
unsigned char srcPixelOctet = src[srcY * pitch + srcX];
unsigned char *destPixelOctet = dest++;
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = tilesY * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = tilesX * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
*dest++ = (rightPixel << 4) | leftPixel;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int tilesWidth, bool invertColors)
{
int tilesX = 0;
int tilesY = 0;
int pitch = tilesWidth * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = tilesY * 8 + j;
for (int k = 0; k < 8; k++) {
int srcX = tilesX * 8 + k;
unsigned char srcPixel = src[srcY * pitch + srcX];
if (invertColors)
srcPixel = 255 - srcPixel;
*dest++ = srcPixel;
}
}
tilesX++;
if (tilesX == tilesWidth) {
tilesX = 0;
tilesY++;
}
}
}
void ReadImage(char *path, int tilesWidth, int bitDepth, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numTiles = fileSize / tileSize;
int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth;
image->width = tilesWidth * 8;
image->height = tilesHeight * 8;
image->bitDepth = bitDepth;
image->pixels = calloc(tilesWidth * tilesHeight, tileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
switch (bitDepth) {
case 1:
ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, tilesWidth, invertColors);
break;
case 4:
ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, tilesWidth, invertColors);
break;
case 8:
ConvertFromTiles8Bpp(buffer, image->pixels, numTiles, tilesWidth, invertColors);
break;
}
free(buffer);
}
void WriteImage(char *path, int numTiles, int bitDepth, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
if (image->width % 8 != 0)
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
if (image->height % 8 != 0)
FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height);
int tilesWidth = image->width / 8;
int tilesHeight = image->height / 8;
int maxNumTiles = tilesWidth * tilesHeight;
if (numTiles == 0) {
numTiles = maxNumTiles;
} else {
if (numTiles > maxNumTiles) {
FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles);
}
}
int bufferSize = numTiles * tileSize;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
switch (bitDepth) {
case 1:
ConvertToTiles1Bpp(image->pixels, buffer, numTiles, tilesWidth, invertColors);
break;
case 4:
ConvertToTiles4Bpp(image->pixels, buffer, numTiles, tilesWidth, invertColors);
break;
case 8:
ConvertToTiles8Bpp(image->pixels, buffer, numTiles, tilesWidth, invertColors);
break;
}
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void FreeImage(struct Image *image)
{
free(image->pixels);
image->pixels = NULL;
}
void ReadGbaPalette(char *path, struct Palette *palette)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
palette->numColors = fileSize / 2;
if (palette->numColors != 16 && palette->numColors != 256)
FATAL_ERROR("\"%s\" contains %d colors, but the number of colors must be 16 or 256.\n", path, palette->numColors);
for (int i = 0; i < palette->numColors; i++) {
uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[i * 2];
palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry));
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
}
free(data);
}
void WriteGbaPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
for (int i = 0; i < palette->numColors; i++) {
unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red);
unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green);
unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue);
uint16_t paletteEntry = SET_GBA_PAL(red, green, blue);
fputc(paletteEntry & 0xFF, fp);
fputc(paletteEntry >> 8, fp);
}
fclose(fp);
}

36
tools/gbagfx/gfx.h Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2015 YamaArashi
#ifndef GFX_H
#define GFX_H
#include <stdint.h>
#include <stdbool.h>
struct Color {
unsigned char red;
unsigned char green;
unsigned char blue;
};
struct Palette {
struct Color colors[256];
int numColors;
};
struct Image {
int width;
int height;
int bitDepth;
unsigned char *pixels;
bool hasPalette;
struct Palette palette;
bool isObject;
};
void ReadImage(char *path, int tilesWidth, int bitDepth, struct Image *image, bool invertColors);
void WriteImage(char *path, int numTiles, int bitDepth, struct Image *image, bool invertColors);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void WriteGbaPalette(char *path, struct Palette *palette);
#endif // GFX_H

35
tools/gbagfx/global.h Normal file
View File

@@ -0,0 +1,35 @@
// Copyright (c) 2015 YamaArashi
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#define GBAGFX_MAX_PATH 255
#define CHECK_PATH_LENGTH(path) \
do { \
if (strlen(path) > GBAGFX_MAX_PATH) \
FATAL_ERROR("\"%s\" is too long a path.\n", path); \
} while (0)
#endif // GLOBAL_H

164
tools/gbagfx/jasc_pal.c Normal file
View File

@@ -0,0 +1,164 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
// Read/write Paint Shop Pro palette files.
// Format of a Paint Shop Pro palette file, line by line:
// "JASC-PAL\r\n" (signature)
// "0100\r\n" (version; seems to always be "0100")
// "<NUMBER_OF_COLORS>\r\n" (number of colors in decimal)
//
// 16 or 256 times (depending on above line):
// "<RED> <GREEN> <BLUE>\r\n" (color entry)
//
// Each color component is a decimal number from 0 to 255.
// Examples:
// Black - "0 0 0\r\n"
// Blue - "0 0 255\r\n"
// Brown - "150 75 0\r\n"
#define MAX_LINE_LENGTH 11
void ReadJascPaletteLine(FILE *fp, char *line)
{
int c;
int length = 0;
for (;;) {
c = fgetc(fp);
if (c == '\r') {
c = fgetc(fp);
if (c != '\n')
FATAL_ERROR("CR line endings aren't supported.\n");
line[length] = 0;
return;
}
if (c == '\n')
FATAL_ERROR("LF line endings aren't supported.\n");
if (c == EOF)
FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n");
if (c == 0)
FATAL_ERROR("NUL character in file.\n");
if (length == MAX_LINE_LENGTH) {
line[length] = 0;
FATAL_ERROR("The line \"%s\" is too long.\n", line);
}
line[length++] = c;
}
}
void ReadJascPalette(char *path, struct Palette *palette)
{
char line[MAX_LINE_LENGTH + 1];
FILE *fp = fopen(path, "rb");
ReadJascPaletteLine(fp, line);
if (strcmp(line, "JASC-PAL") != 0)
FATAL_ERROR("Invalid JASC-PAL signature.\n");
ReadJascPaletteLine(fp, line);
if (strcmp(line, "0100") != 0)
FATAL_ERROR("Unsuported JASC-PAL version.\n");
ReadJascPaletteLine(fp, line);
if (!ParseNumber(line, NULL, 10, &palette->numColors))
FATAL_ERROR("Failed to parse number of colors.\n");
if (palette->numColors != 16 && palette->numColors != 256)
FATAL_ERROR("%d is an invalid number of colors. The number of colors must be 16 or 256.\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++) {
ReadJascPaletteLine(fp, line);
char *s = line;
char *end;
int red;
int green;
int blue;
if (!ParseNumber(s, &end, 10, &red))
FATAL_ERROR("Failed to parse red color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after red color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between red and green color components.\n");
if (!ParseNumber(s, &end, 10, &green))
FATAL_ERROR("Failed to parse green color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after green color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between green and blue color components.\n");
if (!ParseNumber(s, &end, 10, &blue))
FATAL_ERROR("Failed to parse blue color component.\n");
if (*end != 0)
FATAL_ERROR("Garbage after blue color component.\n");
if (red < 0 || red > 255)
FATAL_ERROR("Red color component (%d) is outside the range [0, 255].\n", red);
if (green < 0 || green > 255)
FATAL_ERROR("Green color component (%d) is outside the range [0, 255].\n", green);
if (blue < 0 || blue > 255)
FATAL_ERROR("Blue color component (%d) is outside the range [0, 255].\n", blue);
palette->colors[i].red = red;
palette->colors[i].green = green;
palette->colors[i].blue = blue;
}
if (fgetc(fp) != EOF)
FATAL_ERROR("Garbage after color data.\n");
fclose(fp);
}
void WriteJascPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
fputs("JASC-PAL\r\n", fp);
fputs("0100\r\n", fp);
fprintf(fp, "%d\r\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++) {
struct Color *color = &palette->colors[i];
fprintf(fp, "%d %d %d\r\n", color->red, color->green, color->blue);
}
fclose(fp);
}

9
tools/gbagfx/jasc_pal.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef JASC_PAL_H
#define JASC_PAL_H
void ReadJascPalette(char *path, struct Palette *palette);
void WriteJascPalette(char *path, struct Palette *palette);
#endif // JASC_PAL_H

143
tools/gbagfx/lz.c Normal file
View File

@@ -0,0 +1,143 @@
// Copyright (c) 2015 YamaArashi
#include <stdlib.h>
#include <stdbool.h>
#include "global.h"
#include "lz.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{
if (srcSize < 4)
return NULL;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
return NULL;
int srcPos = 4;
int destPos = 0;
for (;;) {
if (srcPos >= srcSize)
return NULL;
unsigned char flags = src[srcPos++];
for (int i = 0; i < 8; i++) {
if (flags & 0x80) {
if (srcPos + 1 >= srcSize)
return NULL;
int blockSize = (src[srcPos] >> 4) + 3;
int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1;
srcPos += 2;
int blockPos = destPos - blockDistance;
if (destPos + blockSize > destSize || blockPos < 0)
return NULL;
for (int j = 0; j < blockSize; j++)
dest[destPos++] = dest[blockPos + j];
} else {
if (srcPos >= srcSize || destPos >= destSize)
return NULL;
dest[destPos++] = src[srcPos++];
}
if (destPos == destSize) {
*uncompressedSize = destSize;
return dest;
}
flags <<= 1;
}
}
}
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize)
{
const int minDistance = 2; // for compatibility with LZ77UnCompVram()
if (srcSize <= 0)
return NULL;
int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8);
// Round up to the next multiple of four.
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
return NULL;
// header
dest[0] = 0x10; // LZ compression type
dest[1] = (unsigned char)srcSize;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
for (;;) {
unsigned char *flags = &dest[destPos++];
*flags = 0;
for (int i = 0; i < 8; i++) {
int bestBlockDistance = 0;
int bestBlockSize = 0;
int blockDistance = minDistance;
while (blockDistance <= srcPos && blockDistance <= 0x1000) {
int blockStart = srcPos - blockDistance;
int blockSize = 0;
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > bestBlockSize) {
bestBlockDistance = blockDistance;
bestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockDistance++;
}
if (bestBlockSize >= 3) {
*flags |= (0x80 >> i);
srcPos += bestBlockSize;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
dest[destPos++] = src[srcPos++];
}
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
}
}

9
tools/gbagfx/lz.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef LZ_H
#define LZ_H
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize);
#endif // LZ_H

260
tools/gbagfx/main.c Normal file
View File

@@ -0,0 +1,260 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "global.h"
#include "util.h"
#include "gfx.h"
#include "convert_png.h"
#include "jasc_pal.h"
#include "lz.h"
void ConvertToPng(char *imageFilePath, char *paletteFilePath, bool isObject, int width)
{
struct Image image;
int bitDepth = 0;
char *extension = GetFileExtension(imageFilePath);
if (extension == NULL)
FATAL_ERROR("\"%s\" has no file extension.\n", imageFilePath);
if (strcmp(extension, "1bpp") == 0)
bitDepth = 1;
else if (strcmp(extension, "4bpp") == 0)
bitDepth = 4;
else if (strcmp(extension, "8bpp") == 0)
bitDepth = 8;
else
FATAL_ERROR("Unexpected file extension \"%s\". Expected \"1bpp\", \"4bpp\", or \"8bpp\".\n", extension);
if (paletteFilePath != NULL) {
ReadGbaPalette(paletteFilePath, &image.palette);
image.hasPalette = true;
} else {
image.hasPalette = false;
}
ReadImage(imageFilePath, width, bitDepth, &image, !image.hasPalette);
image.isObject = isObject;
ChangeFileExtension(imageFilePath, "png");
WritePng(imageFilePath, &image);
FreeImage(&image);
}
void ConvertFromPng(char *imageFilePath, int numTiles, int bitDepth)
{
struct Image image;
image.bitDepth = bitDepth;
ExpectFileExtension(imageFilePath, "png");
ReadPng(imageFilePath, &image);
char newExtension[5];
snprintf(newExtension, 5, "%dbpp", bitDepth);
ChangeFileExtension(imageFilePath, newExtension);
WriteImage(imageFilePath, numTiles, bitDepth, &image, !image.hasPalette);
FreeImage(&image);
}
void ConvertToJascPalette(char *paletteFilePath)
{
struct Palette palette;
ExpectFileExtension(paletteFilePath, "gbapal");
ReadGbaPalette(paletteFilePath, &palette);
ChangeFileExtension(paletteFilePath, "pal");
WriteJascPalette(paletteFilePath, &palette);
}
void ConvertFromJascPalette(char *paletteFilePath)
{
struct Palette palette;
ExpectFileExtension(paletteFilePath, "pal");
ReadJascPalette(paletteFilePath, &palette);
ChangeFileExtension(paletteFilePath, "gbapal");
WriteGbaPalette(paletteFilePath, &palette);
}
void LZCompressFile(char *path)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize, &compressedSize);
free(buffer);
AddFileExtension(path, "lz");
WriteWholeFile(path, compressedData, compressedSize);
free(compressedData);
}
void LZDecompressFile(char *path)
{
ExpectFileExtension(path, "lz");
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = LZDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
RemoveFileExtension(path);
WriteWholeFile(path, uncompressedData, uncompressedSize);
free(uncompressedData);
}
int main(int argc, char **argv)
{
if (argc < 2)
FATAL_ERROR("No args.\n");
char *command = argv[1];
if (strcmp(command, "png") == 0) {
if (argc < 3)
FATAL_ERROR("No image file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char imageFilePath[GBAGFX_MAX_PATH + 1];
strcpy(imageFilePath, argv[2]);
char paletteFilePath[GBAGFX_MAX_PATH + 1];
bool hasPalette = false;
bool isObject = false;
int width = 1;
for (int i = 3; i < argc; i++) {
char *option = argv[i];
if (strcmp(option, "-palette") == 0) {
if (i + 1 >= argc)
FATAL_ERROR("No palette file path following \"-palette\".\n");
i++;
CHECK_PATH_LENGTH(argv[i]);
strcpy(paletteFilePath, argv[i]);
hasPalette = true;
} else if (strcmp(option, "-object") == 0) {
isObject = true;
} else if (strcmp(option, "-width") == 0) {
if (i + 1 >= argc)
FATAL_ERROR("No width following \"-width\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &width))
FATAL_ERROR("Failed to parse width.\n");
if (width < 1)
FATAL_ERROR("Width must be positive.\n");
} else {
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
ConvertToPng(imageFilePath, hasPalette ? paletteFilePath : NULL, isObject, width);
} else if (strcmp(command, "1bpp") == 0 || strcmp(command, "4bpp") == 0 || strcmp(command, "8bpp") == 0) {
if (argc < 3)
FATAL_ERROR("No image file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char imageFilePath[GBAGFX_MAX_PATH + 1];
strcpy(imageFilePath, argv[2]);
int numTiles = 0;
int bitDepth = command[0] - '0';
for (int i = 3; i < argc; i++) {
char *option = argv[i];
if (strcmp(option, "-num_tiles") == 0) {
if (i + 1 >= argc)
FATAL_ERROR("No number of tiles following \"-num_tiles\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &numTiles))
FATAL_ERROR("Failed to parse number of tiles.\n");
if (numTiles < 1)
FATAL_ERROR("Number of tiles must be positive.\n");
} else {
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
ConvertFromPng(imageFilePath, numTiles, bitDepth);
} else if (strcmp(command, "pal") == 0) {
if (argc < 3)
FATAL_ERROR("No palette file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char paletteFilePath[GBAGFX_MAX_PATH + 1];
strcpy(paletteFilePath, argv[2]);
ConvertToJascPalette(paletteFilePath);
} else if (strcmp(command, "gbapal") == 0) {
if (argc < 3)
FATAL_ERROR("No palette file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char paletteFilePath[GBAGFX_MAX_PATH + 1];
strcpy(paletteFilePath, argv[2]);
ConvertFromJascPalette(paletteFilePath);
} else if (strcmp(command, "lz") == 0) {
if (argc < 3)
FATAL_ERROR("No file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char path[GBAGFX_MAX_PATH + 1];
strcpy(path, argv[2]);
LZCompressFile(path);
} else if (strcmp(command, "unlz") == 0) {
if (argc < 3)
FATAL_ERROR("No file path arg.\n");
CHECK_PATH_LENGTH(argv[2]);
char path[GBAGFX_MAX_PATH + 1];
strcpy(path, argv[2]);
LZDecompressFile(path);
} else {
FATAL_ERROR("Unrecognized command \"%s\".\n", command);
}
return 0;
}

140
tools/gbagfx/util.c Normal file
View File

@@ -0,0 +1,140 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <limits.h>
#include "global.h"
#include "util.h"
bool ParseNumber(char *s, char **end, int radix, int *intValue)
{
char *localEnd;
if (end == NULL)
end = &localEnd;
errno = 0;
const long longValue = strtol(s, end, radix);
if (*end == s)
return false; // not a number
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
return false;
if (longValue > INT_MAX)
return false;
if (longValue < INT_MIN)
return false;
*intValue = (int)longValue;
return true;
}
char *GetFileExtension(char *path)
{
char *extension = path;
while (*extension != 0)
extension++;
while (extension > path && *extension != '.')
extension--;
if (extension == path)
return NULL;
extension++;
if (*extension == 0)
return NULL;
return extension;
}
void ExpectFileExtension(char *path, char *expectedExtension)
{
char *extension = GetFileExtension(path);
if (extension == NULL)
FATAL_ERROR("\"%s\" has no file extension.\n", path);
if (strcmp(extension, expectedExtension) != 0)
FATAL_ERROR("Unexpected file extension \"%s\". Expected \"%s\".\n", extension, expectedExtension);
}
void AddFileExtension(char *path, char *extension)
{
int pathLength = strlen(path);
int extensionLength = strlen(extension);
if (pathLength + 1 + extensionLength > GBAGFX_MAX_PATH)
FATAL_ERROR("\"%s\" is too long a path to add the extension \"%s\".\n", path, extension);
path[pathLength] = '.';
memcpy(path + pathLength + 1, extension, extensionLength);
path[pathLength + 1 + extensionLength] = 0;
}
void RemoveFileExtension(char *path)
{
char *extension = GetFileExtension(path);
if (extension == NULL)
FATAL_ERROR("\"%s\" doesn't have a file extension.\n", path);
extension--;
*extension = 0;
}
void ChangeFileExtension(char *path, char *extension)
{
RemoveFileExtension(path);
AddFileExtension(path, extension);
}
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
void WriteWholeFile(char *path, void *buffer, int bufferSize)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fwrite(buffer, bufferSize, 1, fp) != 1)
FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp);
}

17
tools/gbagfx/util.h Normal file
View File

@@ -0,0 +1,17 @@
// Copyright (c) 2015 YamaArashi
#ifndef UTIL_H
#define UTIL_H
#include <stdbool.h>
bool ParseNumber(char *s, char **end, int radix, int *intValue);
char *GetFileExtension(char *path);
void ExpectFileExtension(char *path, char *expectedExtension);
void AddFileExtension(char *path, char *extension);
void RemoveFileExtension(char *path);
void ChangeFileExtension(char *path, char *extension);
unsigned char *ReadWholeFile(char *path, int *size);
void WriteWholeFile(char *path, void *buffer, int bufferSize);
#endif // UTIL_H

1
tools/scaninc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
scaninc

13
tools/scaninc/Makefile Normal file
View File

@@ -0,0 +1,13 @@
CXX = g++
CXXFLAGS = -Wall -std=c++11 -O2
SRCS = scaninc.cpp
.PHONY: clean
scaninc: $(SRCS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@
clean:
$(RM) scaninc scaninc.exe

305
tools/scaninc/scaninc.cpp Normal file
View File

@@ -0,0 +1,305 @@
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <set>
#include <string>
#ifdef _MSC_VER
#define FATAL_INPUT_ERROR(format, ...) \
do { \
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, __VA_ARGS__); \
exit(1); \
} while (0)
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_INPUT_ERROR(format, ...) \
do { \
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, ##__VA_ARGS__); \
exit(1); \
} while (0)
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#define SCANINC_MAX_PATH 255
enum class IncDirectiveType {
None,
Include,
Incbin
};
class AsmFile
{
public:
AsmFile(std::string path);
~AsmFile();
IncDirectiveType ReadUntilIncDirective(std::string &path);
private:
char *m_buffer;
int m_pos;
int m_size;
int m_lineNum;
std::string m_path;
int GetChar()
{
if (m_pos >= m_size)
return -1;
int c = m_buffer[m_pos++];
if (c == '\r') {
if (m_pos < m_size && m_buffer[m_pos++] == '\n') {
m_lineNum++;
return '\n';
} else {
FATAL_INPUT_ERROR("CR line endings are not supported\n");
}
}
if (c == '\n')
m_lineNum++;
return c;
}
// No newline translation because it's not needed for any use of this function.
int PeekChar()
{
if (m_pos >= m_size)
return -1;
return m_buffer[m_pos];
}
void SkipTabsAndSpaces()
{
while (m_pos < m_size && (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' '))
m_pos++;
}
bool MatchIncDirective(std::string directiveName, std::string &path)
{
int length = directiveName.length();
int i;
for (i = 0; i < length && m_pos + i < m_size; i++)
if (directiveName[i] != m_buffer[m_pos + i])
return false;
if (i < length)
return false;
m_pos += length;
SkipTabsAndSpaces();
if (GetChar() != '"')
FATAL_INPUT_ERROR("no path after \".%s\" directive\n", directiveName.c_str());
path = ReadPath();
return true;
}
std::string ReadPath();
void SkipEndOfLineComment();
void SkipMultiLineComment();
void SkipString();
};
AsmFile::AsmFile(std::string path)
{
m_path = path;
FILE *fp = fopen(path.c_str(), "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str());
fseek(fp, 0, SEEK_END);
m_size = ftell(fp);
m_buffer = new char[m_size];
rewind(fp);
if (fread(m_buffer, m_size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path.c_str());
fclose(fp);
m_pos = 0;
m_lineNum = 1;
}
AsmFile::~AsmFile()
{
delete[] m_buffer;
}
IncDirectiveType AsmFile::ReadUntilIncDirective(std::string &path)
{
// At the beginning of each loop iteration, the current file position
// should be at the start of a line or at the end of the file.
for (;;) {
SkipTabsAndSpaces();
IncDirectiveType incDirectiveType = IncDirectiveType::None;
if (PeekChar() == '.') {
m_pos++;
if (MatchIncDirective("incbin", path))
incDirectiveType = IncDirectiveType::Incbin;
else if (MatchIncDirective("include", path))
incDirectiveType = IncDirectiveType::Include;
}
for (;;) {
int c = GetChar();
if (c == -1)
return incDirectiveType;
if (c == ';') {
SkipEndOfLineComment();
break;
} else if (c == '/' && PeekChar() == '*') {
m_pos++;
SkipMultiLineComment();
} else if (c == '"') {
SkipString();
} else if (c == '\n') {
break;
}
}
if (incDirectiveType != IncDirectiveType::None)
return incDirectiveType;
}
}
std::string AsmFile::ReadPath()
{
int length = 0;
int startPos = m_pos;
for (;;) {
int c = GetChar();
if (c == '"')
break;
if (c == -1)
FATAL_INPUT_ERROR("unexpected EOF in include string\n");
if (c == 0)
FATAL_INPUT_ERROR("unexpected NUL character in include string\n");
if (c == '\n')
FATAL_INPUT_ERROR("unexpected end of line character in include string\n");
if (c == '\\') {
c = GetChar();
if (c != '"')
FATAL_INPUT_ERROR("unknown escape \"\\%c\" in include string\n", c);
}
length++;
if (length > SCANINC_MAX_PATH)
FATAL_INPUT_ERROR("path is too long");
}
return std::string(m_buffer, startPos, length);
}
void AsmFile::SkipEndOfLineComment()
{
int c;
do {
c = GetChar();
} while (c != -1 && c != '\n');
}
void AsmFile::SkipMultiLineComment()
{
for (;;) {
int c = GetChar();
if (c == '*') {
if (PeekChar() == '/') {
m_pos++;
return;
}
} else if (c == -1) {
return;
}
}
}
void AsmFile::SkipString()
{
for (;;) {
int c = GetChar();
if (c == '"')
break;
if (c == -1)
FATAL_INPUT_ERROR("unexpected EOF in string\n");
if (c == '\\') {
c = GetChar();
}
}
}
int main(int argc, char **argv)
{
if (argc < 2)
FATAL_ERROR("Usage: scaninc ASM_FILE_PATH\n");
std::stack<std::string> filesToProcess;
std::set<std::string> dependencies;
filesToProcess.push(std::string(argv[1]));
while (!filesToProcess.empty()) {
AsmFile file(filesToProcess.top());
filesToProcess.pop();
IncDirectiveType incDirectiveType;
std::string path;
while ((incDirectiveType = file.ReadUntilIncDirective(path)) != IncDirectiveType::None) {
bool inserted = dependencies.insert(path).second;
if (inserted && incDirectiveType == IncDirectiveType::Include)
filesToProcess.push(path);
}
}
for (const std::string &path : dependencies) {
printf("%s\n", path.c_str());
}
}