diff options
Diffstat (limited to 'tools/gbagfx')
-rw-r--r-- | tools/gbagfx/Makefile | 7 | ||||
-rw-r--r-- | tools/gbagfx/gfx.c | 197 | ||||
-rw-r--r-- | tools/gbagfx/gfx.h | 17 | ||||
-rw-r--r-- | tools/gbagfx/huff.c | 398 | ||||
-rw-r--r-- | tools/gbagfx/huff.h | 38 | ||||
-rw-r--r-- | tools/gbagfx/main.c | 118 | ||||
-rw-r--r-- | tools/gbagfx/options.h | 2 |
7 files changed, 502 insertions, 275 deletions
diff --git a/tools/gbagfx/Makefile b/tools/gbagfx/Makefile index 339585b92..93bea4bdc 100644 --- a/tools/gbagfx/Makefile +++ b/tools/gbagfx/Makefile @@ -1,16 +1,19 @@ CC = gcc -CFLAGS = -Wall -Wextra -Werror -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK +CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -s -DPNG_SKIP_SETJMP_CHECK LIBS = -lpng -lz -SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c +SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c huff.c .PHONY: all clean all: gbagfx @: +gbagfx-debug: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h + $(CC) $(CFLAGS) -DDEBUG $(SRCS) -o $@ $(LDFLAGS) $(LIBS) + gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h $(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS) diff --git a/tools/gbagfx/gfx.c b/tools/gbagfx/gfx.c index da92771b9..f927deed9 100644 --- a/tools/gbagfx/gfx.c +++ b/tools/gbagfx/gfx.c @@ -4,7 +4,6 @@ #include <stdlib.h> #include <stdint.h> #include <stdbool.h> -#include <string.h> #include "global.h" #include "gfx.h" #include "util.h" @@ -19,140 +18,6 @@ #define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8) -static inline void swap_bytes(unsigned char * orig, unsigned char * dest) { - unsigned char tmp = *orig; - *orig = *dest; - *dest = tmp; -} - -static inline unsigned char swap_nybbles(unsigned char orig) -{ - return (orig >> 4) | (orig << 4); -} - -static inline void swap_bytes_nybswap(unsigned char * orig, unsigned char * dest) { - unsigned char tmp = swap_nybbles(*orig); - *orig = swap_nybbles(*dest); - *dest = tmp; -} - -static inline unsigned char reverse_bits(unsigned char orig) { - unsigned char dest = 0; - for (int i = 0; i < 8; i++) - { - dest <<= 1; - dest |= orig & 1; - orig >>= 1; - } - return dest; -} - -static void vflip(unsigned char * tile, int bitDepth) { - for (int x = 0; x < bitDepth; x++) - { - unsigned char * col = tile + x; - swap_bytes(col + 0 * bitDepth, col + 7 * bitDepth); - swap_bytes(col + 1 * bitDepth, col + 6 * bitDepth); - swap_bytes(col + 2 * bitDepth, col + 5 * bitDepth); - swap_bytes(col + 3 * bitDepth, col + 4 * bitDepth); - } -} - -static void hflip(unsigned char * tile, int bitDepth) { - for (int y = 0; y < 8; y++) - { - unsigned char * row = tile + y * bitDepth; - switch (bitDepth) - { - case 1: - *row = reverse_bits(*row); - break; - case 4: - swap_bytes_nybswap(row + 0, row + 3); - swap_bytes_nybswap(row + 1, row + 2); - break; - case 8: - swap_bytes(row + 0, row + 56); - swap_bytes(row + 8, row + 48); - swap_bytes(row + 16, row + 40); - swap_bytes(row + 24, row + 32); - break; - } - } -} - -static unsigned char * ApplyTilemap(struct Image *image, unsigned char * buffer, int bitDepth) -{ - int tileSize = bitDepth * 8; - unsigned char * tiles = calloc(image->tileMap.numTiles, tileSize); - int i; - struct Tile tileInfo; - - for (i = 0; i < image->tileMap.numTiles; i++) { - tileInfo = image->tileMap.data[i]; - unsigned char * tile = tiles + i * tileSize; - memcpy(tile, buffer + tileInfo.index * tileSize, tileSize); - if (tileInfo.xflip) - hflip(tile, bitDepth); - if (tileInfo.yflip) - vflip(tile, bitDepth); - } - free(buffer); - return tiles; -} - -static unsigned char * BuildTilemap(struct Image *image, unsigned char * buffer, int * bufferSize) -{ - int tileSize = image->bitDepth * 8; - unsigned char * outputPixels = calloc(1024, tileSize); - int nTilesIn = image->height * image->width / 64; - image->tileMap.data = calloc(nTilesIn, sizeof(struct Tilemap)); - image->tileMap.numTiles = nTilesIn; - int nTilesOut = 0; - unsigned char curTile1[tileSize]; - unsigned char curTile2[tileSize]; - - for (int i = 0; i < nTilesIn; i++) { - bool xflip = false; - bool yflip = false; - int j; - memcpy(curTile1, buffer + i * tileSize, tileSize); - - for (j = 0; j < nTilesOut; j++) { - memcpy(curTile2, outputPixels + j * tileSize, tileSize); - if (memcmp(curTile1, curTile2, tileSize) == 0) - break; - xflip = true; - hflip(curTile2, image->bitDepth); - if (memcmp(curTile1, curTile2, tileSize) == 0) - break; - yflip = true; - vflip(curTile2, image->bitDepth); - if (memcmp(curTile1, curTile2, tileSize) == 0) - break; - xflip = false; - hflip(curTile2, image->bitDepth); - if (memcmp(curTile1, curTile2, tileSize) == 0) - break; - yflip = false; - } - image->tileMap.data[i].index = j; - image->tileMap.data[i].xflip = xflip; - image->tileMap.data[i].yflip = yflip; - image->tileMap.data[i].palno = 0; - if (j >= nTilesOut) { - if (nTilesOut >= 1024) - FATAL_ERROR("Cannot reduce image to 1024 or fewer tiles.\n"); - memcpy(outputPixels + nTilesOut * tileSize, curTile1, tileSize); - nTilesOut++; - } - } - - free(buffer); - *bufferSize = nTilesOut * tileSize; - return outputPixels; -} - static void AdvanceMetatilePosition(int *subTileX, int *subTileY, int *metatileX, int *metatileY, int metatilesWide, int metatileWidth, int metatileHeight) { (*subTileX)++; @@ -345,21 +210,15 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int int fileSize; unsigned char *buffer = ReadWholeFile(path, &fileSize); - int numTiles; - if (image->hasTilemap) { - buffer = ApplyTilemap(image, buffer, bitDepth); - numTiles = image->tileMap.numTiles; - } - else - numTiles = fileSize / tileSize; + int numTiles = fileSize / tileSize; int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth; if (tilesWidth % metatileWidth != 0) - FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth); + FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth); if (tilesHeight % metatileHeight != 0) - FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight); + FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight); image->width = tilesWidth * 8; image->height = tilesHeight * 8; @@ -432,9 +291,6 @@ void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int m break; } - if (image->hasTilemap) - buffer = BuildTilemap(image, buffer, &bufferSize); - WriteWholeFile(path, buffer, bufferSize); free(buffer); @@ -444,11 +300,6 @@ void FreeImage(struct Image *image) { free(image->pixels); image->pixels = NULL; - if (image->hasTilemap && image->tileMap.data != NULL) { - free(image->tileMap.data); - image->tileMap.data = NULL; - image->tileMap.numTiles = 0; - } } void ReadGbaPalette(char *path, struct Palette *palette) @@ -491,45 +342,3 @@ void WriteGbaPalette(char *path, struct Palette *palette) fclose(fp); } - -void ReadGbaTilemap(char *path, struct Tilemap *tileMap) -{ - int fileSize; - unsigned char *data = ReadWholeFile(path, &fileSize); - - if (fileSize % 2 != 0) - FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize); - - tileMap->numTiles = fileSize / 2; - tileMap->data = malloc(tileMap->numTiles * sizeof(struct Tile)); - - for (int i = 0; i < tileMap->numTiles; i++) - { - uint16_t raw = data[2 * i + 0] | (data[2 * i + 1] << 8); - tileMap->data[i].index = raw & 0x3FF; - tileMap->data[i].xflip = raw & 0x400 ? 1 : 0; - tileMap->data[i].yflip = raw & 0x800 ? 1 : 0; - tileMap->data[i].palno = raw >> 12; - } - - free(data); -} - -void WriteGbaTilemap(char *path, struct Tilemap *tileMap) -{ - FILE *fp = fopen(path, "wb"); - - if (fp == NULL) - FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); - - for (int i = 0; i < tileMap->numTiles; i++) { - uint16_t raw = tileMap->data[i].index - | (tileMap->data[i].xflip << 10) - | (tileMap->data[i].yflip << 11) - | (tileMap->data[i].palno << 12); - fputc(raw & 0xFF, fp); - fputc(raw >> 8, fp); - } - - fclose(fp); -} diff --git a/tools/gbagfx/gfx.h b/tools/gbagfx/gfx.h index 15a3c6a6c..5355ced85 100644 --- a/tools/gbagfx/gfx.h +++ b/tools/gbagfx/gfx.h @@ -17,18 +17,6 @@ struct Palette { int numColors; }; -struct __attribute__((packed)) Tile { - unsigned short index:10; - unsigned short xflip:1; - unsigned short yflip:1; - unsigned short palno:4; -}; - -struct Tilemap { - struct Tile *data; - int numTiles; -}; - struct Image { int width; int height; @@ -37,9 +25,6 @@ struct Image { bool hasPalette; struct Palette palette; bool hasTransparency; - struct Tilemap tileMap; - bool hasTilemap; - }; void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors); @@ -47,7 +32,5 @@ void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int m void FreeImage(struct Image *image); void ReadGbaPalette(char *path, struct Palette *palette); void WriteGbaPalette(char *path, struct Palette *palette); -void ReadGbaTilemap(char *path, struct Tilemap *tileMap); -void WriteGbaTilemap(char *path, struct Tilemap *tileMap); #endif // GFX_H diff --git a/tools/gbagfx/huff.c b/tools/gbagfx/huff.c new file mode 100644 index 000000000..143ed79be --- /dev/null +++ b/tools/gbagfx/huff.c @@ -0,0 +1,398 @@ +#include <stdbool.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <stdint.h> +#include "global.h" +#include "huff.h" + +static int cmp_tree(const void * a0, const void * b0) { + return ((struct HuffData *)a0)->value - ((struct HuffData *)b0)->value; +} + +typedef int (*cmpfun)(const void *, const void *); + +int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) { + /* + * Out-of-place mergesort (stable sort) + * Returns 1 on success, 0 on failure + */ + void * leftPtr; + void * rightPtr; + void * leftEnd; + void * rightEnd; + int i; + + switch (count) { + case 0: + // Should never be here + return 0; + + case 1: + // Nothing to do here + break; + + case 2: + // Swap the two entries if the right one compares higher. + if (cmp(data, data + size) > 0) { + memcpy(buffer, data, size); + memcpy(data, data + size, size); + memcpy(data + size, buffer, size); + } + break; + default: + // Merge sort out-of-place. + leftPtr = data; + leftEnd = rightPtr = data + count / 2 * size; + rightEnd = data + count * size; + + // Sort the left half + if (!msort_r(leftPtr, count / 2, size, cmp, buffer)) + return 0; + + // Sort the right half + if (!msort_r(rightPtr, count / 2 + (count & 1), size, cmp, buffer)) + return 0; + + // Merge the sorted halves out of place + i = 0; + do { + if (cmp(leftPtr, rightPtr) <= 0) { + memcpy(buffer + i * size, leftPtr, size); + leftPtr += size; + } else { + memcpy(buffer + i * size, rightPtr, size); + rightPtr += size; + } + + } while (++i < count && leftPtr < leftEnd && rightPtr < rightEnd); + + // Copy the remainder + if (i < count) { + if (leftPtr < leftEnd) { + memcpy(buffer + i * size, leftPtr, leftEnd - leftPtr); + } + else { + memcpy(buffer + i * size, rightPtr, rightEnd - rightPtr); + } + } + + // Copy the merged data back + memcpy(data, buffer, count * size); + break; + } + + return 1; +} + +int msort(void * data, size_t count, size_t size, cmpfun cmp) { + void * buffer = malloc(count * size); + if (buffer == NULL) return 0; + int result = msort_r(data, count, size, cmp, buffer); + free(buffer); + return result; +} + +static void write_tree(unsigned char * dest, HuffNode_t * tree, int nitems, struct BitEncoding * encoding) { + /* + * The example used to guide this function encodes the tree in a + * breadth-first manner. We attempt to emulate that here. + */ + + int i, j, k; + + // There are (2 * nitems - 1) nodes in the binary tree. Allocate that. + HuffNode_t * traversal = calloc(2 * nitems - 1, sizeof(HuffNode_t)); + if (traversal == NULL) + FATAL_ERROR("Fatal error while compressing Huff file.\n"); + + // The first node is the root of the tree. + traversal[0] = *tree; + i = 1; + + // Copy the tree into a breadth-first ordering using brute force. + for (int depth = 1; i < 2 * nitems - 1; depth++) { + // Consider every possible path up to the current depth. + for (j = 0; i < 2 * nitems - 1 && j < 1 << depth; j++) { + // The index of the path is used to encode the path itself. + // Start from the most significant relevant bit and work our way down. + // Keep track of the current and previous nodes. + HuffNode_t * currNode = traversal; + HuffNode_t * parent = NULL; + for (k = 0; k < depth; k++) { + if (currNode->header.isLeaf) + break; + parent = currNode; + if ((j >> (depth - k - 1)) & 1) + currNode = currNode->branch.right; + else + currNode = currNode->branch.left; + } + // Check that the length of the current path equals the current depth. + if (k == depth) { + // Make sure we can encode the current branch. + // Bail here if we cannot. + // This is only applicable for 8-bit encodings. + if (traversal + i - parent > 128) + FATAL_ERROR("Fatal error while compressing Huff file: unable to encode binary tree.\n"); + // Copy the current node, and update its parent. + traversal[i] = *currNode; + if (parent != NULL) { + if ((j & 1) == 1) + parent->branch.right = traversal + i; + else + parent->branch.left = traversal + i; + } + // Encode the path through the tree in the lookup table + if (traversal[i].header.isLeaf) { + encoding[traversal[i].leaf.key].nbits = depth; + encoding[traversal[i].leaf.key].bitstring = j; + } + i++; + } + } + } + + // Encode the size of the tree. + // This is used by the decompressor to skip the tree. + dest[4] = nitems - 1; + + // Encode each node in the tree. + for (i = 0; i < 2 * nitems - 1; i++) { + HuffNode_t * currNode = traversal + i; + if (currNode->header.isLeaf) { + dest[5 + i] = traversal[i].leaf.key; + } else { + dest[5 + i] = (((currNode->branch.right - traversal - i) / 2) - 1); + if (currNode->branch.left->header.isLeaf) + dest[5 + i] |= 0x80; + if (currNode->branch.right->header.isLeaf) + dest[5 + i] |= 0x40; + } + } + + free(traversal); +} + +static inline void write_32_le(unsigned char * dest, int * destPos, uint32_t * buff, int * buffPos) { + dest[*destPos] = *buff; + dest[*destPos + 1] = *buff >> 8; + dest[*destPos + 2] = *buff >> 16; + dest[*destPos + 3] = *buff >> 24; + *destPos += 4; + *buff = 0; + *buffPos = 0; +} + +static inline void read_32_le(unsigned char * src, int * srcPos, uint32_t * buff) { + uint32_t tmp = src[*srcPos]; + tmp |= src[*srcPos + 1] << 8; + tmp |= src[*srcPos + 2] << 16; + tmp |= src[*srcPos + 3] << 24; + *srcPos += 4; + *buff = tmp; +} + +static void write_bits(unsigned char * dest, int * destPos, struct BitEncoding * encoding, int value, uint32_t * buff, int * buffBits) { + int nbits = encoding[value].nbits; + uint32_t bitstring = encoding[value].bitstring; + + if (*buffBits + nbits >= 32) { + int diff = *buffBits + nbits - 32; + *buff <<= nbits - diff; + *buff |= bitstring >> diff; + bitstring &= ~(1 << diff); + nbits = diff; + write_32_le(dest, destPos, buff, buffBits); + } + if (nbits != 0) { + *buff <<= nbits; + *buff |= bitstring; + *buffBits += nbits; + } +} + +/* +======================================= +MAIN COMPRESSION/DECOMPRESSION ROUTINES +======================================= + */ + +unsigned char * HuffCompress(unsigned char * src, int srcSize, int * compressedSize_p, int bitDepth) { + if (srcSize <= 0) + goto fail; + + int worstCaseDestSize = 4 + (2 << bitDepth) + srcSize * 3; + + unsigned char *dest = malloc(worstCaseDestSize); + if (dest == NULL) + goto fail; + + int nitems = 1 << bitDepth; + + HuffNode_t * freqs = calloc(nitems, sizeof(HuffNode_t)); + if (freqs == NULL) + goto fail; + + struct BitEncoding * encoding = calloc(nitems, sizeof(struct BitEncoding)); + if (encoding == NULL) + goto fail; + + // Set up the frequencies table. This will inform the tree. + for (int i = 0; i < nitems; i++) { + freqs[i].header.isLeaf = 1; + freqs[i].header.value = 0; + freqs[i].leaf.key = i; + } + + // Count each nybble or byte. + for (int i = 0; i < srcSize; i++) { + if (bitDepth == 8) { + freqs[src[i]].header.value++; + } else { + freqs[src[i] >> 4].header.value++; + freqs[src[i] & 0xF].header.value++; + } + } + +#ifdef DEBUG + for (int i = 0; i < nitems; i++) { + fprintf(stderr, "%d: %d\n", i, freqs[i].header.value); + } +#endif // DEBUG + + // Sort the frequency table. + if (!msort(freqs, nitems, sizeof(HuffNode_t), cmp_tree)) + goto fail; + + // Prune zero-frequency values. + for (int i = 0; i < nitems; i++) { + if (freqs[i].header.value != 0) { + if (i > 0) { + for (int j = i; j < nitems; j++) { + freqs[j - i] = freqs[j]; + } + nitems -= i; + } + break; + } + // This should never happen: + if (i == nitems - 1) + goto fail; + } + + HuffNode_t * tree = calloc(nitems * 2 - 1, sizeof(HuffNode_t)); + if (tree == NULL) + goto fail; + + // Iteratively collapse the two least frequent nodes. + HuffNode_t * endptr = freqs + nitems - 2; + + for (int i = 0; i < nitems - 1; i++) { + HuffNode_t * left = freqs; + HuffNode_t * right = freqs + 1; + tree[i * 2] = *right; + tree[i * 2 + 1] = *left; + for (int j = 0; j < nitems - i - 2; j++) + freqs[j] = freqs[j + 2]; + endptr->header.isLeaf = 0; + endptr->header.value = tree[i * 2].header.value + tree[i * 2 + 1].header.value; + endptr->branch.left = tree + i * 2; + endptr->branch.right = tree + i * 2 + 1; + endptr--; + if (i < nitems - 2 && !msort(freqs, nitems - i - 1, sizeof(HuffNode_t), cmp_tree)) + goto fail; + } + + // Write the tree breadth-first, and create the path lookup table. + write_tree(dest, freqs, nitems, encoding); + + free(tree); + free(freqs); + + // Encode the data itself. + int destPos = 4 + nitems * 2; + uint32_t destBuf = 0; + uint32_t srcBuf = 0; + int destBitPos = 0; + + for (int srcPos = 0; srcPos < srcSize;) { + read_32_le(src, &srcPos, &srcBuf); + for (int i = 0; i < 32 / bitDepth; i++) { + write_bits(dest, &destPos, encoding, srcBuf & (0xFF >> (8 - bitDepth)), &destBuf, &destBitPos); + srcBuf >>= bitDepth; + } + } + + if (destBitPos != 0) { + write_32_le(dest, &destPos, &destBuf, &destBitPos); + } + + free(encoding); + + // Write the header. + dest[0] = bitDepth | 0x20; + dest[1] = srcSize; + dest[2] = srcSize >> 8; + dest[3] = srcSize >> 16; + *compressedSize_p = (destPos + 3) & ~3; + return dest; + +fail: + FATAL_ERROR("Fatal error while compressing Huff file.\n"); +} + +unsigned char * HuffDecompress(unsigned char * src, int srcSize, int * uncompressedSize_p) { + if (srcSize < 4) + goto fail; + + int bitDepth = *src & 15; + if (bitDepth != 4 && bitDepth != 8) + goto fail; + + int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; + + unsigned char *dest = malloc(destSize); + + if (dest == NULL) + goto fail; + + int treePos = 5; + int treeSize = (src[4] + 1) * 2; + int srcPos = 4 + treeSize; + int destPos = 0; + int curValPos = 0; + uint32_t destTmp = 0; + uint32_t window; + + for (;;) + { + if (srcPos >= srcSize) + goto fail; + read_32_le(src, &srcPos, &window); + for (int i = 0; i < 32; i++) { + int curBit = (window >> 31) & 1; + unsigned char treeView = src[treePos]; + bool isLeaf = ((treeView << curBit) & 0x80) != 0; + treePos &= ~1; // align + treePos += ((treeView & 0x3F) + 1) * 2 + curBit; + if (isLeaf) { + destTmp >>= bitDepth; + destTmp |= (src[treePos] << (32 - bitDepth)); + curValPos++; + if (curValPos == 32 / bitDepth) { + write_32_le(dest, &destPos, &destTmp, &curValPos); + if (destPos == destSize) { + *uncompressedSize_p = destSize; + return dest; + } + } + treePos = 5; + } + window <<= 1; + } + } + +fail: + FATAL_ERROR("Fatal error while decompressing Huff file.\n"); +} diff --git a/tools/gbagfx/huff.h b/tools/gbagfx/huff.h new file mode 100644 index 000000000..6002fe954 --- /dev/null +++ b/tools/gbagfx/huff.h @@ -0,0 +1,38 @@ +#ifndef HUFF_H +#define HUFF_H + +union HuffNode; + +struct HuffData { + unsigned value:31; + unsigned isLeaf:1; +}; + +struct HuffLeaf { + struct HuffData header; + unsigned char key; +}; + +struct HuffBranch { + struct HuffData header; + union HuffNode * left; + union HuffNode * right; +}; + +union HuffNode { + struct HuffData header; + struct HuffLeaf leaf; + struct HuffBranch branch; +}; + +typedef union HuffNode HuffNode_t; + +struct BitEncoding { + unsigned long long nbits:6; + unsigned long long bitstring:58; +}; + +unsigned char * HuffCompress(unsigned char * buffer, int srcSize, int * compressedSize_p, int bitDepth); +unsigned char * HuffDecompress(unsigned char * buffer, int srcSize, int * uncompressedSize_p); + +#endif //HUFF_H diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c index 2174e37a5..aa0681fb6 100644 --- a/tools/gbagfx/main.c +++ b/tools/gbagfx/main.c @@ -12,6 +12,7 @@ #include "lz.h" #include "rl.h" #include "font.h" +#include "huff.h" struct CommandHandler { @@ -34,17 +35,6 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions * image.hasPalette = false; } - if (options->tilemapFilePath != NULL) - { - ReadGbaTilemap(options->tilemapFilePath, &image.tileMap); - image.hasTilemap = true; - } - else - { - image.tileMap.data = NULL; - image.hasTilemap = false; - } - ReadImage(inputPath, options->width, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette); image.hasTransparency = options->hasTransparency; @@ -59,17 +49,11 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions * struct Image image; image.bitDepth = options->bitDepth; - image.hasTilemap = options->tilemapFilePath == NULL ? false : true; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; ReadPng(inputPath, &image); WriteImage(outputPath, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette); - if (image.hasTilemap) - WriteGbaTilemap(options->tilemapFilePath, &image.tileMap); - FreeImage(&image); } @@ -78,7 +62,6 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a char *inputFileExtension = GetFileExtension(inputPath); struct GbaToPngOptions options; options.paletteFilePath = NULL; - options.tilemapFilePath = NULL; options.bitDepth = inputFileExtension[0] - '0'; options.hasTransparency = false; options.width = 1; @@ -98,15 +81,6 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a options.paletteFilePath = argv[i]; } - else if (strcmp(option, "-tilemap") == 0) - { - if (i + 1 >= argc) - FATAL_ERROR("No tilemap file path following \"-tilemap\".\n"); - - i++; - - options.tilemapFilePath = argv[i]; - } else if (strcmp(option, "-object") == 0) { options.hasTransparency = true; @@ -171,7 +145,6 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a options.bitDepth = bitDepth; options.metatileWidth = 1; options.metatileHeight = 1; - options.tilemapFilePath = NULL; for (int i = 3; i < argc; i++) { @@ -190,14 +163,6 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a if (options.numTiles < 1) FATAL_ERROR("Number of tiles must be positive.\n"); } - else if (strcmp(option, "-tilemap") == 0) - { - if (i + 1 >= argc) - FATAL_ERROR("No tilemap path following \"-tilemap\".\n"); - - i++; - options.tilemapFilePath = argv[i]; - } else if (strcmp(option, "-mwidth") == 0) { if (i + 1 >= argc) @@ -290,10 +255,6 @@ void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNU { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - ReadLatinFont(inputPath, &image); WritePng(outputPath, &image); @@ -304,10 +265,6 @@ void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNU { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - image.bitDepth = 2; ReadPng(inputPath, &image); @@ -320,10 +277,6 @@ void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - ReadHalfwidthJapaneseFont(inputPath, &image); WritePng(outputPath, &image); @@ -334,10 +287,6 @@ void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - image.bitDepth = 2; ReadPng(inputPath, &image); @@ -350,10 +299,6 @@ void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - ReadFullwidthJapaneseFont(inputPath, &image); WritePng(outputPath, &image); @@ -364,10 +309,6 @@ void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, { struct Image image; - image.hasTilemap = false; - image.tileMap.data = NULL; - image.tileMap.numTiles = 0; - image.bitDepth = 2; ReadPng(inputPath, &image); @@ -485,6 +426,61 @@ void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSE free(uncompressedData); } +void HandleHuffCompressCommand(char *inputPath, char *outputPath, int argc, char **argv) +{ + int fileSize; + int bitDepth = 4; + + for (int i = 3; i < argc; i++) + { + char *option = argv[i]; + + if (strcmp(option, "-depth") == 0) + { + if (i + 1 >= argc) + FATAL_ERROR("No size following \"-depth\".\n"); + + i++; + + if (!ParseNumber(argv[i], NULL, 10, &bitDepth)) + FATAL_ERROR("Failed to parse bit depth.\n"); + + if (bitDepth != 4 && bitDepth != 8) + FATAL_ERROR("GBA only supports bit depth of 4 or 8.\n"); + } + else + { + FATAL_ERROR("Unrecognized option \"%s\".\n", option); + } + } + + unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); + + int compressedSize; + unsigned char *compressedData = HuffCompress(buffer, fileSize, &compressedSize, bitDepth); + + free(buffer); + + WriteWholeFile(outputPath, compressedData, compressedSize); + + free(compressedData); +} + +void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ + int fileSize; + unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); + + int uncompressedSize; + unsigned char *uncompressedData = HuffDecompress(buffer, fileSize, &uncompressedSize); + + free(buffer); + + WriteWholeFile(outputPath, uncompressedData, uncompressedSize); + + free(uncompressedData); +} + int main(int argc, char **argv) { if (argc < 3) @@ -507,7 +503,9 @@ int main(int argc, char **argv) { "png", "hwjpnfont", HandlePngToHalfwidthJapaneseFontCommand }, { "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand }, { "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand }, + { NULL, "huff", HandleHuffCompressCommand }, { NULL, "lz", HandleLZCompressCommand }, + { "huff", NULL, HandleHuffDecompressCommand }, { "lz", NULL, HandleLZDecompressCommand }, { NULL, "rl", HandleRLCompressCommand }, { "rl", NULL, HandleRLDecompressCommand }, diff --git a/tools/gbagfx/options.h b/tools/gbagfx/options.h index 463ee2455..2ff3967a4 100644 --- a/tools/gbagfx/options.h +++ b/tools/gbagfx/options.h @@ -7,7 +7,6 @@ struct GbaToPngOptions { char *paletteFilePath; - char *tilemapFilePath; int bitDepth; bool hasTransparency; int width; @@ -16,7 +15,6 @@ struct GbaToPngOptions { }; struct PngToGbaOptions { - char *tilemapFilePath; int numTiles; int bitDepth; int metatileWidth; |