Sync tools directory

This commit is contained in:
GriffinR 2024-09-12 12:39:00 -04:00
parent a14180a64e
commit f0566e68f0
25 changed files with 13669 additions and 12384 deletions

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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++;

View File

@ -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

0
tools/jsonproc/.gitignore vendored Normal file → Executable file
View File

14
tools/jsonproc/Makefile Normal file → Executable file
View File

@ -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:

5462
tools/jsonproc/inja.hpp Normal file → Executable file

File diff suppressed because it is too large Load Diff

25
tools/jsonproc/jsonproc.cpp Normal file → Executable file
View File

@ -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;
});

0
tools/jsonproc/jsonproc.h Normal file → Executable file
View File

19586
tools/jsonproc/nlohmann/json.hpp Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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;

View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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 ')'");

View File

@ -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();

51
tools/preproc/io.cpp Normal file
View File

@ -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;
}

8
tools/preproc/io.h Normal file
View File

@ -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_

View File

@ -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;
}

View File

@ -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:

View File

@ -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();
}
}