diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/gbagfx/Makefile | 4 | ||||
-rw-r--r-- | tools/gbagfx/main.c | 33 | ||||
-rw-r--r-- | tools/gbagfx/rl.c | 143 | ||||
-rw-r--r-- | tools/gbagfx/rl.h | 9 |
4 files changed, 187 insertions, 2 deletions
diff --git a/tools/gbagfx/Makefile b/tools/gbagfx/Makefile index 6c8e5e1fa..de4ea5c42 100644 --- a/tools/gbagfx/Makefile +++ b/tools/gbagfx/Makefile @@ -4,11 +4,11 @@ CFLAGS = -Wall -Wextra -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK LIBS = -lpng -lz -SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c util.c font.c +SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c .PHONY: clean -gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h util.h font.h +gbagfx: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h $(CC) $(CFLAGS) $(SRCS) -o $@ $(LIBS) clean: diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c index e33e061b2..54a3c600d 100644 --- a/tools/gbagfx/main.c +++ b/tools/gbagfx/main.c @@ -9,6 +9,7 @@ #include "convert_png.h" #include "jasc_pal.h" #include "lz.h" +#include "rl.h" #include "font.h" struct CommandHandler @@ -289,6 +290,36 @@ void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSE free(uncompressedData); } +void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ + int fileSize; + unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); + + int compressedSize; + unsigned char *compressedData = RLCompress(buffer, fileSize, &compressedSize); + + free(buffer); + + WriteWholeFile(outputPath, compressedData, compressedSize); + + free(compressedData); +} + +void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED) +{ + int fileSize; + unsigned char *buffer = ReadWholeFile(inputPath, &fileSize); + + int uncompressedSize; + unsigned char *uncompressedData = RLDecompress(buffer, fileSize, &uncompressedSize); + + free(buffer); + + WriteWholeFile(outputPath, uncompressedData, uncompressedSize); + + free(uncompressedData); +} + int main(int argc, char **argv) { if (argc < 3) @@ -312,6 +343,8 @@ int main(int argc, char **argv) { "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand }, { NULL, "lz", HandleLZCompressCommand }, { "lz", NULL, HandleLZDecompressCommand }, + { NULL, "rl", HandleRLCompressCommand }, + { "rl", NULL, HandleRLDecompressCommand }, { NULL, NULL, NULL } }; diff --git a/tools/gbagfx/rl.c b/tools/gbagfx/rl.c new file mode 100644 index 000000000..e90ad808b --- /dev/null +++ b/tools/gbagfx/rl.c @@ -0,0 +1,143 @@ +// Copyright (c) 2016 YamaArashi + +#include <stdlib.h> +#include <stdbool.h> +#include "global.h" +#include "rl.h" + +unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize) +{ + if (srcSize < 4) + goto fail; + + int destSize = (src[3] << 16) | (src[2] << 8) | src[1]; + + unsigned char *dest = malloc(destSize); + + if (dest == NULL) + goto fail; + + int srcPos = 4; + int destPos = 0; + + for (;;) + { + if (srcPos >= srcSize) + goto fail; + + unsigned char flags = src[srcPos++]; + bool compressed = ((flags & 0x80) != 0); + + if (compressed) + { + int length = (flags & 0x7F) + 3; + unsigned char data = src[srcPos++]; + + for (int i = 0; i < length; i++) + dest[destPos++] = data; + } + else + { + int length = (flags & 0x7F) + 1; + + for (int i = 0; i < length; i++) + dest[destPos++] = src[srcPos++]; + } + + if (destPos == destSize) + { + *uncompressedSize = destSize; + return dest; + } + } + +fail: + FATAL_ERROR("Fatal error while decompressing RL file.\n"); +} + +unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize) +{ + if (srcSize <= 0) + goto fail; + + int worstCaseDestSize = 4 + srcSize * 2; + + // Round up to the next multiple of four. + worstCaseDestSize = (worstCaseDestSize + 3) & ~3; + + unsigned char *dest = malloc(worstCaseDestSize); + + if (dest == NULL) + goto fail; + + // header + dest[0] = 0x30; // RL 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 (;;) + { + bool compress = false; + int uncompressedStart = srcPos; + int uncompressedLength = 0; + + while (srcPos < srcSize && uncompressedLength < (0x7F + 1)) + { + compress = (srcPos + 2 < srcSize && src[srcPos] == src[srcPos + 1] && src[srcPos] == src[srcPos + 2]); + + if (compress) + break; + + srcPos++; + uncompressedLength++; + } + + if (uncompressedLength > 0) + { + dest[destPos++] = uncompressedLength - 1; + + for (int i = 0; i < uncompressedLength; i++) + dest[destPos++] = src[uncompressedStart + i]; + } + + if (compress) + { + unsigned char data = src[srcPos]; + int compressedLength = 0; + + while (compressedLength < (0x7F + 3) + && srcPos + compressedLength < srcSize + && src[srcPos + compressedLength] == data) + { + compressedLength++; + } + + dest[destPos++] = 0x80 | (compressedLength - 3); + dest[destPos++] = data; + + srcPos += compressedLength; + } + + 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; + } + } + +fail: + FATAL_ERROR("Fatal error while compressing RL file.\n"); +} diff --git a/tools/gbagfx/rl.h b/tools/gbagfx/rl.h new file mode 100644 index 000000000..02ad8d6d3 --- /dev/null +++ b/tools/gbagfx/rl.h @@ -0,0 +1,9 @@ +// Copyright (c) 2016 YamaArashi + +#ifndef RL_H +#define RL_H + +unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize); +unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize); + +#endif // RL_H |