mirror of https://github.com/pret/pokefirered
Sync tools directory
This commit is contained in:
parent
a14180a64e
commit
f0566e68f0
|
|
@ -1,4 +1,4 @@
|
|||
CC = gcc
|
||||
CC ?= gcc
|
||||
|
||||
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O3 -flto -DPNG_SKIP_SETJMP_CHECK
|
||||
CFLAGS += $(shell pkg-config --cflags libpng)
|
||||
|
|
@ -8,15 +8,21 @@ LDFLAGS += $(shell pkg-config --libs-only-L libpng)
|
|||
|
||||
SRCS = main.c convert_png.c gfx.c jasc_pal.c lz.c rl.c util.c font.c huff.c
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
else
|
||||
EXE :=
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: gbagfx
|
||||
all: gbagfx$(EXE)
|
||||
@:
|
||||
|
||||
gbagfx-debug: $(SRCS) convert_png.h gfx.h global.h jasc_pal.h lz.h rl.h util.h font.h
|
||||
gbagfx-debug$(EXE): $(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
|
||||
gbagfx$(EXE): $(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)
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int d
|
|||
|
||||
for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth)
|
||||
{
|
||||
unsigned char pixel = (srcByte >> j) % (1 << destBitDepth);
|
||||
unsigned char pixel = ((srcByte >> j) % (1 << srcBitDepth)) % (1 << destBitDepth);
|
||||
*dest |= pixel << destBit;
|
||||
destBit -= destBitDepth;
|
||||
if (destBit < 0)
|
||||
|
|
@ -130,7 +130,6 @@ void ReadPng(char *path, struct Image *image)
|
|||
FATAL_ERROR("Bit depth of image must be 1, 2, 4, or 8.\n");
|
||||
image->pixels = ConvertBitDepth(image->pixels, bit_depth, image->bitDepth, image->width * image->height);
|
||||
free(src);
|
||||
image->bitDepth = bit_depth;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,18 @@ static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numT
|
|||
}
|
||||
}
|
||||
|
||||
// For untiled, plain images
|
||||
static void CopyPlainPixels(unsigned char *src, unsigned char *dest, int size, int dataWidth, bool invertColors)
|
||||
{
|
||||
if (dataWidth == 0) return;
|
||||
for (int i = 0; i < size; i += dataWidth) {
|
||||
for (int j = dataWidth; j > 0; j--) {
|
||||
unsigned char pixels = src[i + j - 1];
|
||||
*dest++ = invertColors ? ~pixels : pixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeAffineTilemap(unsigned char *input, unsigned char *output, unsigned char *tilemap, int tileSize, int numTiles)
|
||||
{
|
||||
for (int i = 0; i < numTiles; i++)
|
||||
|
|
@ -345,9 +357,9 @@ static unsigned char *DecodeTilemap(unsigned char *tiles, struct Tilemap *tilema
|
|||
return decoded;
|
||||
}
|
||||
|
||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
||||
void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
||||
{
|
||||
int tileSize = bitDepth * 8;
|
||||
int tileSize = image->bitDepth * 8;
|
||||
|
||||
int fileSize;
|
||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||
|
|
@ -355,26 +367,25 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
|
|||
int numTiles = fileSize / tileSize;
|
||||
if (image->tilemap.data.affine != NULL)
|
||||
{
|
||||
int outTileSize = (bitDepth == 4 && image->palette.numColors > 16) ? 64 : tileSize;
|
||||
buffer = DecodeTilemap(buffer, &image->tilemap, &numTiles, image->isAffine, tileSize, outTileSize, bitDepth);
|
||||
int outTileSize = (image->bitDepth == 4 && image->palette.numColors > 16) ? 64 : tileSize;
|
||||
buffer = DecodeTilemap(buffer, &image->tilemap, &numTiles, image->isAffine, tileSize, outTileSize, image->bitDepth);
|
||||
if (outTileSize == 64)
|
||||
{
|
||||
tileSize = 64;
|
||||
image->bitDepth = bitDepth = 8;
|
||||
image->bitDepth = 8;
|
||||
}
|
||||
}
|
||||
|
||||
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)", tilesWidth, metatileWidth);
|
||||
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth);
|
||||
|
||||
if (tilesHeight % metatileHeight != 0)
|
||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
|
||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight);
|
||||
|
||||
image->width = tilesWidth * 8;
|
||||
image->height = tilesHeight * 8;
|
||||
image->bitDepth = bitDepth;
|
||||
image->pixels = calloc(tilesWidth * tilesHeight, tileSize);
|
||||
|
||||
if (image->pixels == NULL)
|
||||
|
|
@ -382,7 +393,7 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
|
|||
|
||||
int metatilesWide = tilesWidth / metatileWidth;
|
||||
|
||||
switch (bitDepth) {
|
||||
switch (image->bitDepth) {
|
||||
case 1:
|
||||
ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
||||
break;
|
||||
|
|
@ -397,9 +408,9 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
void WriteImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
||||
void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
||||
{
|
||||
int tileSize = bitDepth * 8;
|
||||
int tileSize = image->bitDepth * 8;
|
||||
|
||||
if (image->width % 8 != 0)
|
||||
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
|
||||
|
|
@ -411,10 +422,10 @@ void WriteImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int bi
|
|||
int tilesHeight = image->height / 8;
|
||||
|
||||
if (tilesWidth % metatileWidth != 0)
|
||||
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
|
||||
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)\n", tilesWidth, metatileWidth);
|
||||
|
||||
if (tilesHeight % metatileHeight != 0)
|
||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
|
||||
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)\n", tilesHeight, metatileHeight);
|
||||
|
||||
int maxNumTiles = tilesWidth * tilesHeight;
|
||||
|
||||
|
|
@ -432,7 +443,7 @@ void WriteImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int bi
|
|||
|
||||
int metatilesWide = tilesWidth / metatileWidth;
|
||||
|
||||
switch (bitDepth) {
|
||||
switch (image->bitDepth) {
|
||||
case 1:
|
||||
ConvertToTiles1Bpp(image->pixels, buffer, maxNumTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
|
||||
break;
|
||||
|
|
@ -468,9 +479,60 @@ void WriteImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int bi
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
void ReadPlainImage(char *path, int dataWidth, struct Image *image, bool invertColors)
|
||||
{
|
||||
int fileSize;
|
||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||
|
||||
if (fileSize % dataWidth != 0)
|
||||
FATAL_ERROR("The image data size (%d) isn't a multiple of the specified data width %d.\n", fileSize, dataWidth);
|
||||
|
||||
// png scanlines have wasted bits if they do not align to byte boundaries.
|
||||
// pngs misaligned in this way are not currently handled.
|
||||
int pixelsPerByte = 8 / image->bitDepth;
|
||||
if (image->width % pixelsPerByte != 0)
|
||||
FATAL_ERROR("The width in pixels (%d) isn't a multiple of %d.\n", image->width, pixelsPerByte);
|
||||
|
||||
int numPixels = fileSize * pixelsPerByte;
|
||||
image->height = (numPixels + image->width - 1) / image->width;
|
||||
image->pixels = calloc(image->width * image->height * image->bitDepth / 8, 1);
|
||||
|
||||
if (image->pixels == NULL)
|
||||
FATAL_ERROR("Failed to allocate memory for pixels.\n");
|
||||
|
||||
CopyPlainPixels(buffer, image->pixels, fileSize, dataWidth, invertColors);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void WritePlainImage(char *path, int dataWidth, struct Image *image, bool invertColors)
|
||||
{
|
||||
int bufferSize = image->width * image->height * image->bitDepth / 8;
|
||||
|
||||
if (bufferSize % dataWidth != 0)
|
||||
FATAL_ERROR("The image data size (%d) isn't a multiple of the specified data width %d.\n", bufferSize, dataWidth);
|
||||
|
||||
// png scanlines have wasted bits if they do not align to byte boundaries.
|
||||
// pngs misaligned in this way are not currently handled.
|
||||
int pixelsPerByte = 8 / image->bitDepth;
|
||||
if (image->width % pixelsPerByte != 0)
|
||||
FATAL_ERROR("The width in pixels (%d) isn't a multiple of %d.\n", image->width, pixelsPerByte);
|
||||
|
||||
unsigned char *buffer = malloc(bufferSize);
|
||||
|
||||
if (buffer == NULL)
|
||||
FATAL_ERROR("Failed to allocate memory for pixels.\n");
|
||||
|
||||
CopyPlainPixels(image->pixels, buffer, bufferSize, dataWidth, invertColors);
|
||||
|
||||
WriteWholeFile(path, buffer, bufferSize);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void FreeImage(struct Image *image)
|
||||
{
|
||||
if (image->tilemap.data.affine != NULL)
|
||||
if (image->tilemap.data.affine != NULL)
|
||||
{
|
||||
free(image->tilemap.data.affine);
|
||||
image->tilemap.data.affine = NULL;
|
||||
|
|
|
|||
|
|
@ -50,8 +50,10 @@ enum NumTilesMode {
|
|||
NUM_TILES_ERROR,
|
||||
};
|
||||
|
||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||
void WriteImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||
void ReadTileImage(char *path, int tilesWidth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||
void WriteTileImage(char *path, enum NumTilesMode numTilesMode, int numTiles, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||
void ReadPlainImage(char *path, int dataWidth, struct Image *image, bool invertColors);
|
||||
void WritePlainImage(char *path, int dataWidth, struct Image *image, bool invertColors);
|
||||
void FreeImage(struct Image *image);
|
||||
void ReadGbaPalette(char *path, struct Palette *palette);
|
||||
void WriteGbaPalette(char *path, struct Palette *palette);
|
||||
|
|
|
|||
|
|
@ -46,10 +46,14 @@ void ReadJascPaletteLine(FILE *fp, char *line)
|
|||
}
|
||||
|
||||
if (c == '\n')
|
||||
FATAL_ERROR("LF line endings aren't supported.\n");
|
||||
{
|
||||
line[length] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == EOF)
|
||||
FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n");
|
||||
FATAL_ERROR("Unexpected EOF. No LF or CRLF at end of file.\n");
|
||||
|
||||
if (c == 0)
|
||||
FATAL_ERROR("NUL character in file.\n");
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
|
|||
{
|
||||
struct Image image;
|
||||
|
||||
image.bitDepth = options->bitDepth;
|
||||
image.tilemap.data.affine = NULL;
|
||||
|
||||
if (options->paletteFilePath != NULL)
|
||||
{
|
||||
char *paletteFileExtension = GetFileExtensionAfterDot(options->paletteFilePath);
|
||||
|
|
@ -45,22 +48,25 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
|
|||
image.hasPalette = false;
|
||||
}
|
||||
|
||||
if (options->tilemapFilePath != NULL)
|
||||
if (options->isTiled)
|
||||
{
|
||||
int fileSize;
|
||||
image.tilemap.data.affine = ReadWholeFile(options->tilemapFilePath, &fileSize);
|
||||
if (options->isAffineMap && options->bitDepth != 8)
|
||||
FATAL_ERROR("affine maps are necessarily 8bpp\n");
|
||||
image.isAffine = options->isAffineMap;
|
||||
image.tilemap.size = fileSize;
|
||||
if (options->tilemapFilePath != NULL)
|
||||
{
|
||||
int fileSize;
|
||||
image.tilemap.data.affine = ReadWholeFile(options->tilemapFilePath, &fileSize);
|
||||
if (options->isAffineMap && options->bitDepth != 8)
|
||||
FATAL_ERROR("affine maps are necessarily 8bpp\n");
|
||||
image.isAffine = options->isAffineMap;
|
||||
image.tilemap.size = fileSize;
|
||||
}
|
||||
ReadTileImage(inputPath, options->width, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
|
||||
}
|
||||
else
|
||||
{
|
||||
image.tilemap.data.affine = NULL;
|
||||
image.width = options->width;
|
||||
ReadPlainImage(inputPath, options->dataWidth, &image, !image.hasPalette);
|
||||
}
|
||||
|
||||
ReadImage(inputPath, options->width, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
|
||||
|
||||
image.hasTransparency = options->hasTransparency;
|
||||
|
||||
WritePng(outputPath, &image);
|
||||
|
|
@ -77,7 +83,10 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
|
|||
|
||||
ReadPng(inputPath, &image);
|
||||
|
||||
WriteImage(outputPath, options->numTilesMode, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
|
||||
if (options->isTiled)
|
||||
WriteTileImage(outputPath, options->numTilesMode, options->numTiles, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
|
||||
else
|
||||
WritePlainImage(outputPath, options->dataWidth, &image, !image.hasPalette);
|
||||
|
||||
FreeImage(&image);
|
||||
}
|
||||
|
|
@ -94,6 +103,8 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
|||
options.metatileHeight = 1;
|
||||
options.tilemapFilePath = NULL;
|
||||
options.isAffineMap = false;
|
||||
options.isTiled = true;
|
||||
options.dataWidth = 1;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
|
|
@ -162,6 +173,22 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
|||
{
|
||||
options.isAffineMap = true;
|
||||
}
|
||||
else if (strcmp(option, "-plain") == 0)
|
||||
{
|
||||
options.isTiled = false;
|
||||
}
|
||||
else if (strcmp(option, "-data_width") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No data width value following \"-data_width\".\n");
|
||||
i++;
|
||||
|
||||
if (!ParseNumber(argv[i], NULL, 10, &options.dataWidth))
|
||||
FATAL_ERROR("Failed to parse data width.\n");
|
||||
|
||||
if (options.dataWidth < 1)
|
||||
FATAL_ERROR("Data width must be positive.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
|
||||
|
|
@ -177,15 +204,16 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
|||
void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv)
|
||||
{
|
||||
char *outputFileExtension = GetFileExtensionAfterDot(outputPath);
|
||||
int bitDepth = outputFileExtension[0] - '0';
|
||||
struct PngToGbaOptions options;
|
||||
options.numTilesMode = NUM_TILES_IGNORE;
|
||||
options.numTiles = 0;
|
||||
options.bitDepth = bitDepth;
|
||||
options.bitDepth = outputFileExtension[0] - '0';
|
||||
options.metatileWidth = 1;
|
||||
options.metatileHeight = 1;
|
||||
options.tilemapFilePath = NULL;
|
||||
options.isAffineMap = false;
|
||||
options.isTiled = true;
|
||||
options.dataWidth = 1;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
|
|
@ -236,6 +264,22 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
|
|||
if (options.metatileHeight < 1)
|
||||
FATAL_ERROR("metatile height must be positive.\n");
|
||||
}
|
||||
else if (strcmp(option, "-plain") == 0)
|
||||
{
|
||||
options.isTiled = false;
|
||||
}
|
||||
else if (strcmp(option, "-data_width") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No data width value following \"-data_width\".\n");
|
||||
i++;
|
||||
|
||||
if (!ParseNumber(argv[i], NULL, 10, &options.dataWidth))
|
||||
FATAL_ERROR("Failed to parse data width.\n");
|
||||
|
||||
if (options.dataWidth < 1)
|
||||
FATAL_ERROR("Data width must be positive.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
|
||||
|
|
@ -403,7 +447,7 @@ void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char *
|
|||
else if (strcmp(option, "-search") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No size following \"-overflow\".\n");
|
||||
FATAL_ERROR("No size following \"-search\".\n");
|
||||
|
||||
i++;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ struct GbaToPngOptions {
|
|||
int metatileHeight;
|
||||
char *tilemapFilePath;
|
||||
bool isAffineMap;
|
||||
bool isTiled;
|
||||
int dataWidth;
|
||||
};
|
||||
|
||||
struct PngToGbaOptions {
|
||||
|
|
@ -25,6 +27,8 @@ struct PngToGbaOptions {
|
|||
int metatileHeight;
|
||||
char *tilemapFilePath;
|
||||
bool isAffineMap;
|
||||
bool isTiled;
|
||||
int dataWidth;
|
||||
};
|
||||
|
||||
#endif // OPTIONS_H
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -Wall -std=c++11 -O2
|
||||
CXXFLAGS := -Wall -std=c++17 -O2
|
||||
|
||||
INCLUDES := -I .
|
||||
|
||||
|
|
@ -8,12 +8,18 @@ SRCS := jsonproc.cpp
|
|||
|
||||
HEADERS := jsonproc.h inja.hpp nlohmann/json.hpp
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
else
|
||||
EXE :=
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: jsonproc
|
||||
all: jsonproc$(EXE)
|
||||
@:
|
||||
|
||||
jsonproc: $(SRCS) $(HEADERS)
|
||||
jsonproc$(EXE): $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(INCLUDES) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,6 @@
|
|||
// jsonproc.cpp
|
||||
// jsonproc converts JSON data to an output file based on an Inja template.
|
||||
// https://github.com/pantor/inja
|
||||
|
||||
#include "jsonproc.h"
|
||||
|
||||
|
|
@ -7,6 +9,9 @@
|
|||
#include <string>
|
||||
using std::string; using std::to_string;
|
||||
|
||||
#include <algorithm>
|
||||
using std::replace_if;
|
||||
|
||||
#include <inja.hpp>
|
||||
using namespace inja;
|
||||
using json = nlohmann::json;
|
||||
|
|
@ -40,13 +45,6 @@ int main(int argc, char *argv[])
|
|||
return "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n";
|
||||
});
|
||||
|
||||
env.add_callback("contains", 2, [](Arguments& args) {
|
||||
string word = args.at(0)->get<string>();
|
||||
string check = args.at(1)->get<string>();
|
||||
|
||||
return word.find(check) != std::string::npos;
|
||||
});
|
||||
|
||||
env.add_callback("subtract", 2, [](Arguments& args) {
|
||||
int minuend = args.at(0)->get<int>();
|
||||
int subtrahend = args.at(1)->get<int>();
|
||||
|
|
@ -109,11 +107,16 @@ int main(int argc, char *argv[])
|
|||
});
|
||||
|
||||
env.add_callback("cleanString", 1, [](Arguments& args) {
|
||||
string badChars = ".'{} \n\t-_\u00e9";
|
||||
string str = args.at(0)->get<string>();
|
||||
str.erase(remove_if(str.begin(), str.end(), [&badChars](const char &c) {
|
||||
return badChars.find(c) != std::string::npos;
|
||||
}), str.end());
|
||||
for (unsigned int i = 0; i < str.length(); i++) {
|
||||
// This code is not Unicode aware, so UTF-8 is not easily parsable without introducing
|
||||
// another library. Just filter out any non-alphanumeric characters for now.
|
||||
// TODO: proper Unicode string normalization
|
||||
if ((i == 0 && isdigit(str[i]))
|
||||
|| !isalnum(str[i])) {
|
||||
str[i] = '_';
|
||||
}
|
||||
}
|
||||
return str;
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,4 @@
|
|||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -Wall -std=c++11 -O2
|
||||
|
||||
|
|
@ -6,12 +6,18 @@ SRCS := json11.cpp mapjson.cpp
|
|||
|
||||
HEADERS := mapjson.h
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
else
|
||||
EXE :=
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: mapjson
|
||||
all: mapjson$(EXE)
|
||||
@:
|
||||
|
||||
mapjson: $(SRCS) $(HEADERS)
|
||||
mapjson$(EXE): $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ using json11::Json;
|
|||
#include "mapjson.h"
|
||||
|
||||
string version;
|
||||
// System directory separator
|
||||
string sep;
|
||||
|
||||
string read_text_file(string filepath) {
|
||||
ifstream in_file(filepath);
|
||||
|
|
@ -330,13 +332,22 @@ string generate_map_events_text(Json map_data) {
|
|||
return text.str();
|
||||
}
|
||||
|
||||
string get_directory_name(string filename) {
|
||||
size_t dir_pos = filename.find_last_of("/\\");
|
||||
string strip_trailing_separator(string filename) {
|
||||
if(filename.back() == '/' || filename.back() == '\\')
|
||||
filename.pop_back();
|
||||
|
||||
return filename;
|
||||
}
|
||||
void infer_separator(string filename) {
|
||||
size_t dir_pos = filename.find_last_of("/\\");
|
||||
sep = filename[dir_pos];
|
||||
}
|
||||
string file_parent(string filename){
|
||||
size_t dir_pos = filename.find_last_of("/\\");
|
||||
return filename.substr(0, dir_pos + 1);
|
||||
}
|
||||
|
||||
void process_map(string map_filepath, string layouts_filepath) {
|
||||
void process_map(string map_filepath, string layouts_filepath, string output_dir) {
|
||||
string mapdata_err, layouts_err;
|
||||
|
||||
string mapdata_json_text = read_text_file(map_filepath);
|
||||
|
|
@ -354,10 +365,10 @@ void process_map(string map_filepath, string layouts_filepath) {
|
|||
string events_text = generate_map_events_text(map_data);
|
||||
string connections_text = generate_map_connections_text(map_data);
|
||||
|
||||
string files_dir = get_directory_name(map_filepath);
|
||||
write_text_file(files_dir + "header.inc", header_text);
|
||||
write_text_file(files_dir + "events.inc", events_text);
|
||||
write_text_file(files_dir + "connections.inc", connections_text);
|
||||
string out_dir = strip_trailing_separator(output_dir).append(sep);
|
||||
write_text_file(out_dir + "header.inc", header_text);
|
||||
write_text_file(out_dir + "events.inc", events_text);
|
||||
write_text_file(out_dir + "connections.inc", connections_text);
|
||||
}
|
||||
|
||||
string generate_groups_text(Json groups_data) {
|
||||
|
|
@ -382,7 +393,7 @@ string generate_groups_text(Json groups_data) {
|
|||
return text.str();
|
||||
}
|
||||
|
||||
string generate_connections_text(Json groups_data) {
|
||||
string generate_connections_text(Json groups_data, string include_path) {
|
||||
vector<Json> map_names;
|
||||
|
||||
for (auto &group : groups_data["group_order"].array_items())
|
||||
|
|
@ -407,12 +418,12 @@ string generate_connections_text(Json groups_data) {
|
|||
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n";
|
||||
|
||||
for (Json map_name : map_names)
|
||||
text << "\t.include \"data/maps/" << json_to_string(map_name) << "/connections.inc\"\n";
|
||||
text << "\t.include \"" << include_path << "/" << json_to_string(map_name) << "/connections.inc\"\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
string generate_headers_text(Json groups_data) {
|
||||
string generate_headers_text(Json groups_data, string include_path) {
|
||||
vector<string> map_names;
|
||||
|
||||
for (auto &group : groups_data["group_order"].array_items())
|
||||
|
|
@ -424,12 +435,12 @@ string generate_headers_text(Json groups_data) {
|
|||
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n";
|
||||
|
||||
for (string map_name : map_names)
|
||||
text << "\t.include \"data/maps/" << map_name << "/header.inc\"\n";
|
||||
text << "\t.include \"" << include_path << "/" << map_name << "/header.inc\"\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
string generate_events_text(Json groups_data) {
|
||||
string generate_events_text(Json groups_data, string include_path) {
|
||||
vector<string> map_names;
|
||||
|
||||
for (auto &group : groups_data["group_order"].array_items())
|
||||
|
|
@ -438,17 +449,16 @@ string generate_events_text(Json groups_data) {
|
|||
|
||||
ostringstream text;
|
||||
|
||||
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n";
|
||||
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from " << include_path << "/map_groups.json\n@\n\n";
|
||||
|
||||
for (string map_name : map_names)
|
||||
text << "\t.include \"data/maps/" << map_name << "/events.inc\"\n";
|
||||
text << "\t.include \"" << include_path << "/" << map_name << "/events.inc\"\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
string generate_map_constants_text(string groups_filepath, Json groups_data) {
|
||||
string file_dir = get_directory_name(groups_filepath);
|
||||
char dir_separator = file_dir.back();
|
||||
string file_dir = file_parent(groups_filepath) + sep;
|
||||
|
||||
ostringstream text;
|
||||
|
||||
|
|
@ -466,7 +476,7 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
|
|||
size_t max_length = 0;
|
||||
|
||||
for (auto &map_name : groups_data[groupName].array_items()) {
|
||||
string map_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json";
|
||||
string map_filepath = file_dir + json_to_string(map_name) + sep + "map.json";
|
||||
string err_str;
|
||||
Json map_data = Json::parse(read_text_file(map_filepath), err_str);
|
||||
if (map_data == Json())
|
||||
|
|
@ -493,7 +503,11 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) {
|
|||
return text.str();
|
||||
}
|
||||
|
||||
void process_groups(string groups_filepath) {
|
||||
// Output paths are directories with trailing path separators
|
||||
void process_groups(string groups_filepath, string output_asm, string output_c) {
|
||||
output_asm = strip_trailing_separator(output_asm); // Remove separator if existing.
|
||||
output_c = strip_trailing_separator(output_c);
|
||||
|
||||
string err;
|
||||
Json groups_data = Json::parse(read_text_file(groups_filepath), err);
|
||||
|
||||
|
|
@ -501,19 +515,16 @@ void process_groups(string groups_filepath) {
|
|||
FATAL_ERROR("%s\n", err.c_str());
|
||||
|
||||
string groups_text = generate_groups_text(groups_data);
|
||||
string connections_text = generate_connections_text(groups_data);
|
||||
string headers_text = generate_headers_text(groups_data);
|
||||
string events_text = generate_events_text(groups_data);
|
||||
string connections_text = generate_connections_text(groups_data, output_asm);
|
||||
string headers_text = generate_headers_text(groups_data, output_asm);
|
||||
string events_text = generate_events_text(groups_data, output_asm);
|
||||
string map_header_text = generate_map_constants_text(groups_filepath, groups_data);
|
||||
|
||||
string file_dir = get_directory_name(groups_filepath);
|
||||
char s = file_dir.back();
|
||||
|
||||
write_text_file(file_dir + "groups.inc", groups_text);
|
||||
write_text_file(file_dir + "connections.inc", connections_text);
|
||||
write_text_file(file_dir + "headers.inc", headers_text);
|
||||
write_text_file(file_dir + "events.inc", events_text);
|
||||
write_text_file(file_dir + ".." + s + ".." + s + "include" + s + "constants" + s + "map_groups.h", map_header_text);
|
||||
write_text_file(output_asm + sep + "groups.inc", groups_text);
|
||||
write_text_file(output_asm + sep + "connections.inc", connections_text);
|
||||
write_text_file(output_asm + sep + "headers.inc", headers_text);
|
||||
write_text_file(output_asm + sep + "events.inc", events_text);
|
||||
write_text_file(output_c + sep + "map_groups.h", map_header_text);
|
||||
}
|
||||
|
||||
string generate_layout_headers_text(Json layouts_data) {
|
||||
|
|
@ -586,7 +597,10 @@ string generate_layouts_constants_text(Json layouts_data) {
|
|||
return text.str();
|
||||
}
|
||||
|
||||
void process_layouts(string layouts_filepath) {
|
||||
void process_layouts(string layouts_filepath, string output_asm, string output_c) {
|
||||
output_asm = strip_trailing_separator(output_asm).append(sep);
|
||||
output_c = strip_trailing_separator(output_c).append(sep);
|
||||
|
||||
string err;
|
||||
Json layouts_data = Json::parse(read_text_file(layouts_filepath), err);
|
||||
|
||||
|
|
@ -597,12 +611,9 @@ void process_layouts(string layouts_filepath) {
|
|||
string layouts_table_text = generate_layouts_table_text(layouts_data);
|
||||
string layouts_constants_text = generate_layouts_constants_text(layouts_data);
|
||||
|
||||
string file_dir = get_directory_name(layouts_filepath);
|
||||
char s = file_dir.back();
|
||||
|
||||
write_text_file(file_dir + "layouts.inc", layout_headers_text);
|
||||
write_text_file(file_dir + "layouts_table.inc", layouts_table_text);
|
||||
write_text_file(file_dir + ".." + s + ".." + s + "include" + s + "constants" + s + "layouts.h", layouts_constants_text);
|
||||
write_text_file(output_asm + "layouts.inc", layout_headers_text);
|
||||
write_text_file(output_asm + "layouts_table.inc", layouts_table_text);
|
||||
write_text_file(output_c + "layouts.h", layouts_constants_text);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
|
@ -620,29 +631,40 @@ int main(int argc, char *argv[]) {
|
|||
FATAL_ERROR("ERROR: <mode> must be 'layouts', 'map', or 'groups'.\n");
|
||||
|
||||
if (mode == "map") {
|
||||
if (argc != 5)
|
||||
FATAL_ERROR("USAGE: mapjson map <game-version> <map_file> <layouts_file>\n");
|
||||
if (argc != 6)
|
||||
FATAL_ERROR("USAGE: mapjson map <game-version> <map_file> <layouts_file> <output_dir>\n");
|
||||
|
||||
infer_separator(argv[3]);
|
||||
string filepath(argv[3]);
|
||||
string layouts_filepath(argv[4]);
|
||||
string output_dir(argv[5]);
|
||||
|
||||
process_map(filepath, layouts_filepath);
|
||||
process_map(filepath, layouts_filepath, output_dir);
|
||||
}
|
||||
else if (mode == "groups") {
|
||||
if (argc != 4)
|
||||
FATAL_ERROR("USAGE: mapjson groups <game-version> <groups_file>\n");
|
||||
if (argc != 6)
|
||||
FATAL_ERROR("USAGE: mapjson groups <game-version> <groups_file> <output_asm_dir> <output_c_dir>\n");
|
||||
|
||||
infer_separator(argv[3]);
|
||||
string filepath(argv[3]);
|
||||
string output_asm(argv[4]);
|
||||
string output_c(argv[5]);
|
||||
|
||||
process_groups(filepath);
|
||||
process_groups(filepath, output_asm, output_c);
|
||||
}
|
||||
else if (mode == "layouts") {
|
||||
if (argc != 4)
|
||||
FATAL_ERROR("USAGE: mapjson layouts <game-version> <layouts_file>\n");
|
||||
if (argc != 6)
|
||||
FATAL_ERROR("USAGE: mapjson layouts <game-version> <layouts_file> <output_asm_dir> <output_c_dir>\n");
|
||||
|
||||
infer_separator(argv[3]);
|
||||
string filepath(argv[3]);
|
||||
string output_asm(argv[4]);
|
||||
string output_c(argv[5]);
|
||||
|
||||
process_layouts(filepath);
|
||||
process_layouts(filepath, output_asm, output_c);
|
||||
}
|
||||
else {
|
||||
FATAL_ERROR("ERROR: <mode> must be 'layouts', 'map', or 'groups'.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
||||
|
||||
SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \
|
||||
utf8.cpp
|
||||
utf8.cpp io.cpp
|
||||
|
||||
HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
|
||||
utf8.h
|
||||
utf8.h io.h
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
else
|
||||
EXE :=
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: preproc
|
||||
all: preproc$(EXE)
|
||||
@:
|
||||
|
||||
preproc: $(SRCS) $(HEADERS)
|
||||
preproc$(EXE): $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -26,33 +26,13 @@
|
|||
#include "char_util.h"
|
||||
#include "utf8.h"
|
||||
#include "string_parser.h"
|
||||
#include "../../include/characters.h"
|
||||
#include "io.h"
|
||||
|
||||
AsmFile::AsmFile(std::string filename) : m_filename(filename)
|
||||
AsmFile::AsmFile(std::string filename, bool isStdin, bool doEnum) : m_filename(filename)
|
||||
{
|
||||
FILE *fp = std::fopen(filename.c_str(), "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
|
||||
m_size = std::ftell(fp);
|
||||
|
||||
if (m_size < 0)
|
||||
FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
|
||||
else if (m_size == 0)
|
||||
return; // Empty file
|
||||
|
||||
m_buffer = new char[m_size + 1];
|
||||
|
||||
std::rewind(fp);
|
||||
|
||||
if (std::fread(m_buffer, m_size, 1, fp) != 1)
|
||||
FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
|
||||
|
||||
m_buffer[m_size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
m_buffer = ReadFileToBuffer(filename.c_str(), isStdin, &m_size);
|
||||
m_doEnum = doEnum;
|
||||
|
||||
m_pos = 0;
|
||||
m_lineNum = 1;
|
||||
|
|
@ -64,6 +44,7 @@ AsmFile::AsmFile(std::string filename) : m_filename(filename)
|
|||
AsmFile::AsmFile(AsmFile&& other) : m_filename(std::move(other.m_filename))
|
||||
{
|
||||
m_buffer = other.m_buffer;
|
||||
m_doEnum = other.m_doEnum;
|
||||
m_pos = other.m_pos;
|
||||
m_size = other.m_size;
|
||||
m_lineNum = other.m_lineNum;
|
||||
|
|
@ -173,6 +154,8 @@ Directive AsmFile::GetDirective()
|
|||
return Directive::String;
|
||||
else if (CheckForDirective(".braille"))
|
||||
return Directive::Braille;
|
||||
else if (CheckForDirective("enum"))
|
||||
return Directive::Enum;
|
||||
else
|
||||
return Directive::Unknown;
|
||||
}
|
||||
|
|
@ -283,7 +266,7 @@ int AsmFile::ReadString(unsigned char* s)
|
|||
|
||||
while (length < padLength)
|
||||
{
|
||||
s[length++] = 0;
|
||||
s[length++] = CHAR_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -292,40 +275,92 @@ int AsmFile::ReadString(unsigned char* s)
|
|||
return length;
|
||||
}
|
||||
|
||||
void AsmFile::VerifyStringLength(int length)
|
||||
{
|
||||
if (length == kMaxStringLength)
|
||||
RaiseError("mapped string longer than %d bytes", kMaxStringLength);
|
||||
}
|
||||
|
||||
int AsmFile::ReadBraille(unsigned char* s)
|
||||
{
|
||||
static std::map<char, unsigned char> encoding =
|
||||
{
|
||||
{ 'A', 0x01 },
|
||||
{ 'B', 0x05 },
|
||||
{ 'C', 0x03 },
|
||||
{ 'D', 0x0B },
|
||||
{ 'E', 0x09 },
|
||||
{ 'F', 0x07 },
|
||||
{ 'G', 0x0F },
|
||||
{ 'H', 0x0D },
|
||||
{ 'I', 0x06 },
|
||||
{ 'J', 0x0E },
|
||||
{ 'K', 0x11 },
|
||||
{ 'L', 0x15 },
|
||||
{ 'M', 0x13 },
|
||||
{ 'N', 0x1B },
|
||||
{ 'O', 0x19 },
|
||||
{ 'P', 0x17 },
|
||||
{ 'Q', 0x1F },
|
||||
{ 'R', 0x1D },
|
||||
{ 'S', 0x16 },
|
||||
{ 'T', 0x1E },
|
||||
{ 'U', 0x31 },
|
||||
{ 'V', 0x35 },
|
||||
{ 'W', 0x2E },
|
||||
{ 'X', 0x33 },
|
||||
{ 'Y', 0x3B },
|
||||
{ 'Z', 0x39 },
|
||||
{ ' ', 0x00 },
|
||||
{ ',', 0x04 },
|
||||
{ '.', 0x2C },
|
||||
{ '$', 0xFF },
|
||||
{ 'A', BRAILLE_CHAR_A },
|
||||
{ 'B', BRAILLE_CHAR_B },
|
||||
{ 'C', BRAILLE_CHAR_C },
|
||||
{ 'D', BRAILLE_CHAR_D },
|
||||
{ 'E', BRAILLE_CHAR_E },
|
||||
{ 'F', BRAILLE_CHAR_F },
|
||||
{ 'G', BRAILLE_CHAR_G },
|
||||
{ 'H', BRAILLE_CHAR_H },
|
||||
{ 'I', BRAILLE_CHAR_I },
|
||||
{ 'J', BRAILLE_CHAR_J },
|
||||
{ 'K', BRAILLE_CHAR_K },
|
||||
{ 'L', BRAILLE_CHAR_L },
|
||||
{ 'M', BRAILLE_CHAR_M },
|
||||
{ 'N', BRAILLE_CHAR_N },
|
||||
{ 'O', BRAILLE_CHAR_O },
|
||||
{ 'P', BRAILLE_CHAR_P },
|
||||
{ 'Q', BRAILLE_CHAR_Q },
|
||||
{ 'R', BRAILLE_CHAR_R },
|
||||
{ 'S', BRAILLE_CHAR_S },
|
||||
{ 'T', BRAILLE_CHAR_T },
|
||||
{ 'U', BRAILLE_CHAR_U },
|
||||
{ 'V', BRAILLE_CHAR_V },
|
||||
{ 'W', BRAILLE_CHAR_W },
|
||||
{ 'X', BRAILLE_CHAR_X },
|
||||
{ 'Y', BRAILLE_CHAR_Y },
|
||||
{ 'Z', BRAILLE_CHAR_Z },
|
||||
{ 'a', BRAILLE_CHAR_A },
|
||||
{ 'b', BRAILLE_CHAR_B },
|
||||
{ 'c', BRAILLE_CHAR_C },
|
||||
{ 'd', BRAILLE_CHAR_D },
|
||||
{ 'e', BRAILLE_CHAR_E },
|
||||
{ 'f', BRAILLE_CHAR_F },
|
||||
{ 'g', BRAILLE_CHAR_G },
|
||||
{ 'h', BRAILLE_CHAR_H },
|
||||
{ 'i', BRAILLE_CHAR_I },
|
||||
{ 'j', BRAILLE_CHAR_J },
|
||||
{ 'k', BRAILLE_CHAR_K },
|
||||
{ 'l', BRAILLE_CHAR_L },
|
||||
{ 'm', BRAILLE_CHAR_M },
|
||||
{ 'n', BRAILLE_CHAR_N },
|
||||
{ 'o', BRAILLE_CHAR_O },
|
||||
{ 'p', BRAILLE_CHAR_P },
|
||||
{ 'q', BRAILLE_CHAR_Q },
|
||||
{ 'r', BRAILLE_CHAR_R },
|
||||
{ 's', BRAILLE_CHAR_S },
|
||||
{ 't', BRAILLE_CHAR_T },
|
||||
{ 'u', BRAILLE_CHAR_U },
|
||||
{ 'v', BRAILLE_CHAR_V },
|
||||
{ 'w', BRAILLE_CHAR_W },
|
||||
{ 'x', BRAILLE_CHAR_X },
|
||||
{ 'y', BRAILLE_CHAR_Y },
|
||||
{ 'z', BRAILLE_CHAR_Z },
|
||||
{ '0', BRAILLE_CHAR_0 },
|
||||
{ '1', BRAILLE_CHAR_1 },
|
||||
{ '2', BRAILLE_CHAR_2 },
|
||||
{ '3', BRAILLE_CHAR_3 },
|
||||
{ '4', BRAILLE_CHAR_4 },
|
||||
{ '5', BRAILLE_CHAR_5 },
|
||||
{ '6', BRAILLE_CHAR_6 },
|
||||
{ '7', BRAILLE_CHAR_7 },
|
||||
{ '8', BRAILLE_CHAR_8 },
|
||||
{ '9', BRAILLE_CHAR_9 },
|
||||
{ ' ', BRAILLE_CHAR_SPACE },
|
||||
{ ',', BRAILLE_CHAR_COMMA },
|
||||
{ '.', BRAILLE_CHAR_PERIOD },
|
||||
{ '?', BRAILLE_CHAR_QUESTION_MARK },
|
||||
{ '!', BRAILLE_CHAR_EXCL_MARK },
|
||||
{ ':', BRAILLE_CHAR_COLON },
|
||||
{ ';', BRAILLE_CHAR_SEMICOLON },
|
||||
{ '-', BRAILLE_CHAR_HYPHEN },
|
||||
{ '/', BRAILLE_CHAR_SLASH },
|
||||
{ '(', BRAILLE_CHAR_PAREN },
|
||||
{ ')', BRAILLE_CHAR_PAREN },
|
||||
{ '\'', BRAILLE_CHAR_APOSTROPHE },
|
||||
{ '#', BRAILLE_CHAR_NUMBER },
|
||||
{ '$', EOS },
|
||||
};
|
||||
|
||||
SkipWhitespace();
|
||||
|
|
@ -337,14 +372,13 @@ int AsmFile::ReadBraille(unsigned char* s)
|
|||
|
||||
m_pos++;
|
||||
|
||||
bool inNumber = false;
|
||||
while (m_buffer[m_pos] != '"')
|
||||
{
|
||||
if (length == kMaxStringLength)
|
||||
RaiseError("mapped string longer than %d bytes", kMaxStringLength);
|
||||
|
||||
if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == 'n')
|
||||
{
|
||||
s[length++] = 0xFE;
|
||||
VerifyStringLength(length);
|
||||
s[length++] = CHAR_NEWLINE;
|
||||
m_pos += 2;
|
||||
}
|
||||
else
|
||||
|
|
@ -359,6 +393,21 @@ int AsmFile::ReadBraille(unsigned char* s)
|
|||
RaiseError("character '\\x%02X' not valid in braille string", m_buffer[m_pos]);
|
||||
}
|
||||
|
||||
if (!inNumber && c >= '0' && c <= '9' )
|
||||
{
|
||||
// Output number indicator at start of a number
|
||||
inNumber = true;
|
||||
VerifyStringLength(length);
|
||||
s[length++] = BRAILLE_CHAR_NUMBER;
|
||||
}
|
||||
else if (inNumber && encoding[c] == BRAILLE_CHAR_SPACE)
|
||||
{
|
||||
// Number ends at a space.
|
||||
// Non-number characters encountered before a space will simply be output as is.
|
||||
inNumber = false;
|
||||
}
|
||||
|
||||
VerifyStringLength(length);
|
||||
s[length++] = encoding[c];
|
||||
m_pos++;
|
||||
}
|
||||
|
|
@ -460,6 +509,88 @@ void AsmFile::OutputLine()
|
|||
}
|
||||
}
|
||||
|
||||
// parses an assumed C `enum`. Returns false if `enum { ...` is not matched
|
||||
bool AsmFile::ParseEnum()
|
||||
{
|
||||
if (!m_doEnum)
|
||||
return false;
|
||||
|
||||
long fallbackPosition = m_pos;
|
||||
std::string headerFilename = "";
|
||||
long currentHeaderLine = SkipWhitespaceAndEol();
|
||||
std::string enumName = ReadIdentifier();
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
std::string enumBase = "0";
|
||||
long enumCounter = 0;
|
||||
long symbolCount = 0;
|
||||
|
||||
if (m_buffer[m_pos] != '{') // assume assembly macro, otherwise assume enum and report errors accordingly
|
||||
{
|
||||
m_pos = fallbackPosition - 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
currentHeaderLine += FindLastLineNumber(headerFilename);
|
||||
m_pos++;
|
||||
for (;;)
|
||||
{
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
std::string currentIdentName = ReadIdentifier();
|
||||
if (!currentIdentName.empty())
|
||||
{
|
||||
std::printf("# %ld \"%s\"\n", currentHeaderLine, headerFilename.c_str());
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
if (m_buffer[m_pos] == '=')
|
||||
{
|
||||
m_pos++;
|
||||
SkipWhitespace();
|
||||
enumBase.clear();
|
||||
for (;;)
|
||||
{
|
||||
if (m_pos == m_size)
|
||||
RaiseError("unexpected EOF");
|
||||
if (m_buffer[m_pos] == ',')
|
||||
break;
|
||||
if (m_buffer[m_pos] == '\n')
|
||||
{
|
||||
currentHeaderLine++;
|
||||
enumBase.push_back(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
enumBase.push_back(m_buffer[m_pos]);
|
||||
}
|
||||
m_pos++;
|
||||
}
|
||||
enumCounter = 0;
|
||||
}
|
||||
std::printf(".equiv %s, (%s) + %ld\n", currentIdentName.c_str(), enumBase.c_str(), enumCounter);
|
||||
enumCounter++;
|
||||
symbolCount++;
|
||||
}
|
||||
else if (symbolCount == 0)
|
||||
{
|
||||
RaiseError("%s:%ld: empty enum is invalid", headerFilename.c_str(), currentHeaderLine);
|
||||
}
|
||||
|
||||
if (m_buffer[m_pos] != ',')
|
||||
{
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
if (m_buffer[m_pos++] == '}' && m_buffer[m_pos++] == ';')
|
||||
{
|
||||
ExpectEmptyRestOfLine();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseError("unterminated enum from included file %s:%ld", headerFilename.c_str(), currentHeaderLine);
|
||||
}
|
||||
}
|
||||
m_pos++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Asserts that the rest of the line is empty and moves to the next one.
|
||||
void AsmFile::ExpectEmptyRestOfLine()
|
||||
{
|
||||
|
|
@ -532,3 +663,130 @@ void AsmFile::RaiseWarning(const char* format, ...)
|
|||
{
|
||||
DO_REPORT("warning");
|
||||
}
|
||||
|
||||
// Skips Whitespace including newlines and returns the amount of newlines skipped
|
||||
int AsmFile::SkipWhitespaceAndEol()
|
||||
{
|
||||
int newlines = 0;
|
||||
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ' || m_buffer[m_pos] == '\n')
|
||||
{
|
||||
if (m_buffer[m_pos] == '\n')
|
||||
newlines++;
|
||||
m_pos++;
|
||||
}
|
||||
return newlines;
|
||||
}
|
||||
|
||||
// returns the last line indicator and its corresponding file name without modifying the token index
|
||||
int AsmFile::FindLastLineNumber(std::string& filename)
|
||||
{
|
||||
long pos = m_pos;
|
||||
long linebreaks = 0;
|
||||
while (m_buffer[pos] != '#' && pos >= 0)
|
||||
{
|
||||
if (m_buffer[pos] == '\n')
|
||||
linebreaks++;
|
||||
pos--;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
RaiseError("line indicator for header file not found before `enum`");
|
||||
|
||||
pos++;
|
||||
while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t')
|
||||
pos++;
|
||||
|
||||
if (!IsAsciiDigit(m_buffer[pos]))
|
||||
RaiseError("malformatted line indicator found before `enum`, expected line number");
|
||||
|
||||
unsigned n = 0;
|
||||
int digit = 0;
|
||||
while ((digit = ConvertDigit(m_buffer[pos++], 10)) != -1)
|
||||
n = 10 * n + digit;
|
||||
|
||||
while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t')
|
||||
pos++;
|
||||
|
||||
if (m_buffer[pos++] != '"')
|
||||
RaiseError("malformatted line indicator found before `enum`, expected filename");
|
||||
|
||||
while (m_buffer[pos] != '"')
|
||||
{
|
||||
unsigned char c = m_buffer[pos++];
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
if (pos >= m_size)
|
||||
RaiseError("unexpected EOF in line indicator");
|
||||
else
|
||||
RaiseError("unexpected null character in line indicator");
|
||||
}
|
||||
|
||||
if (!IsAsciiPrintable(c))
|
||||
RaiseError("unexpected character '\\x%02X' in line indicator", c);
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
c = m_buffer[pos];
|
||||
RaiseError("unexpected escape '\\%c' in line indicator", c);
|
||||
}
|
||||
|
||||
filename += c;
|
||||
}
|
||||
|
||||
return n + linebreaks - 1;
|
||||
}
|
||||
|
||||
std::string AsmFile::ReadIdentifier()
|
||||
{
|
||||
long start = m_pos;
|
||||
if (!IsIdentifierStartingChar(m_buffer[m_pos]))
|
||||
return std::string();
|
||||
|
||||
m_pos++;
|
||||
|
||||
while (IsIdentifierChar(m_buffer[m_pos]))
|
||||
m_pos++;
|
||||
|
||||
return std::string(&m_buffer[start], m_pos - start);
|
||||
}
|
||||
|
||||
long AsmFile::ReadInteger(std::string filename, long line)
|
||||
{
|
||||
bool negate = false;
|
||||
int radix = 10;
|
||||
if (!IsAsciiDigit(m_buffer[m_pos]))
|
||||
{
|
||||
if (m_buffer[m_pos++] == '-')
|
||||
negate = true;
|
||||
else
|
||||
RaiseError("expected number in included file %s:%ld", filename.c_str(), line);
|
||||
}
|
||||
|
||||
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
|
||||
{
|
||||
radix = 16;
|
||||
m_pos += 2;
|
||||
}
|
||||
else if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'b')
|
||||
{
|
||||
radix = 2;
|
||||
m_pos += 2;
|
||||
}
|
||||
else if (m_buffer[m_pos] == '0' && IsAsciiDigit(m_buffer[m_pos+1]))
|
||||
{
|
||||
radix = 8;
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
long n = 0;
|
||||
int digit;
|
||||
|
||||
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
|
||||
{
|
||||
n = n * radix + digit;
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
return negate ? -n : n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,13 +31,14 @@ enum class Directive
|
|||
Include,
|
||||
String,
|
||||
Braille,
|
||||
Enum,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class AsmFile
|
||||
{
|
||||
public:
|
||||
AsmFile(std::string filename);
|
||||
AsmFile(std::string filename, bool isStdin, bool doEnum);
|
||||
AsmFile(AsmFile&& other);
|
||||
AsmFile(const AsmFile&) = delete;
|
||||
~AsmFile();
|
||||
|
|
@ -49,9 +50,11 @@ public:
|
|||
bool IsAtEnd();
|
||||
void OutputLine();
|
||||
void OutputLocation();
|
||||
bool ParseEnum();
|
||||
|
||||
private:
|
||||
char* m_buffer;
|
||||
bool m_doEnum;
|
||||
long m_pos;
|
||||
long m_size;
|
||||
long m_lineNum;
|
||||
|
|
@ -67,6 +70,11 @@ private:
|
|||
void ReportDiagnostic(const char* type, const char* format, std::va_list args);
|
||||
void RaiseError(const char* format, ...);
|
||||
void RaiseWarning(const char* format, ...);
|
||||
void VerifyStringLength(int length);
|
||||
int SkipWhitespaceAndEol();
|
||||
int FindLastLineNumber(std::string& filename);
|
||||
std::string ReadIdentifier();
|
||||
long ReadInteger(std::string filename, long line);
|
||||
};
|
||||
|
||||
#endif // ASM_FILE_H
|
||||
|
|
|
|||
|
|
@ -23,39 +23,27 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include "preproc.h"
|
||||
#include "c_file.h"
|
||||
#include "char_util.h"
|
||||
#include "utf8.h"
|
||||
#include "string_parser.h"
|
||||
#include "io.h"
|
||||
|
||||
CFile::CFile(std::string filename) : m_filename(filename)
|
||||
CFile::CFile(const char * filenameCStr, bool isStdin)
|
||||
{
|
||||
FILE *fp = std::fopen(filename.c_str(), "rb");
|
||||
if (isStdin)
|
||||
m_filename = std::string{"<stdin>/"}.append(filenameCStr);
|
||||
else
|
||||
m_filename = std::string(filenameCStr);
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
|
||||
m_size = std::ftell(fp);
|
||||
|
||||
if (m_size < 0)
|
||||
FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
|
||||
|
||||
m_buffer = new char[m_size + 1];
|
||||
|
||||
std::rewind(fp);
|
||||
|
||||
if (std::fread(m_buffer, m_size, 1, fp) != 1)
|
||||
FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
|
||||
|
||||
m_buffer[m_size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
m_buffer = ReadFileToBuffer(filenameCStr, isStdin, &m_size);
|
||||
|
||||
m_pos = 0;
|
||||
m_lineNum = 1;
|
||||
m_isStdin = isStdin;
|
||||
}
|
||||
|
||||
CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename))
|
||||
|
|
@ -64,13 +52,14 @@ CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename))
|
|||
m_pos = other.m_pos;
|
||||
m_size = other.m_size;
|
||||
m_lineNum = other.m_lineNum;
|
||||
m_isStdin = other.m_isStdin;
|
||||
|
||||
other.m_buffer = nullptr;
|
||||
other.m_buffer = NULL;
|
||||
}
|
||||
|
||||
CFile::~CFile()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
free(m_buffer);
|
||||
}
|
||||
|
||||
void CFile::Preproc()
|
||||
|
|
@ -354,7 +343,7 @@ void CFile::TryConvertIncbin()
|
|||
|
||||
if (m_buffer[m_pos] == '\\')
|
||||
RaiseError("unexpected escape in path string");
|
||||
|
||||
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +378,7 @@ void CFile::TryConvertIncbin()
|
|||
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
|
||||
if (m_buffer[m_pos] != ')')
|
||||
RaiseError("expected ')'");
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
class CFile
|
||||
{
|
||||
public:
|
||||
CFile(std::string filename);
|
||||
CFile(const char * filenameCStr, bool isStdin);
|
||||
CFile(CFile&& other);
|
||||
CFile(const CFile&) = delete;
|
||||
~CFile();
|
||||
|
|
@ -42,6 +42,7 @@ private:
|
|||
long m_size;
|
||||
long m_lineNum;
|
||||
std::string m_filename;
|
||||
bool m_isStdin;
|
||||
|
||||
bool ConsumeHorizontalWhitespace();
|
||||
bool ConsumeNewline();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
#include "preproc.h"
|
||||
#include "io.h"
|
||||
#include <string>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
char *ReadFileToBuffer(const char *filename, bool isStdin, long *size)
|
||||
{
|
||||
FILE *fp;
|
||||
if (isStdin)
|
||||
fp = stdin;
|
||||
else
|
||||
fp = std::fopen(filename, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename);
|
||||
|
||||
*size = 0;
|
||||
char *buffer = (char *)malloc(CHUNK_SIZE + 1);
|
||||
if (buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename);
|
||||
}
|
||||
|
||||
std::size_t numAllocatedBytes = CHUNK_SIZE + 1;
|
||||
std::size_t bufferOffset = 0;
|
||||
std::size_t count;
|
||||
|
||||
while ((count = std::fread(buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) {
|
||||
if (!std::ferror(fp)) {
|
||||
*size += count;
|
||||
|
||||
if (std::feof(fp)) {
|
||||
break;
|
||||
}
|
||||
|
||||
numAllocatedBytes += CHUNK_SIZE;
|
||||
bufferOffset += CHUNK_SIZE;
|
||||
buffer = (char *)realloc(buffer, numAllocatedBytes);
|
||||
if (buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename);
|
||||
}
|
||||
} else {
|
||||
FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
buffer[*size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
return buffer;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef IO_H_
|
||||
#define IO_H_
|
||||
|
||||
#define CHUNK_SIZE 4096
|
||||
|
||||
char *ReadFileToBuffer(const char *filename, bool isStdin, long *size);
|
||||
|
||||
#endif // IO_H_
|
||||
|
|
@ -20,11 +20,14 @@
|
|||
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <unistd.h>
|
||||
#include "preproc.h"
|
||||
#include "asm_file.h"
|
||||
#include "c_file.h"
|
||||
#include "charmap.h"
|
||||
|
||||
static void UsageAndExit(const char *program);
|
||||
|
||||
Charmap* g_charmap;
|
||||
|
||||
void PrintAsmBytes(unsigned char *s, int length)
|
||||
|
|
@ -43,11 +46,12 @@ void PrintAsmBytes(unsigned char *s, int length)
|
|||
}
|
||||
}
|
||||
|
||||
void PreprocAsmFile(std::string filename)
|
||||
void PreprocAsmFile(std::string filename, bool isStdin, bool doEnum)
|
||||
{
|
||||
std::stack<AsmFile> stack;
|
||||
|
||||
stack.push(AsmFile(filename));
|
||||
stack.push(AsmFile(filename, isStdin, doEnum));
|
||||
std::printf("# 1 \"%s\"\n", filename.c_str());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
|
@ -66,7 +70,7 @@ void PreprocAsmFile(std::string filename)
|
|||
switch (directive)
|
||||
{
|
||||
case Directive::Include:
|
||||
stack.push(AsmFile(stack.top().ReadPath()));
|
||||
stack.push(AsmFile(stack.top().ReadPath(), false, doEnum));
|
||||
stack.top().OutputLocation();
|
||||
break;
|
||||
case Directive::String:
|
||||
|
|
@ -83,6 +87,12 @@ void PreprocAsmFile(std::string filename)
|
|||
PrintAsmBytes(s, length);
|
||||
break;
|
||||
}
|
||||
case Directive::Enum:
|
||||
{
|
||||
if (!stack.top().ParseEnum())
|
||||
stack.top().OutputLine();
|
||||
break;
|
||||
}
|
||||
case Directive::Unknown:
|
||||
{
|
||||
std::string globalLabel = stack.top().GetGlobalLabel();
|
||||
|
|
@ -103,15 +113,15 @@ void PreprocAsmFile(std::string filename)
|
|||
}
|
||||
}
|
||||
|
||||
void PreprocCFile(std::string filename)
|
||||
void PreprocCFile(const char * filename, bool isStdin)
|
||||
{
|
||||
CFile cFile(filename);
|
||||
CFile cFile(filename, isStdin);
|
||||
cFile.Preproc();
|
||||
}
|
||||
|
||||
char* GetFileExtension(char* filename)
|
||||
const char* GetFileExtension(const char* filename)
|
||||
{
|
||||
char* extension = filename;
|
||||
const char* extension = filename;
|
||||
|
||||
while (*extension != 0)
|
||||
extension++;
|
||||
|
|
@ -130,27 +140,64 @@ char* GetFileExtension(char* filename)
|
|||
return extension;
|
||||
}
|
||||
|
||||
static void UsageAndExit(const char *program)
|
||||
{
|
||||
std::fprintf(stderr, "Usage: %s [-i] [-e] SRC_FILE CHARMAP_FILE\nwhere -i denotes if input is from stdin\n -e enables enum handling\n", program);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3)
|
||||
int opt;
|
||||
const char *source = NULL;
|
||||
const char *charmap = NULL;
|
||||
bool isStdin = false;
|
||||
bool doEnum = false;
|
||||
|
||||
/* preproc [-i] [-e] SRC_FILE CHARMAP_FILE */
|
||||
while ((opt = getopt(argc, argv, "ie")) != -1)
|
||||
{
|
||||
std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE", argv[0]);
|
||||
return 1;
|
||||
switch (opt)
|
||||
{
|
||||
case 'i':
|
||||
isStdin = true;
|
||||
break;
|
||||
case 'e':
|
||||
doEnum = true;
|
||||
break;
|
||||
default:
|
||||
UsageAndExit(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_charmap = new Charmap(argv[2]);
|
||||
if (optind + 2 != argc)
|
||||
UsageAndExit(argv[0]);
|
||||
|
||||
char* extension = GetFileExtension(argv[1]);
|
||||
source = argv[optind + 0];
|
||||
charmap = argv[optind + 1];
|
||||
|
||||
g_charmap = new Charmap(charmap);
|
||||
|
||||
const char* extension = GetFileExtension(source);
|
||||
|
||||
if (!extension)
|
||||
FATAL_ERROR("\"%s\" has no file extension.\n", argv[1]);
|
||||
|
||||
if ((extension[0] == 's') && extension[1] == 0)
|
||||
PreprocAsmFile(argv[1]);
|
||||
{
|
||||
PreprocAsmFile(source, isStdin, doEnum);
|
||||
}
|
||||
else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0)
|
||||
PreprocCFile(argv[1]);
|
||||
{
|
||||
if (doEnum)
|
||||
FATAL_ERROR("-e is invalid for C sources\n");
|
||||
PreprocCFile(source, isStdin);
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR("\"%s\" has an unknown file extension of \"%s\".\n", argv[1], extension);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
CXX = g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS = -Wall -Werror -std=c++11 -O2
|
||||
|
||||
|
|
@ -8,10 +8,16 @@ HEADERS := scaninc.h asm_file.h c_file.h source_file.h
|
|||
|
||||
.PHONY: all clean
|
||||
|
||||
all: scaninc
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
else
|
||||
EXE :=
|
||||
endif
|
||||
|
||||
all: scaninc$(EXE)
|
||||
@:
|
||||
|
||||
scaninc: $(SRCS) $(HEADERS)
|
||||
scaninc$(EXE): $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include "scaninc.h"
|
||||
#include "source_file.h"
|
||||
|
||||
|
|
@ -38,15 +41,19 @@ bool CanOpenFile(std::string path)
|
|||
return true;
|
||||
}
|
||||
|
||||
const char *const USAGE = "Usage: scaninc [-I INCLUDE_PATH] FILE_PATH\n";
|
||||
const char *const USAGE = "Usage: scaninc [-I INCLUDE_PATH] [-M DEPENDENCY_OUT_PATH] FILE_PATH\n";
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::queue<std::string> filesToProcess;
|
||||
std::set<std::string> dependencies;
|
||||
std::set<std::string> dependencies_includes;
|
||||
|
||||
std::vector<std::string> includeDirs;
|
||||
|
||||
bool makeformat = false;
|
||||
std::string make_outfile;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
|
|
@ -68,6 +75,13 @@ int main(int argc, char **argv)
|
|||
}
|
||||
includeDirs.push_back(includeDir);
|
||||
}
|
||||
else if(arg.substr(0, 2) == "-M")
|
||||
{
|
||||
makeformat = true;
|
||||
argc--;
|
||||
argv++;
|
||||
make_outfile = std::string(argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR(USAGE);
|
||||
|
|
@ -112,6 +126,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
path = include;
|
||||
}
|
||||
dependencies_includes.insert(path);
|
||||
bool inserted = dependencies.insert(path).second;
|
||||
if (inserted && exists)
|
||||
{
|
||||
|
|
@ -121,8 +136,36 @@ int main(int argc, char **argv)
|
|||
includeDirs.pop_back();
|
||||
}
|
||||
|
||||
for (const std::string &path : dependencies)
|
||||
if(!makeformat)
|
||||
{
|
||||
std::printf("%s\n", path.c_str());
|
||||
for (const std::string &path : dependencies)
|
||||
{
|
||||
std::printf("%s\n", path.c_str());
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write out make rules to a file
|
||||
std::ofstream output(make_outfile);
|
||||
|
||||
// Print a make rule for the object file
|
||||
size_t ext_pos = make_outfile.find_last_of(".");
|
||||
auto object_file = make_outfile.substr(0, ext_pos + 1) + "o";
|
||||
output << object_file.c_str() << ": ";
|
||||
for (const std::string &path : dependencies)
|
||||
{
|
||||
output << path << " ";
|
||||
}
|
||||
|
||||
// Dependency list rule.
|
||||
// Although these rules are identical, they need to be separate, else make will trigger the rule again after the file is created for the first time.
|
||||
output << "\n" << make_outfile.c_str() << ": ";
|
||||
for (const std::string &path : dependencies_includes)
|
||||
{
|
||||
output << path << " ";
|
||||
}
|
||||
output.flush();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue