diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/gbagfx/.gitignore | 1 | ||||
-rw-r--r-- | tools/gbagfx/Makefile | 15 | ||||
-rw-r--r-- | tools/gbagfx/convert_png.c | 167 | ||||
-rw-r--r-- | tools/gbagfx/convert_png.h | 11 | ||||
-rw-r--r-- | tools/gbagfx/gfx.c | 332 | ||||
-rw-r--r-- | tools/gbagfx/gfx.h | 36 | ||||
-rw-r--r-- | tools/gbagfx/global.h | 35 | ||||
-rw-r--r-- | tools/gbagfx/jasc_pal.c | 164 | ||||
-rw-r--r-- | tools/gbagfx/jasc_pal.h | 9 | ||||
-rw-r--r-- | tools/gbagfx/lz.c | 143 | ||||
-rw-r--r-- | tools/gbagfx/lz.h | 9 | ||||
-rw-r--r-- | tools/gbagfx/main.c | 260 | ||||
-rw-r--r-- | tools/gbagfx/util.c | 140 | ||||
-rw-r--r-- | tools/gbagfx/util.h | 17 | ||||
-rw-r--r-- | tools/scaninc/.gitignore | 1 | ||||
-rw-r--r-- | tools/scaninc/Makefile | 13 | ||||
-rw-r--r-- | tools/scaninc/scaninc.cpp | 305 |
17 files changed, 1658 insertions, 0 deletions
diff --git a/tools/gbagfx/.gitignore b/tools/gbagfx/.gitignore new file mode 100644 index 000000000..dbbb3f04c --- /dev/null +++ b/tools/gbagfx/.gitignore @@ -0,0 +1 @@ +gbagfx diff --git a/tools/gbagfx/Makefile b/tools/gbagfx/Makefile new file mode 100644 index 000000000..1bf37e301 --- /dev/null +++ b/tools/gbagfx/Makefile @@ -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 diff --git a/tools/gbagfx/convert_png.c b/tools/gbagfx/convert_png.c new file mode 100644 index 000000000..8a3d03943 --- /dev/null +++ b/tools/gbagfx/convert_png.c @@ -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); +} diff --git a/tools/gbagfx/convert_png.h b/tools/gbagfx/convert_png.h new file mode 100644 index 000000000..55d3d6942 --- /dev/null +++ b/tools/gbagfx/convert_png.h @@ -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 diff --git a/tools/gbagfx/gfx.c b/tools/gbagfx/gfx.c new file mode 100644 index 000000000..71459c607 --- /dev/null +++ b/tools/gbagfx/gfx.c @@ -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); +} diff --git a/tools/gbagfx/gfx.h b/tools/gbagfx/gfx.h new file mode 100644 index 000000000..473640d44 --- /dev/null +++ b/tools/gbagfx/gfx.h @@ -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 diff --git a/tools/gbagfx/global.h b/tools/gbagfx/global.h new file mode 100644 index 000000000..3ea5966af --- /dev/null +++ b/tools/gbagfx/global.h @@ -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 diff --git a/tools/gbagfx/jasc_pal.c b/tools/gbagfx/jasc_pal.c new file mode 100644 index 000000000..02b6ed915 --- /dev/null +++ b/tools/gbagfx/jasc_pal.c @@ -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); +} diff --git a/tools/gbagfx/jasc_pal.h b/tools/gbagfx/jasc_pal.h new file mode 100644 index 000000000..b60b31fc8 --- /dev/null +++ b/tools/gbagfx/jasc_pal.h @@ -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 diff --git a/tools/gbagfx/lz.c b/tools/gbagfx/lz.c new file mode 100644 index 000000000..7669dab9a --- /dev/null +++ b/tools/gbagfx/lz.c @@ -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; + } + } + } +} diff --git a/tools/gbagfx/lz.h b/tools/gbagfx/lz.h new file mode 100644 index 000000000..164d62279 --- /dev/null +++ b/tools/gbagfx/lz.h @@ -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 diff --git a/tools/gbagfx/main.c b/tools/gbagfx/main.c new file mode 100644 index 000000000..8a750f03a --- /dev/null +++ b/tools/gbagfx/main.c @@ -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; +} diff --git a/tools/gbagfx/util.c b/tools/gbagfx/util.c new file mode 100644 index 000000000..180b93d9b --- /dev/null +++ b/tools/gbagfx/util.c @@ -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); +} diff --git a/tools/gbagfx/util.h b/tools/gbagfx/util.h new file mode 100644 index 000000000..ec81471c0 --- /dev/null +++ b/tools/gbagfx/util.h @@ -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 diff --git a/tools/scaninc/.gitignore b/tools/scaninc/.gitignore new file mode 100644 index 000000000..94bfbf989 --- /dev/null +++ b/tools/scaninc/.gitignore @@ -0,0 +1 @@ +scaninc diff --git a/tools/scaninc/Makefile b/tools/scaninc/Makefile new file mode 100644 index 000000000..0efe283f1 --- /dev/null +++ b/tools/scaninc/Makefile @@ -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 diff --git a/tools/scaninc/scaninc.cpp b/tools/scaninc/scaninc.cpp new file mode 100644 index 000000000..fa5ab87b8 --- /dev/null +++ b/tools/scaninc/scaninc.cpp @@ -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()); + } +} |