mirror of
https://github.com/pret/pmd-red.git
synced 2026-08-24 17:54:43 -05:00
update tools
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
CC = gcc
|
||||
CC ?= gcc
|
||||
|
||||
CFLAGS = -Wall -Wextra -Wno-switch -Werror -std=c11 -O2
|
||||
|
||||
@@ -6,7 +6,10 @@ LIBS = -lm
|
||||
|
||||
SRCS = main.c extended.c
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: aif2pcm
|
||||
@:
|
||||
|
||||
aif2pcm: $(SRCS)
|
||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
||||
|
||||
@@ -34,8 +34,8 @@ double ieee754_read_extended (uint8_t*);
|
||||
#define FATAL_ERROR(format, ...) \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, format, __VA_ARGS__); \
|
||||
exit(1); \
|
||||
fprintf(stderr, format, __VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
@@ -43,8 +43,8 @@ do \
|
||||
#define FATAL_ERROR(format, ...) \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, format, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
fprintf(stderr, format, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
#endif // _MSC_VER
|
||||
@@ -64,6 +64,12 @@ struct Bytes {
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct Marker {
|
||||
unsigned short id;
|
||||
unsigned long position;
|
||||
// don't care about the name
|
||||
};
|
||||
|
||||
struct Bytes *read_bytearray(const char *filename)
|
||||
{
|
||||
struct Bytes *bytes = malloc(sizeof(struct Bytes));
|
||||
@@ -167,6 +173,8 @@ void read_aif(struct Bytes *aif, AifData *aif_data)
|
||||
FATAL_ERROR("FORM Type is '%s', but it must be AIFF!", chunk_type);
|
||||
}
|
||||
|
||||
struct Marker *markers = NULL;
|
||||
unsigned short num_markers = 0, loop_start = 0, loop_end = 0;
|
||||
unsigned long num_sample_frames = 0;
|
||||
|
||||
// Read all the Chunks to populate the AifData struct.
|
||||
@@ -219,10 +227,17 @@ void read_aif(struct Bytes *aif, AifData *aif_data)
|
||||
}
|
||||
else if (strcmp(chunk_name, "MARK") == 0)
|
||||
{
|
||||
unsigned short num_markers = (aif->data[pos++] << 8);
|
||||
num_markers = (aif->data[pos++] << 8);
|
||||
num_markers |= (uint8_t)aif->data[pos++];
|
||||
|
||||
// Read each marker and look for the "START" marker.
|
||||
if (markers)
|
||||
{
|
||||
FATAL_ERROR("More than one MARK Chunk in file!\n");
|
||||
}
|
||||
|
||||
markers = calloc(num_markers, sizeof(struct Marker));
|
||||
|
||||
// Read each marker.
|
||||
for (int i = 0; i < num_markers; i++)
|
||||
{
|
||||
unsigned short marker_id = (aif->data[pos++] << 8);
|
||||
@@ -233,28 +248,16 @@ void read_aif(struct Bytes *aif, AifData *aif_data)
|
||||
marker_position |= (aif->data[pos++] << 8);
|
||||
marker_position |= (uint8_t)aif->data[pos++];
|
||||
|
||||
// Marker id is a pascal-style string.
|
||||
// Marker name is a Pascal-style string.
|
||||
uint8_t marker_name_size = aif->data[pos++];
|
||||
char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char));
|
||||
// We don't actually need the marker name for anything anymore.
|
||||
/*char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char));
|
||||
memcpy(marker_name, &aif->data[pos], marker_name_size);
|
||||
marker_name[marker_name_size] = '\0';
|
||||
pos += marker_name_size;
|
||||
marker_name[marker_name_size] = '\0';*/
|
||||
pos += marker_name_size + !(marker_name_size & 1);
|
||||
|
||||
if (strcmp(marker_name, "START") == 0)
|
||||
{
|
||||
aif_data->loop_offset = marker_position;
|
||||
aif_data->has_loop = true;
|
||||
}
|
||||
else if (strcmp(marker_name, "END") == 0)
|
||||
{
|
||||
if (!aif_data->has_loop) {
|
||||
aif_data->loop_offset = marker_position;
|
||||
aif_data->has_loop = true;
|
||||
}
|
||||
aif_data->num_samples = marker_position;
|
||||
}
|
||||
|
||||
free(marker_name);
|
||||
markers[i].id = marker_id;
|
||||
markers[i].position = marker_position;
|
||||
}
|
||||
}
|
||||
else if (strcmp(chunk_name, "INST") == 0)
|
||||
@@ -264,11 +267,31 @@ void read_aif(struct Bytes *aif, AifData *aif_data)
|
||||
aif_data->midi_note = midi_note;
|
||||
|
||||
// Skip over data we don't need.
|
||||
pos += 19;
|
||||
pos += 7;
|
||||
|
||||
unsigned short loop_type = (aif->data[pos++] << 8);
|
||||
loop_type |= (uint8_t)aif->data[pos++];
|
||||
|
||||
if (loop_type)
|
||||
{
|
||||
loop_start = (aif->data[pos++] << 8);
|
||||
loop_start |= (uint8_t)aif->data[pos++];
|
||||
|
||||
loop_end = (aif->data[pos++] << 8);
|
||||
loop_end |= (uint8_t)aif->data[pos++];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip NoLooping sustain loop.
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
// Skip release loop, we don't need it.
|
||||
pos += 6;
|
||||
}
|
||||
else if (strcmp(chunk_name, "SSND") == 0)
|
||||
{
|
||||
// SKip offset and blockSize
|
||||
// Skip offset and blockSize
|
||||
pos += 8;
|
||||
|
||||
unsigned long num_samples = chunk_size - 8;
|
||||
@@ -285,6 +308,41 @@ void read_aif(struct Bytes *aif, AifData *aif_data)
|
||||
pos += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (markers)
|
||||
{
|
||||
// Resolve loop points.
|
||||
struct Marker *cur_marker = markers;
|
||||
|
||||
// Grab loop start point.
|
||||
for (int i = 0; i < num_markers; i++, cur_marker++)
|
||||
{
|
||||
if (cur_marker->id == loop_start)
|
||||
{
|
||||
aif_data->loop_offset = cur_marker->position;
|
||||
aif_data->has_loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cur_marker = markers;
|
||||
|
||||
// Grab loop end point.
|
||||
for (int i = 0; i < num_markers; i++, cur_marker++)
|
||||
{
|
||||
if (cur_marker->id == loop_end)
|
||||
{
|
||||
if (cur_marker->position < aif_data->loop_offset) {
|
||||
aif_data->loop_offset = cur_marker->position;
|
||||
aif_data->has_loop = true;
|
||||
}
|
||||
aif_data->num_samples = cur_marker->position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(markers);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a table of deltas between sample values in compressed PCM data.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
CC = gcc
|
||||
CC ?= gcc
|
||||
|
||||
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
SRCS = bin2c.c
|
||||
|
||||
all: bin2c
|
||||
@:
|
||||
|
||||
bin2c: $(SRCS)
|
||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
CC = gcc
|
||||
.PHONY: clean
|
||||
CC ?= gcc
|
||||
.PHONY: all clean
|
||||
|
||||
SRCS = gbafix.c
|
||||
|
||||
all: gbafix
|
||||
@:
|
||||
|
||||
gbafix: $(SRCS)
|
||||
$(CC) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ int main(int argc, char *argv[])
|
||||
// parse command line
|
||||
for (arg=1; arg<argc; arg++)
|
||||
{
|
||||
if ((ARGV[0] == '-'))
|
||||
if (ARGV[0] == '-')
|
||||
{
|
||||
switch (ARGV[1])
|
||||
{
|
||||
|
||||
@@ -125,7 +125,7 @@ void ReadPng(char *path, struct Image *image)
|
||||
free(row_pointers);
|
||||
fclose(fp);
|
||||
|
||||
if (bit_depth != image->bitDepth)
|
||||
if (bit_depth != image->bitDepth && image->tilemap.data.affine == NULL)
|
||||
{
|
||||
unsigned char *src = image->pixels;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "global.h"
|
||||
#include "gfx.h"
|
||||
#include "util.h"
|
||||
@@ -203,6 +204,147 @@ static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numT
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeAffineTilemap(unsigned char *input, unsigned char *output, unsigned char *tilemap, int tileSize, int numTiles)
|
||||
{
|
||||
for (int i = 0; i < numTiles; i++)
|
||||
{
|
||||
memcpy(&output[i * tileSize], &input[tilemap[i] * tileSize], tileSize);
|
||||
}
|
||||
}
|
||||
|
||||
#define REVERSE_BIT_ORDER(x) ({ \
|
||||
((((x) >> 7) & 1) << 0) \
|
||||
| ((((x) >> 6) & 1) << 1) \
|
||||
| ((((x) >> 5) & 1) << 2) \
|
||||
| ((((x) >> 4) & 1) << 3) \
|
||||
| ((((x) >> 3) & 1) << 4) \
|
||||
| ((((x) >> 2) & 1) << 5) \
|
||||
| ((((x) >> 1) & 1) << 6) \
|
||||
| ((((x) >> 0) & 1) << 7); \
|
||||
})
|
||||
|
||||
#define SWAP_BYTES(a, b) ({ \
|
||||
unsigned char tmp = *(a); \
|
||||
*(a) = *(b); \
|
||||
*(b) = tmp; \
|
||||
})
|
||||
|
||||
#define NSWAP(x) ({ (((x) >> 4) & 0xF) | (((x) << 4) & 0xF0); })
|
||||
|
||||
#define SWAP_NYBBLES(a, b) ({ \
|
||||
unsigned char tmp = NSWAP(*(a)); \
|
||||
*(a) = NSWAP(*(b)); \
|
||||
*(b) = tmp; \
|
||||
})
|
||||
|
||||
static void VflipTile(unsigned char * tile, int bitDepth)
|
||||
{
|
||||
int i;
|
||||
switch (bitDepth)
|
||||
{
|
||||
case 1:
|
||||
SWAP_BYTES(&tile[0], &tile[7]);
|
||||
SWAP_BYTES(&tile[1], &tile[6]);
|
||||
SWAP_BYTES(&tile[2], &tile[5]);
|
||||
SWAP_BYTES(&tile[3], &tile[4]);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
SWAP_BYTES(&tile[i + 0], &tile[i + 28]);
|
||||
SWAP_BYTES(&tile[i + 4], &tile[i + 24]);
|
||||
SWAP_BYTES(&tile[i + 8], &tile[i + 20]);
|
||||
SWAP_BYTES(&tile[i + 12], &tile[i + 16]);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
SWAP_BYTES(&tile[i + 0], &tile[i + 56]);
|
||||
SWAP_BYTES(&tile[i + 8], &tile[i + 48]);
|
||||
SWAP_BYTES(&tile[i + 16], &tile[i + 40]);
|
||||
SWAP_BYTES(&tile[i + 24], &tile[i + 32]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void HflipTile(unsigned char * tile, int bitDepth)
|
||||
{
|
||||
int i;
|
||||
switch (bitDepth)
|
||||
{
|
||||
case 1:
|
||||
for (i = 0; i < 8; i++)
|
||||
tile[i] = REVERSE_BIT_ORDER(tile[i]);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
SWAP_NYBBLES(&tile[4 * i + 0], &tile[4 * i + 3]);
|
||||
SWAP_NYBBLES(&tile[4 * i + 1], &tile[4 * i + 2]);;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
SWAP_BYTES(&tile[8 * i + 0], &tile[8 * i + 7]);
|
||||
SWAP_BYTES(&tile[8 * i + 1], &tile[8 * i + 6]);
|
||||
SWAP_BYTES(&tile[8 * i + 2], &tile[8 * i + 5]);
|
||||
SWAP_BYTES(&tile[8 * i + 3], &tile[8 * i + 4]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeNonAffineTilemap(unsigned char *input, unsigned char *output, struct NonAffineTile *tilemap, int tileSize, int outTileSize, int bitDepth, int numTiles)
|
||||
{
|
||||
unsigned char * in_tile;
|
||||
unsigned char * out_tile = output;
|
||||
int effectiveBitDepth = tileSize == outTileSize ? bitDepth : 8;
|
||||
for (int i = 0; i < numTiles; i++)
|
||||
{
|
||||
in_tile = &input[tilemap[i].index * tileSize];
|
||||
if (tileSize == outTileSize)
|
||||
memcpy(out_tile, in_tile, tileSize);
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < 64; j++)
|
||||
{
|
||||
int shift = (j & 1) * 4;
|
||||
out_tile[j] = (in_tile[j / 2] & (0xF << shift)) >> shift;
|
||||
}
|
||||
}
|
||||
if (tilemap[i].hflip)
|
||||
HflipTile(out_tile, effectiveBitDepth);
|
||||
if (tilemap[i].vflip)
|
||||
VflipTile(out_tile, effectiveBitDepth);
|
||||
if (bitDepth == 4 && effectiveBitDepth == 8)
|
||||
{
|
||||
for (int j = 0; j < 64; j++)
|
||||
{
|
||||
out_tile[j] &= 0xF;
|
||||
out_tile[j] |= (15 - tilemap[i].palno) << 4;
|
||||
}
|
||||
}
|
||||
out_tile += outTileSize;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char *DecodeTilemap(unsigned char *tiles, struct Tilemap *tilemap, int *numTiles_p, bool isAffine, int tileSize, int outTileSize, int bitDepth)
|
||||
{
|
||||
int mapTileSize = isAffine ? 1 : 2;
|
||||
int numTiles = tilemap->size / mapTileSize;
|
||||
unsigned char *decoded = calloc(numTiles, outTileSize);
|
||||
if (isAffine)
|
||||
DecodeAffineTilemap(tiles, decoded, tilemap->data.affine, tileSize, numTiles);
|
||||
else
|
||||
DecodeNonAffineTilemap(tiles, decoded, tilemap->data.non_affine, tileSize, outTileSize, bitDepth, numTiles);
|
||||
free(tiles);
|
||||
*numTiles_p = numTiles;
|
||||
return decoded;
|
||||
}
|
||||
|
||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
|
||||
{
|
||||
int tileSize = bitDepth * 8;
|
||||
@@ -211,6 +353,16 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
|
||||
unsigned char *buffer = ReadWholeFile(path, &fileSize);
|
||||
|
||||
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);
|
||||
if (outTileSize == 64)
|
||||
{
|
||||
tileSize = 64;
|
||||
image->bitDepth = bitDepth = 8;
|
||||
}
|
||||
}
|
||||
|
||||
int tilesHeight = (numTiles + tilesWidth - 1) / tilesWidth;
|
||||
|
||||
@@ -298,6 +450,11 @@ void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int m
|
||||
|
||||
void FreeImage(struct Image *image)
|
||||
{
|
||||
if (image->tilemap.data.affine != NULL)
|
||||
{
|
||||
free(image->tilemap.data.affine);
|
||||
image->tilemap.data.affine = NULL;
|
||||
}
|
||||
free(image->pixels);
|
||||
image->pixels = NULL;
|
||||
}
|
||||
@@ -318,6 +475,12 @@ void ReadGbaPalette(char *path, struct Palette *palette)
|
||||
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
|
||||
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
|
||||
}
|
||||
// png can only accept 16 or 256 colors, so fill the remainder with black
|
||||
if (palette->numColors > 16)
|
||||
{
|
||||
memset(&palette->colors[palette->numColors], 0, (256 - palette->numColors) * sizeof(struct Color));
|
||||
palette->numColors = 256;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,21 @@ struct Palette {
|
||||
int numColors;
|
||||
};
|
||||
|
||||
struct NonAffineTile {
|
||||
unsigned short index:10;
|
||||
unsigned short hflip:1;
|
||||
unsigned short vflip:1;
|
||||
unsigned short palno:4;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Tilemap {
|
||||
union {
|
||||
struct NonAffineTile *non_affine;
|
||||
unsigned char *affine;
|
||||
} data;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct Image {
|
||||
int width;
|
||||
int height;
|
||||
@@ -25,6 +40,8 @@ struct Image {
|
||||
bool hasPalette;
|
||||
struct Palette palette;
|
||||
bool hasTransparency;
|
||||
struct Tilemap tilemap;
|
||||
bool isAffine;
|
||||
};
|
||||
|
||||
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
|
||||
|
||||
@@ -27,7 +27,17 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
|
||||
|
||||
if (options->paletteFilePath != NULL)
|
||||
{
|
||||
ReadGbaPalette(options->paletteFilePath, &image.palette);
|
||||
char *paletteFileExtension = GetFileExtensionAfterDot(options->paletteFilePath);
|
||||
|
||||
if (strcmp(paletteFileExtension, "gbapal") == 0)
|
||||
{
|
||||
ReadGbaPalette(options->paletteFilePath, &image.palette);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadJascPalette(options->paletteFilePath, &image.palette);
|
||||
}
|
||||
|
||||
image.hasPalette = true;
|
||||
}
|
||||
else
|
||||
@@ -35,6 +45,20 @@ void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
|
||||
image.hasPalette = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
image.tilemap.data.affine = NULL;
|
||||
}
|
||||
|
||||
ReadImage(inputPath, options->width, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
|
||||
|
||||
image.hasTransparency = options->hasTransparency;
|
||||
@@ -49,6 +73,7 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
|
||||
struct Image image;
|
||||
|
||||
image.bitDepth = options->bitDepth;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
ReadPng(inputPath, &image);
|
||||
|
||||
@@ -59,7 +84,7 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
|
||||
|
||||
void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **argv)
|
||||
{
|
||||
char *inputFileExtension = GetFileExtension(inputPath);
|
||||
char *inputFileExtension = GetFileExtensionAfterDot(inputPath);
|
||||
struct GbaToPngOptions options;
|
||||
options.paletteFilePath = NULL;
|
||||
options.bitDepth = inputFileExtension[0] - '0';
|
||||
@@ -67,6 +92,7 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
||||
options.width = 1;
|
||||
options.metatileWidth = 1;
|
||||
options.metatileHeight = 1;
|
||||
options.isAffineMap = false;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
@@ -124,6 +150,17 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
||||
if (options.metatileHeight < 1)
|
||||
FATAL_ERROR("metatile height must be positive.\n");
|
||||
}
|
||||
else if (strcmp(option, "-tilemap") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
FATAL_ERROR("No tilemap value following \"-tilemap\".\n");
|
||||
i++;
|
||||
options.tilemapFilePath = argv[i];
|
||||
}
|
||||
else if (strcmp(option, "-affine") == 0)
|
||||
{
|
||||
options.isAffineMap = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
|
||||
@@ -138,13 +175,15 @@ void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **a
|
||||
|
||||
void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv)
|
||||
{
|
||||
char *outputFileExtension = GetFileExtension(outputPath);
|
||||
char *outputFileExtension = GetFileExtensionAfterDot(outputPath);
|
||||
int bitDepth = outputFileExtension[0] - '0';
|
||||
struct PngToGbaOptions options;
|
||||
options.numTiles = 0;
|
||||
options.bitDepth = bitDepth;
|
||||
options.metatileWidth = 1;
|
||||
options.metatileHeight = 1;
|
||||
options.tilemapFilePath = NULL;
|
||||
options.isAffineMap = false;
|
||||
|
||||
for (int i = 3; i < argc; i++)
|
||||
{
|
||||
@@ -198,9 +237,17 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
|
||||
ConvertPngToGba(inputPath, outputPath, &options);
|
||||
}
|
||||
|
||||
void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Palette palette = {};
|
||||
|
||||
ReadPngPalette(inputPath, &palette);
|
||||
WriteJascPalette(outputPath, &palette);
|
||||
}
|
||||
|
||||
void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Palette palette;
|
||||
struct Palette palette = {};
|
||||
|
||||
ReadPngPalette(inputPath, &palette);
|
||||
WriteGbaPalette(outputPath, &palette);
|
||||
@@ -208,7 +255,7 @@ void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UN
|
||||
|
||||
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Palette palette;
|
||||
struct Palette palette = {};
|
||||
|
||||
ReadGbaPalette(inputPath, &palette);
|
||||
WriteJascPalette(outputPath, &palette);
|
||||
@@ -241,7 +288,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc,
|
||||
}
|
||||
}
|
||||
|
||||
struct Palette palette;
|
||||
struct Palette palette = {};
|
||||
|
||||
ReadJascPalette(inputPath, &palette);
|
||||
|
||||
@@ -254,6 +301,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc,
|
||||
void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
ReadLatinFont(inputPath, &image);
|
||||
WritePng(outputPath, &image);
|
||||
@@ -264,6 +312,7 @@ void HandleLatinFontToPngCommand(char *inputPath, char *outputPath, int argc UNU
|
||||
void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
image.bitDepth = 2;
|
||||
|
||||
@@ -276,6 +325,7 @@ void HandlePngToLatinFontCommand(char *inputPath, char *outputPath, int argc UNU
|
||||
void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
ReadHalfwidthJapaneseFont(inputPath, &image);
|
||||
WritePng(outputPath, &image);
|
||||
@@ -286,6 +336,7 @@ void HandleHalfwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath,
|
||||
void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
image.bitDepth = 2;
|
||||
|
||||
@@ -298,6 +349,7 @@ void HandlePngToHalfwidthJapaneseFontCommand(char *inputPath, char *outputPath,
|
||||
void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
ReadFullwidthJapaneseFont(inputPath, &image);
|
||||
WritePng(outputPath, &image);
|
||||
@@ -308,6 +360,7 @@ void HandleFullwidthJapaneseFontToPngCommand(char *inputPath, char *outputPath,
|
||||
void HandlePngToFullwidthJapaneseFontCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
|
||||
{
|
||||
struct Image image;
|
||||
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
|
||||
|
||||
image.bitDepth = 2;
|
||||
|
||||
@@ -483,6 +536,8 @@ void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNU
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char converted = 0;
|
||||
|
||||
if (argc < 3)
|
||||
FATAL_ERROR("Usage: gbagfx INPUT_PATH OUTPUT_PATH [options...]\n");
|
||||
|
||||
@@ -495,6 +550,7 @@ int main(int argc, char **argv)
|
||||
{ "png", "4bpp", HandlePngToGbaCommand },
|
||||
{ "png", "8bpp", HandlePngToGbaCommand },
|
||||
{ "png", "gbapal", HandlePngToGbaPaletteCommand },
|
||||
{ "png", "pal", HandlePngToJascPaletteCommand },
|
||||
{ "gbapal", "pal", HandleGbaToJascPaletteCommand },
|
||||
{ "pal", "gbapal", HandleJascToGbaPaletteCommand },
|
||||
{ "latfont", "png", HandleLatinFontToPngCommand },
|
||||
@@ -514,14 +570,39 @@ int main(int argc, char **argv)
|
||||
|
||||
char *inputPath = argv[1];
|
||||
char *outputPath = argv[2];
|
||||
char *inputFileExtension = GetFileExtension(inputPath);
|
||||
char *outputFileExtension = GetFileExtension(outputPath);
|
||||
char *inputFileExtension = GetFileExtensionAfterDot(inputPath);
|
||||
char *outputFileExtension = GetFileExtensionAfterDot(outputPath);
|
||||
|
||||
if (inputFileExtension == NULL)
|
||||
FATAL_ERROR("Input file \"%s\" has no extension.\n", inputPath);
|
||||
|
||||
if (outputFileExtension == NULL)
|
||||
FATAL_ERROR("Output file \"%s\" has no extension.\n", outputPath);
|
||||
{
|
||||
outputFileExtension = GetFileExtension(outputPath);
|
||||
|
||||
if (*outputFileExtension == '.')
|
||||
outputFileExtension++;
|
||||
|
||||
if (*outputFileExtension == 0)
|
||||
FATAL_ERROR("Output file \"%s\" has no extension.\n", outputPath);
|
||||
|
||||
size_t newOutputPathSize = strlen(inputPath) - strlen(inputFileExtension) + strlen(outputFileExtension);
|
||||
outputPath = malloc(newOutputPathSize);
|
||||
|
||||
if (outputPath == NULL)
|
||||
FATAL_ERROR("Failed to allocate memory for new output path.\n");
|
||||
|
||||
for (int i = 0; i < newOutputPathSize; i++)
|
||||
{
|
||||
outputPath[i] = inputPath[i];
|
||||
|
||||
if (outputPath[i] == '.')
|
||||
{
|
||||
strcpy(&outputPath[i + 1], outputFileExtension);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; handlers[i].function != NULL; i++)
|
||||
{
|
||||
@@ -529,9 +610,16 @@ int main(int argc, char **argv)
|
||||
&& (handlers[i].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0))
|
||||
{
|
||||
handlers[i].function(inputPath, outputPath, argc, argv);
|
||||
return 0;
|
||||
converted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FATAL_ERROR("Don't know how to convert \"%s\" to \"%s\".\n", inputPath, outputPath);
|
||||
if (outputPath != argv[2])
|
||||
free(outputPath);
|
||||
|
||||
if (!converted)
|
||||
FATAL_ERROR("Don't know how to convert \"%s\" to \"%s\".\n", argv[1], argv[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ struct GbaToPngOptions {
|
||||
int width;
|
||||
int metatileWidth;
|
||||
int metatileHeight;
|
||||
char *tilemapFilePath;
|
||||
bool isAffineMap;
|
||||
};
|
||||
|
||||
struct PngToGbaOptions {
|
||||
@@ -19,6 +21,8 @@ struct PngToGbaOptions {
|
||||
int bitDepth;
|
||||
int metatileWidth;
|
||||
int metatileHeight;
|
||||
char *tilemapFilePath;
|
||||
bool isAffineMap;
|
||||
};
|
||||
|
||||
#endif // OPTIONS_H
|
||||
|
||||
@@ -47,6 +47,13 @@ char *GetFileExtension(char *path)
|
||||
while (extension > path && *extension != '.')
|
||||
extension--;
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
char *GetFileExtensionAfterDot(char *path)
|
||||
{
|
||||
char *extension = GetFileExtension(path);
|
||||
|
||||
if (extension == path)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
bool ParseNumber(char *s, char **end, int radix, int *intValue);
|
||||
char *GetFileExtension(char *path);
|
||||
char *GetFileExtensionAfterDot(char *path);
|
||||
unsigned char *ReadWholeFile(char *path, int *size);
|
||||
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount);
|
||||
void WriteWholeFile(char *path, void *buffer, int bufferSize);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -Wall -std=c++11 -O2
|
||||
|
||||
@@ -8,7 +8,7 @@ SRCS := jsonproc.cpp
|
||||
|
||||
HEADERS := jsonproc.h inja.hpp nlohmann/json.hpp
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: jsonproc
|
||||
@:
|
||||
|
||||
@@ -517,7 +517,7 @@ public:
|
||||
typedef const_pointer iterator;
|
||||
typedef const_pointer const_iterator;
|
||||
typedef std::reverse_iterator< const_iterator > reverse_iterator;
|
||||
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
|
||||
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
|
||||
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
@@ -1411,6 +1411,9 @@ enum class ElementNotation {
|
||||
Pointer
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Class for lexer configuration.
|
||||
*/
|
||||
struct LexerConfig {
|
||||
std::string statement_open {"{%"};
|
||||
std::string statement_close {"%}"};
|
||||
@@ -1421,6 +1424,9 @@ struct LexerConfig {
|
||||
std::string comment_close {"#}"};
|
||||
std::string open_chars {"#{"};
|
||||
|
||||
bool trim_blocks {false};
|
||||
bool lstrip_blocks {false};
|
||||
|
||||
void update_open_chars() {
|
||||
open_chars = "";
|
||||
if (open_chars.find(line_statement[0]) == std::string::npos) {
|
||||
@@ -1438,6 +1444,9 @@ struct LexerConfig {
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Class for parser configuration.
|
||||
*/
|
||||
struct ParserConfig {
|
||||
ElementNotation notation {ElementNotation::Dot};
|
||||
};
|
||||
@@ -1450,10 +1459,13 @@ struct ParserConfig {
|
||||
#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
|
||||
#define PANTOR_INJA_FUNCTION_STORAGE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
// #include "bytecode.hpp"
|
||||
#ifndef PANTOR_INJA_BYTECODE_HPP
|
||||
#define PANTOR_INJA_BYTECODE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -1464,7 +1476,7 @@ struct ParserConfig {
|
||||
|
||||
namespace inja {
|
||||
|
||||
using namespace nlohmann;
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
||||
struct Bytecode {
|
||||
@@ -1492,6 +1504,7 @@ struct Bytecode {
|
||||
GreaterEqual,
|
||||
Less,
|
||||
LessEqual,
|
||||
At,
|
||||
Different,
|
||||
DivisibleBy,
|
||||
Even,
|
||||
@@ -1594,6 +1607,9 @@ using namespace nlohmann;
|
||||
using Arguments = std::vector<const json*>;
|
||||
using CallbackFunction = std::function<json(Arguments& args)>;
|
||||
|
||||
/*!
|
||||
* \brief Class for builtin functions and user-defined callbacks.
|
||||
*/
|
||||
class FunctionStorage {
|
||||
public:
|
||||
void add_builtin(nonstd::string_view name, unsigned int num_args, Bytecode::Op op) {
|
||||
@@ -1658,6 +1674,9 @@ class FunctionStorage {
|
||||
#define PANTOR_INJA_PARSER_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// #include "bytecode.hpp"
|
||||
|
||||
@@ -1678,12 +1697,17 @@ class FunctionStorage {
|
||||
#ifndef PANTOR_INJA_TOKEN_HPP
|
||||
#define PANTOR_INJA_TOKEN_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
// #include "string_view.hpp"
|
||||
|
||||
|
||||
|
||||
namespace inja {
|
||||
|
||||
/*!
|
||||
* \brief Helper-class for the inja Parser.
|
||||
*/
|
||||
struct Token {
|
||||
enum class Kind {
|
||||
Text,
|
||||
@@ -1737,13 +1761,17 @@ struct Token {
|
||||
|
||||
}
|
||||
|
||||
#endif // PANTOR_INJA_TOKEN_HPP
|
||||
#endif // PANTOR_INJA_TOKEN_HPP
|
||||
|
||||
// #include "utils.hpp"
|
||||
#ifndef PANTOR_INJA_UTILS_HPP
|
||||
#define PANTOR_INJA_UTILS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
// #include "string_view.hpp"
|
||||
|
||||
@@ -1755,11 +1783,22 @@ inline void inja_throw(const std::string& type, const std::string& message) {
|
||||
throw std::runtime_error("[inja.exception." + type + "] " + message);
|
||||
}
|
||||
|
||||
inline std::ifstream open_file_or_throw(const std::string& path) {
|
||||
std::ifstream file;
|
||||
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
try {
|
||||
file.open(path);
|
||||
} catch(const std::ios_base::failure& e) {
|
||||
inja_throw("file_error", "failed accessing file at '" + path + "'");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
namespace string_view {
|
||||
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
|
||||
start = std::min(start, view.size());
|
||||
end = std::min(std::max(start, end), view.size());
|
||||
return view.substr(start, end - start); // StringRef(Data + Start, End - Start);
|
||||
return view.substr(start, end - start); // StringRef(Data + Start, End - Start);
|
||||
}
|
||||
|
||||
inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
|
||||
@@ -1783,6 +1822,9 @@ namespace string_view {
|
||||
|
||||
namespace inja {
|
||||
|
||||
/*!
|
||||
* \brief Class for lexing an inja Template.
|
||||
*/
|
||||
class Lexer {
|
||||
enum class State {
|
||||
Text,
|
||||
@@ -1831,12 +1873,15 @@ class Lexer {
|
||||
|
||||
// try to match one of the opening sequences, and get the close
|
||||
nonstd::string_view open_str = m_in.substr(m_pos);
|
||||
bool must_lstrip = false;
|
||||
if (inja::string_view::starts_with(open_str, m_config.expression_open)) {
|
||||
m_state = State::ExpressionStart;
|
||||
} else if (inja::string_view::starts_with(open_str, m_config.statement_open)) {
|
||||
m_state = State::StatementStart;
|
||||
must_lstrip = m_config.lstrip_blocks;
|
||||
} else if (inja::string_view::starts_with(open_str, m_config.comment_open)) {
|
||||
m_state = State::CommentStart;
|
||||
must_lstrip = m_config.lstrip_blocks;
|
||||
} else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') &&
|
||||
inja::string_view::starts_with(open_str, m_config.line_statement)) {
|
||||
m_state = State::LineStart;
|
||||
@@ -1844,8 +1889,13 @@ class Lexer {
|
||||
m_pos += 1; // wasn't actually an opening sequence
|
||||
goto again;
|
||||
}
|
||||
if (m_pos == m_tok_start) goto again; // don't generate empty token
|
||||
return make_token(Token::Kind::Text);
|
||||
|
||||
nonstd::string_view text = string_view::slice(m_in, m_tok_start, m_pos);
|
||||
if (must_lstrip)
|
||||
text = clear_final_line_if_whitespace(text);
|
||||
|
||||
if (text.empty()) goto again; // don't generate empty token
|
||||
return Token(Token::Kind::Text, text);
|
||||
}
|
||||
case State::ExpressionStart: {
|
||||
m_state = State::ExpressionBody;
|
||||
@@ -1872,7 +1922,7 @@ class Lexer {
|
||||
case State::LineBody:
|
||||
return scan_body("\n", Token::Kind::LineStatementClose);
|
||||
case State::StatementBody:
|
||||
return scan_body(m_config.statement_close, Token::Kind::StatementClose);
|
||||
return scan_body(m_config.statement_close, Token::Kind::StatementClose, m_config.trim_blocks);
|
||||
case State::CommentBody: {
|
||||
// fast-scan to comment close
|
||||
size_t end = m_in.substr(m_pos).find(m_config.comment_close);
|
||||
@@ -1883,7 +1933,10 @@ class Lexer {
|
||||
// return the entire comment in the close token
|
||||
m_state = State::Text;
|
||||
m_pos += end + m_config.comment_close.size();
|
||||
return make_token(Token::Kind::CommentClose);
|
||||
Token tok = make_token(Token::Kind::CommentClose);
|
||||
if (m_config.trim_blocks)
|
||||
skip_newline();
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1891,7 +1944,7 @@ class Lexer {
|
||||
const LexerConfig& get_config() const { return m_config; }
|
||||
|
||||
private:
|
||||
Token scan_body(nonstd::string_view close, Token::Kind closeKind) {
|
||||
Token scan_body(nonstd::string_view close, Token::Kind closeKind, bool trim = false) {
|
||||
again:
|
||||
// skip whitespace (except for \n as it might be a close)
|
||||
if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof);
|
||||
@@ -1905,7 +1958,10 @@ class Lexer {
|
||||
if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) {
|
||||
m_state = State::Text;
|
||||
m_pos = m_tok_start + close.size();
|
||||
return make_token(closeKind);
|
||||
Token tok = make_token(closeKind);
|
||||
if (trim)
|
||||
skip_newline();
|
||||
return tok;
|
||||
}
|
||||
|
||||
// skip \n
|
||||
@@ -2026,6 +2082,34 @@ class Lexer {
|
||||
Token make_token(Token::Kind kind) const {
|
||||
return Token(kind, string_view::slice(m_in, m_tok_start, m_pos));
|
||||
}
|
||||
|
||||
void skip_newline() {
|
||||
if (m_pos < m_in.size()) {
|
||||
char ch = m_in[m_pos];
|
||||
if (ch == '\n')
|
||||
m_pos += 1;
|
||||
else if (ch == '\r') {
|
||||
m_pos += 1;
|
||||
if (m_pos < m_in.size() && m_in[m_pos] == '\n')
|
||||
m_pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text)
|
||||
{
|
||||
nonstd::string_view result = text;
|
||||
while (!result.empty()) {
|
||||
char ch = result.back();
|
||||
if (ch == ' ' || ch == '\t')
|
||||
result.remove_suffix(1);
|
||||
else if (ch == '\n' || ch == '\r')
|
||||
break;
|
||||
else
|
||||
return text;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2036,6 +2120,7 @@ class Lexer {
|
||||
#ifndef PANTOR_INJA_TEMPLATE_HPP
|
||||
#define PANTOR_INJA_TEMPLATE_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -2045,6 +2130,9 @@ class Lexer {
|
||||
|
||||
namespace inja {
|
||||
|
||||
/*!
|
||||
* \brief The main inja Template.
|
||||
*/
|
||||
struct Template {
|
||||
std::vector<Bytecode> bytecodes;
|
||||
std::string content;
|
||||
@@ -2054,7 +2142,7 @@ using TemplateStorage = std::map<std::string, Template>;
|
||||
|
||||
}
|
||||
|
||||
#endif // PANTOR_INJA_TEMPLATE_HPP
|
||||
#endif // PANTOR_INJA_TEMPLATE_HPP
|
||||
|
||||
// #include "token.hpp"
|
||||
|
||||
@@ -2068,6 +2156,7 @@ namespace inja {
|
||||
|
||||
class ParserStatic {
|
||||
ParserStatic() {
|
||||
functions.add_builtin("at", 2, Bytecode::Op::At);
|
||||
functions.add_builtin("default", 2, Bytecode::Op::Default);
|
||||
functions.add_builtin("divisibleBy", 2, Bytecode::Op::DivisibleBy);
|
||||
functions.add_builtin("even", 1, Bytecode::Op::Even);
|
||||
@@ -2107,13 +2196,16 @@ class ParserStatic {
|
||||
FunctionStorage functions;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Class for parsing an inja Template.
|
||||
*/
|
||||
class Parser {
|
||||
public:
|
||||
explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& included_templates): m_config(parser_config), m_lexer(lexer_config), m_included_templates(included_templates), m_static(ParserStatic::get_instance()) { }
|
||||
|
||||
bool parse_expression(Template& tmpl) {
|
||||
if (!parse_expression_and(tmpl)) return false;
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != "or") return true;
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("or")) return true;
|
||||
get_next_token();
|
||||
if (!parse_expression_and(tmpl)) return false;
|
||||
append_function(tmpl, Bytecode::Op::Or, 2);
|
||||
@@ -2122,7 +2214,7 @@ class Parser {
|
||||
|
||||
bool parse_expression_and(Template& tmpl) {
|
||||
if (!parse_expression_not(tmpl)) return false;
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != "and") return true;
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("and")) return true;
|
||||
get_next_token();
|
||||
if (!parse_expression_not(tmpl)) return false;
|
||||
append_function(tmpl, Bytecode::Op::And, 2);
|
||||
@@ -2130,7 +2222,7 @@ class Parser {
|
||||
}
|
||||
|
||||
bool parse_expression_not(Template& tmpl) {
|
||||
if (m_tok.kind == Token::Kind::Id && m_tok.text == "not") {
|
||||
if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast<decltype(m_tok.text)>("not")) {
|
||||
get_next_token();
|
||||
if (!parse_expression_not(tmpl)) return false;
|
||||
append_function(tmpl, Bytecode::Op::Not, 1);
|
||||
@@ -2145,7 +2237,7 @@ class Parser {
|
||||
Bytecode::Op op;
|
||||
switch (m_tok.kind) {
|
||||
case Token::Kind::Id:
|
||||
if (m_tok.text == "in")
|
||||
if (m_tok.text == static_cast<decltype(m_tok.text)>("in"))
|
||||
op = Bytecode::Op::In;
|
||||
else
|
||||
return true;
|
||||
@@ -2233,7 +2325,9 @@ class Parser {
|
||||
append_callback(tmpl, func_token.text, num_args);
|
||||
return true;
|
||||
}
|
||||
} else if (m_tok.text == "true" || m_tok.text == "false" || m_tok.text == "null") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("true") ||
|
||||
m_tok.text == static_cast<decltype(m_tok.text)>("false") ||
|
||||
m_tok.text == static_cast<decltype(m_tok.text)>("null")) {
|
||||
// true, false, null are json literals
|
||||
if (brace_level == 0 && bracket_level == 0) {
|
||||
json_first = m_tok.text;
|
||||
@@ -2312,7 +2406,7 @@ class Parser {
|
||||
bool parse_statement(Template& tmpl, nonstd::string_view path) {
|
||||
if (m_tok.kind != Token::Kind::Id) return false;
|
||||
|
||||
if (m_tok.text == "if") {
|
||||
if (m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
|
||||
get_next_token();
|
||||
|
||||
// evaluate expression
|
||||
@@ -2323,7 +2417,7 @@ class Parser {
|
||||
|
||||
// conditional jump; destination will be filled in by else or endif
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
|
||||
} else if (m_tok.text == "endif") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("endif")) {
|
||||
if (m_if_stack.empty()) {
|
||||
inja_throw("parser_error", "endif without matching if");
|
||||
}
|
||||
@@ -2342,7 +2436,7 @@ class Parser {
|
||||
|
||||
// pop if stack
|
||||
m_if_stack.pop_back();
|
||||
} else if (m_tok.text == "else") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("else")) {
|
||||
if (m_if_stack.empty())
|
||||
inja_throw("parser_error", "else without matching if");
|
||||
auto& if_data = m_if_stack.back();
|
||||
@@ -2358,7 +2452,7 @@ class Parser {
|
||||
if_data.prev_cond_jump = std::numeric_limits<unsigned int>::max();
|
||||
|
||||
// chained else if
|
||||
if (m_tok.kind == Token::Kind::Id && m_tok.text == "if") {
|
||||
if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
|
||||
get_next_token();
|
||||
|
||||
// evaluate expression
|
||||
@@ -2370,7 +2464,7 @@ class Parser {
|
||||
// conditional jump; destination will be filled in by else or endif
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
|
||||
}
|
||||
} else if (m_tok.text == "for") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("for")) {
|
||||
get_next_token();
|
||||
|
||||
// options: for a in arr; for a, b in obj
|
||||
@@ -2389,7 +2483,7 @@ class Parser {
|
||||
get_next_token();
|
||||
}
|
||||
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != "in")
|
||||
if (m_tok.kind != Token::Kind::Id || m_tok.text != static_cast<decltype(m_tok.text)>("in"))
|
||||
inja_throw("parser_error",
|
||||
"expected 'in', got '" + m_tok.describe() + "'");
|
||||
get_next_token();
|
||||
@@ -2403,7 +2497,7 @@ class Parser {
|
||||
tmpl.bytecodes.back().value = key_token.text;
|
||||
}
|
||||
tmpl.bytecodes.back().str = static_cast<std::string>(value_token.text);
|
||||
} else if (m_tok.text == "endfor") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("endfor")) {
|
||||
get_next_token();
|
||||
if (m_loop_stack.empty()) {
|
||||
inja_throw("parser_error", "endfor without matching for");
|
||||
@@ -2415,7 +2509,7 @@ class Parser {
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::EndLoop);
|
||||
tmpl.bytecodes.back().args = m_loop_stack.back() + 1; // loop body
|
||||
m_loop_stack.pop_back();
|
||||
} else if (m_tok.text == "include") {
|
||||
} else if (m_tok.text == static_cast<decltype(m_tok.text)>("include")) {
|
||||
get_next_token();
|
||||
|
||||
if (m_tok.kind != Token::Kind::String) {
|
||||
@@ -2431,8 +2525,10 @@ class Parser {
|
||||
}
|
||||
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);
|
||||
|
||||
Template include_template = parse_template(pathname);
|
||||
m_included_templates.emplace(pathname, include_template);
|
||||
if (m_included_templates.find(pathname) == m_included_templates.end()) {
|
||||
Template include_template = parse_template(pathname);
|
||||
m_included_templates.emplace(pathname, include_template);
|
||||
}
|
||||
|
||||
// generate a reference bytecode
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::Include, json(pathname), Bytecode::Flag::ValueImmediate);
|
||||
@@ -2552,10 +2648,10 @@ class Parser {
|
||||
}
|
||||
|
||||
std::string load_file(nonstd::string_view filename) {
|
||||
std::ifstream file(static_cast<std::string>(filename));
|
||||
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
return text;
|
||||
}
|
||||
std::ifstream file = open_file_or_throw(static_cast<std::string>(filename));
|
||||
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
return text;
|
||||
}
|
||||
|
||||
private:
|
||||
const ParserConfig& m_config;
|
||||
@@ -2605,6 +2701,7 @@ class Parser {
|
||||
#if __cplusplus < 201402L
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -2655,6 +2752,9 @@ namespace stdinja = std;
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -2679,6 +2779,9 @@ inline nonstd::string_view convert_dot_to_json_pointer(nonstd::string_view dot,
|
||||
return nonstd::string_view(out.data(), out.size());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Class for rendering a Template with data.
|
||||
*/
|
||||
class Renderer {
|
||||
std::vector<const json*>& get_args(const Bytecode& bc) {
|
||||
m_tmp_args.clear();
|
||||
@@ -2765,7 +2868,7 @@ class Renderer {
|
||||
LoopLevel& level = m_loop_stack.back();
|
||||
|
||||
if (level.loop_type == LoopLevel::Type::Array) {
|
||||
level.data[static_cast<std::string>(level.value_name)] = level.values.at(level.index); // *level.it;
|
||||
level.data[static_cast<std::string>(level.value_name)] = level.values.at(level.index); // *level.it;
|
||||
auto& loopData = level.data["loop"];
|
||||
loopData["index"] = level.index;
|
||||
loopData["index1"] = level.index + 1;
|
||||
@@ -2787,8 +2890,8 @@ class Renderer {
|
||||
enum class Type { Map, Array };
|
||||
|
||||
Type loop_type;
|
||||
nonstd::string_view key_name; // variable name for keys
|
||||
nonstd::string_view value_name; // variable name for values
|
||||
nonstd::string_view key_name; // variable name for keys
|
||||
nonstd::string_view value_name; // variable name for values
|
||||
json data; // data with loop info added
|
||||
|
||||
json values; // values to iterate over
|
||||
@@ -2800,8 +2903,8 @@ class Renderer {
|
||||
// loop over map
|
||||
using KeyValue = std::pair<nonstd::string_view, json*>;
|
||||
using MapValues = std::vector<KeyValue>;
|
||||
MapValues map_values; // values to iterate over
|
||||
MapValues::iterator map_it; // iterator over values
|
||||
MapValues map_values; // values to iterate over
|
||||
MapValues::iterator map_it; // iterator over values
|
||||
|
||||
};
|
||||
|
||||
@@ -2835,11 +2938,11 @@ class Renderer {
|
||||
}
|
||||
case Bytecode::Op::PrintValue: {
|
||||
const json& val = *get_args(bc)[0];
|
||||
if (val.is_string())
|
||||
if (val.is_string()) {
|
||||
os << val.get_ref<const std::string&>();
|
||||
else
|
||||
} else {
|
||||
os << val.dump();
|
||||
// val.dump(os);
|
||||
}
|
||||
pop_args(bc);
|
||||
break;
|
||||
}
|
||||
@@ -2870,7 +2973,15 @@ class Renderer {
|
||||
break;
|
||||
}
|
||||
case Bytecode::Op::Length: {
|
||||
auto result = get_args(bc)[0]->size();
|
||||
const json& val = *get_args(bc)[0];
|
||||
|
||||
int result;
|
||||
if (val.is_string()) {
|
||||
result = val.get_ref<const std::string&>().length();
|
||||
} else {
|
||||
result = val.size();
|
||||
}
|
||||
|
||||
pop_args(bc);
|
||||
m_stack.emplace_back(result);
|
||||
break;
|
||||
@@ -2882,6 +2993,13 @@ class Renderer {
|
||||
m_stack.emplace_back(std::move(result));
|
||||
break;
|
||||
}
|
||||
case Bytecode::Op::At: {
|
||||
auto args = get_args(bc);
|
||||
auto result = args[0]->at(args[1]->get<int>());
|
||||
pop_args(bc);
|
||||
m_stack.emplace_back(result);
|
||||
break;
|
||||
}
|
||||
case Bytecode::Op::First: {
|
||||
auto result = get_args(bc)[0]->front();
|
||||
pop_args(bc);
|
||||
@@ -3091,7 +3209,7 @@ class Renderer {
|
||||
break;
|
||||
}
|
||||
case Bytecode::Op::Include:
|
||||
Renderer(m_included_templates, m_callbacks).render_to(os, m_included_templates.find(get_imm(bc)->get_ref<const std::string&>())->second, data);
|
||||
Renderer(m_included_templates, m_callbacks).render_to(os, m_included_templates.find(get_imm(bc)->get_ref<const std::string&>())->second, *m_data);
|
||||
break;
|
||||
case Bytecode::Op::Callback: {
|
||||
auto callback = m_callbacks.find_callback(bc.str, bc.args);
|
||||
@@ -3216,12 +3334,17 @@ class Renderer {
|
||||
|
||||
// #include "template.hpp"
|
||||
|
||||
// #include "utils.hpp"
|
||||
|
||||
|
||||
|
||||
namespace inja {
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
/*!
|
||||
* \brief Class for changing the configuration.
|
||||
*/
|
||||
class Environment {
|
||||
class Impl {
|
||||
public:
|
||||
@@ -3238,7 +3361,7 @@ class Environment {
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
public:
|
||||
Environment(): Environment("./") { }
|
||||
Environment(): Environment("") { }
|
||||
|
||||
explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
|
||||
m_impl->input_path = global_path;
|
||||
@@ -3277,6 +3400,16 @@ class Environment {
|
||||
m_impl->lexer_config.update_open_chars();
|
||||
}
|
||||
|
||||
/// Sets whether to remove the first newline after a block
|
||||
void set_trim_blocks(bool trim_blocks) {
|
||||
m_impl->lexer_config.trim_blocks = trim_blocks;
|
||||
}
|
||||
|
||||
/// Sets whether to strip the spaces and tabs from the start of a line to a block
|
||||
void set_lstrip_blocks(bool lstrip_blocks) {
|
||||
m_impl->lexer_config.lstrip_blocks = lstrip_blocks;
|
||||
}
|
||||
|
||||
/// Sets the element notation syntax
|
||||
void set_element_notation(ElementNotation notation) {
|
||||
m_impl->parser_config.notation = notation;
|
||||
@@ -3290,8 +3423,8 @@ class Environment {
|
||||
|
||||
Template parse_template(const std::string& filename) {
|
||||
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
|
||||
return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
|
||||
}
|
||||
return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
|
||||
}
|
||||
|
||||
std::string render(nonstd::string_view input, const json& data) {
|
||||
return render(parse(input), data);
|
||||
@@ -3304,35 +3437,35 @@ class Environment {
|
||||
}
|
||||
|
||||
std::string render_file(const std::string& filename, const json& data) {
|
||||
return render(parse_template(filename), data);
|
||||
}
|
||||
return render(parse_template(filename), data);
|
||||
}
|
||||
|
||||
std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
|
||||
const json data = load_json(filename_data);
|
||||
return render_file(filename, data);
|
||||
}
|
||||
const json data = load_json(filename_data);
|
||||
return render_file(filename, data);
|
||||
}
|
||||
|
||||
void write(const std::string& filename, const json& data, const std::string& filename_out) {
|
||||
std::ofstream file(m_impl->output_path + filename_out);
|
||||
file << render_file(filename, data);
|
||||
file.close();
|
||||
}
|
||||
std::ofstream file(m_impl->output_path + filename_out);
|
||||
file << render_file(filename, data);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void write(const Template& temp, const json& data, const std::string& filename_out) {
|
||||
std::ofstream file(m_impl->output_path + filename_out);
|
||||
file << render(temp, data);
|
||||
file.close();
|
||||
}
|
||||
std::ofstream file(m_impl->output_path + filename_out);
|
||||
file << render(temp, data);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
|
||||
const json data = load_json(filename_data);
|
||||
write(filename, data, filename_out);
|
||||
}
|
||||
void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
|
||||
const json data = load_json(filename_data);
|
||||
write(filename, data, filename_out);
|
||||
}
|
||||
|
||||
void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
|
||||
const json data = load_json(filename_data);
|
||||
write(temp, data, filename_out);
|
||||
}
|
||||
void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
|
||||
const json data = load_json(filename_data);
|
||||
write(temp, data, filename_out);
|
||||
}
|
||||
|
||||
std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
|
||||
Renderer(m_impl->included_templates, m_impl->callbacks).render_to(os, tmpl, data);
|
||||
@@ -3341,15 +3474,15 @@ class Environment {
|
||||
|
||||
std::string load_file(const std::string& filename) {
|
||||
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
|
||||
return parser.load_file(m_impl->input_path + filename);
|
||||
}
|
||||
return parser.load_file(m_impl->input_path + filename);
|
||||
}
|
||||
|
||||
json load_json(const std::string& filename) {
|
||||
std::ifstream file(m_impl->input_path + filename);
|
||||
json j;
|
||||
file >> j;
|
||||
return j;
|
||||
}
|
||||
std::ifstream file = open_file_or_throw(m_impl->input_path + filename);
|
||||
json j;
|
||||
file >> j;
|
||||
return j;
|
||||
}
|
||||
|
||||
void add_callback(const std::string& name, unsigned int numArgs, const CallbackFunction& callback) {
|
||||
m_impl->callbacks.add_callback(name, numArgs, callback);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <map>
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
using std::string; using std::to_string;
|
||||
|
||||
#include <inja.hpp>
|
||||
using namespace inja;
|
||||
@@ -36,7 +36,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Add custom command callbacks.
|
||||
env.add_callback("doNotModifyHeader", 0, [jsonfilepath, templateFilepath](Arguments& args) {
|
||||
return "//\n// DO NOT MODIFY THIS FILE! IT IS AUTO-GENERATED FROM " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n";
|
||||
return "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from " + jsonfilepath +" and Inja template " + templateFilepath + "\n//\n";
|
||||
});
|
||||
|
||||
env.add_callback("subtract", 2, [](Arguments& args) {
|
||||
int minuend = args.at(0)->get<int>();
|
||||
int subtrahend = args.at(1)->get<int>();
|
||||
|
||||
return minuend - subtrahend;
|
||||
});
|
||||
|
||||
env.add_callback("setVar", 2, [=](Arguments& args) {
|
||||
@@ -46,6 +53,13 @@ int main(int argc, char *argv[])
|
||||
return "";
|
||||
});
|
||||
|
||||
env.add_callback("setVarInt", 2, [=](Arguments& args) {
|
||||
string key = args.at(0)->get<string>();
|
||||
string value = to_string(args.at(1)->get<int>());
|
||||
set_custom_var(key, value);
|
||||
return "";
|
||||
});
|
||||
|
||||
env.add_callback("getVar", 1, [=](Arguments& args) {
|
||||
string key = args.at(0)->get<string>();
|
||||
return get_custom_var(key);
|
||||
@@ -67,7 +81,6 @@ int main(int argc, char *argv[])
|
||||
return rawValue.erase(0, prefix.length());
|
||||
});
|
||||
|
||||
// Add custom command callbacks.
|
||||
env.add_callback("removeSuffix", 2, [](Arguments& args) {
|
||||
string rawValue = args.at(0)->get<string>();
|
||||
string suffix = args.at(1)->get<string>();
|
||||
@@ -78,6 +91,11 @@ int main(int argc, char *argv[])
|
||||
return rawValue.substr(0, i);
|
||||
});
|
||||
|
||||
// single argument is a json object
|
||||
env.add_callback("isEmpty", 1, [](Arguments& args) {
|
||||
return args.at(0)->empty();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -Wall -std=c++11 -O2
|
||||
|
||||
@@ -6,7 +6,10 @@ SRCS := json11.cpp mapjson.cpp
|
||||
|
||||
HEADERS := mapjson.h
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: mapjson
|
||||
@:
|
||||
|
||||
mapjson: $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
@@ -29,7 +29,6 @@ using json11::Json;
|
||||
|
||||
#include "mapjson.h"
|
||||
|
||||
string version;
|
||||
|
||||
string read_text_file(string filepath) {
|
||||
ifstream in_file(filepath);
|
||||
@@ -61,7 +60,7 @@ void write_text_file(string filepath, string text) {
|
||||
out_file.close();
|
||||
}
|
||||
|
||||
string generate_map_header_text(Json map_data, Json layouts_data) {
|
||||
string generate_map_header_text(Json map_data, Json layouts_data, string version) {
|
||||
string map_layout_id = map_data["layout"].string_value();
|
||||
|
||||
vector<Json> matched;
|
||||
@@ -78,7 +77,7 @@ string generate_map_header_text(Json map_data, Json layouts_data) {
|
||||
|
||||
ostringstream text;
|
||||
|
||||
text << map_data["name"].string_value() << "::\n"
|
||||
text << map_data["name"].string_value() << ":\n"
|
||||
<< "\t.4byte " << layout["name"].string_value() << "\n";
|
||||
|
||||
if (map_data.object_items().find("shared_events_map") != map_data.object_items().end())
|
||||
@@ -92,7 +91,7 @@ string generate_map_header_text(Json map_data, Json layouts_data) {
|
||||
text << "\t.4byte " << map_data["name"].string_value() << "_MapScripts\n";
|
||||
|
||||
if (map_data.object_items().find("connections") != map_data.object_items().end()
|
||||
&& map_data["connections"].array_items().size() > 0 && map_data["connections_no_include"] == Json())
|
||||
&& map_data["connections"].array_items().size() > 0)
|
||||
text << "\t.4byte " << map_data["name"].string_value() << "_MapConnections\n";
|
||||
else
|
||||
text << "\t.4byte 0x0\n";
|
||||
@@ -102,27 +101,17 @@ string generate_map_header_text(Json map_data, Json layouts_data) {
|
||||
<< "\t.byte " << map_data["region_map_section"].string_value() << "\n"
|
||||
<< "\t.byte " << map_data["requires_flash"].bool_value() << "\n"
|
||||
<< "\t.byte " << map_data["weather"].string_value() << "\n"
|
||||
<< "\t.byte " << map_data["map_type"].string_value() << "\n";
|
||||
<< "\t.byte " << map_data["map_type"].string_value() << "\n"
|
||||
<< "\t.2byte 0\n";
|
||||
|
||||
if (version == "firered")
|
||||
text << "\t.byte " << map_data["unknown_18"].int_value() << "\n"
|
||||
<< "\t.byte " << map_data["unknown_19"].int_value() << "\n";
|
||||
else
|
||||
text << "\t.2byte 0\n";
|
||||
|
||||
if (version == "ruby") {
|
||||
if (version == "ruby")
|
||||
text << "\t.byte " << map_data["show_map_name"].bool_value() << "\n";
|
||||
}
|
||||
else if (version == "emerald") {
|
||||
else if (version == "emerald")
|
||||
text << "\tmap_header_flags "
|
||||
<< "allow_bike=" << map_data["allow_bike"].bool_value() << ", "
|
||||
<< "allow_escape_rope=" << map_data["allow_escape_rope"].bool_value() << ", "
|
||||
<< "allow_run=" << map_data["allow_running"].bool_value() << ", "
|
||||
<< "allow_cycling=" << map_data["allow_cycling"].bool_value() << ", "
|
||||
<< "allow_escaping=" << map_data["allow_escaping"].bool_value() << ", "
|
||||
<< "allow_running=" << map_data["allow_running"].bool_value() << ", "
|
||||
<< "show_map_name=" << map_data["show_map_name"].bool_value() << "\n";
|
||||
}
|
||||
else if (version == "firered") {
|
||||
text << "\t.byte " << map_data["elevator_flag"].int_value() << "\n";
|
||||
}
|
||||
|
||||
text << "\t.byte " << map_data["battle_scene"].string_value() << "\n\n";
|
||||
|
||||
@@ -135,7 +124,7 @@ string generate_map_connections_text(Json map_data) {
|
||||
|
||||
ostringstream text;
|
||||
|
||||
text << map_data["name"].string_value() << "_MapConnectionsList::\n";
|
||||
text << map_data["name"].string_value() << "_MapConnectionsList:\n";
|
||||
|
||||
for (auto &connection : map_data["connections"].array_items()) {
|
||||
text << "\tconnection "
|
||||
@@ -144,7 +133,7 @@ string generate_map_connections_text(Json map_data) {
|
||||
<< connection["map"].string_value() << "\n";
|
||||
}
|
||||
|
||||
text << "\n" << map_data["name"].string_value() << "_MapConnections::\n"
|
||||
text << "\n" << map_data["name"].string_value() << "_MapConnections:\n"
|
||||
<< "\t.4byte " << map_data["connections"].array_items().size() << "\n"
|
||||
<< "\t.4byte " << map_data["name"].string_value() << "_MapConnectionsList\n\n";
|
||||
|
||||
@@ -160,7 +149,7 @@ string generate_map_events_text(Json map_data) {
|
||||
string objects_label, warps_label, coords_label, bgs_label;
|
||||
|
||||
if (map_data["object_events"].array_items().size() > 0) {
|
||||
objects_label = map_data["name"].string_value() + "_EventObjects";
|
||||
objects_label = map_data["name"].string_value() + "_ObjectEvents";
|
||||
text << objects_label << ":\n";
|
||||
for (unsigned int i = 0; i < map_data["object_events"].array_items().size(); i++) {
|
||||
auto obj_event = map_data["object_events"].array_items()[i];
|
||||
@@ -264,120 +253,13 @@ string generate_map_events_text(Json map_data) {
|
||||
return text.str();
|
||||
}
|
||||
|
||||
string generate_firered_map_events_text(Json map_data) {
|
||||
ostringstream text;
|
||||
|
||||
string objects_label, warps_label, coords_label, bgs_label;
|
||||
|
||||
if (map_data["object_events"].array_items().size() > 0) {
|
||||
objects_label = map_data["name"].string_value() + "_EventObjects";
|
||||
text << objects_label << "::\n";
|
||||
for (unsigned int i = 0; i < map_data["object_events"].array_items().size(); i++) {
|
||||
auto obj_event = map_data["object_events"].array_items()[i];
|
||||
text << "\tobject_event " << i + 1 << ", "
|
||||
<< obj_event["graphics_id"].string_value() << ", "
|
||||
<< obj_event["x"].int_value() << ", "
|
||||
<< obj_event["y"].int_value() << ", "
|
||||
<< obj_event["elevation"].int_value() << ", "
|
||||
<< obj_event["movement_type"].string_value() << ", "
|
||||
<< obj_event["movement_range_x"].int_value() << ", "
|
||||
<< obj_event["movement_range_y"].int_value() << ", "
|
||||
<< obj_event["trainer_type"].int_value() << ", "
|
||||
<< obj_event["trainer_sight_or_berry_tree_id"].int_value() << ", "
|
||||
<< obj_event["script"].string_value() << ", "
|
||||
<< obj_event["flag"].string_value() << "\n";
|
||||
}
|
||||
text << "\n";
|
||||
} else {
|
||||
objects_label = "0x0";
|
||||
}
|
||||
|
||||
if (map_data["warp_events"].array_items().size() > 0) {
|
||||
warps_label = map_data["name"].string_value() + "_MapWarps";
|
||||
text << warps_label << "::\n";
|
||||
for (auto &warp_event : map_data["warp_events"].array_items()) {
|
||||
text << "\twarp_def "
|
||||
<< warp_event["x"].int_value() << ", "
|
||||
<< warp_event["y"].int_value() << ", "
|
||||
<< warp_event["elevation"].int_value() << ", "
|
||||
<< warp_event["dest_warp_id"].int_value() << ", "
|
||||
<< warp_event["dest_map"].string_value() << "\n";
|
||||
}
|
||||
text << "\n";
|
||||
} else {
|
||||
warps_label = "0x0";
|
||||
}
|
||||
|
||||
if (map_data["coord_events"].array_items().size() > 0) {
|
||||
coords_label = map_data["name"].string_value() + "_MapCoordEvents";
|
||||
text << coords_label << "::\n";
|
||||
for (auto &coord_event : map_data["coord_events"].array_items()) {
|
||||
if (coord_event["type"].string_value() == "trigger") {
|
||||
text << "\tcoord_event "
|
||||
<< coord_event["x"].int_value() << ", "
|
||||
<< coord_event["y"].int_value() << ", "
|
||||
<< coord_event["elevation"].int_value() << ", 0, "
|
||||
<< coord_event["var"].string_value() << ", "
|
||||
<< coord_event["var_value"].int_value() << ", 0, "
|
||||
<< coord_event["script"].string_value() << "\n";
|
||||
}
|
||||
else if (coord_event["type"] == "weather") {
|
||||
text << "\tcoord_weather_event "
|
||||
<< coord_event["x"].int_value() << ", "
|
||||
<< coord_event["y"].int_value() << ", "
|
||||
<< coord_event["elevation"].int_value() << ", "
|
||||
<< coord_event["weather"].string_value() << "\n";
|
||||
}
|
||||
}
|
||||
text << "\n";
|
||||
} else {
|
||||
coords_label = "0x0";
|
||||
}
|
||||
|
||||
if (map_data["bg_events"].array_items().size() > 0) {
|
||||
bgs_label = map_data["name"].string_value() + "_MapBGEvents";
|
||||
text << bgs_label << "::\n";
|
||||
for (auto &bg_event : map_data["bg_events"].array_items()) {
|
||||
if (bg_event["type"] == "hidden_item") {
|
||||
text << "\tbg_hidden_item_event "
|
||||
<< bg_event["x"].int_value() << ", "
|
||||
<< bg_event["y"].int_value() << ", "
|
||||
<< bg_event["elevation"].int_value() << ", "
|
||||
<< bg_event["item"].string_value() << ", "
|
||||
<< bg_event["flag"].string_value() << ", "
|
||||
<< bg_event["unknown"].int_value() << "\n";
|
||||
}
|
||||
else {
|
||||
string type_string = bg_event["type"].string_value();
|
||||
type_string.erase(0, 14);
|
||||
int type = std::stoi(type_string);
|
||||
text << "\tbg_event "
|
||||
<< bg_event["x"].int_value() << ", "
|
||||
<< bg_event["y"].int_value() << ", "
|
||||
<< bg_event["elevation"].int_value() << ", "
|
||||
<< type << ", 0, "
|
||||
<< bg_event["script"].string_value() << "\n";
|
||||
}
|
||||
}
|
||||
text << "\n";
|
||||
} else {
|
||||
bgs_label = "0x0";
|
||||
}
|
||||
|
||||
text << map_data["name"].string_value() << "_MapEvents::\n"
|
||||
<< "\tmap_events " << objects_label << ", " << warps_label << ", "
|
||||
<< coords_label << ", " << bgs_label << "\n\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
string get_directory_name(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 version) {
|
||||
string mapdata_err, layouts_err;
|
||||
|
||||
string mapdata_json_text = read_text_file(map_filepath);
|
||||
@@ -391,9 +273,8 @@ void process_map(string map_filepath, string layouts_filepath) {
|
||||
if (layouts_data == Json())
|
||||
FATAL_ERROR("%s\n", layouts_err.c_str());
|
||||
|
||||
string header_text = generate_map_header_text(map_data, layouts_data);
|
||||
string events_text = version == "firered" ? generate_firered_map_events_text(map_data)
|
||||
: generate_map_events_text(map_data);
|
||||
string header_text = generate_map_header_text(map_data, layouts_data, version);
|
||||
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);
|
||||
@@ -414,10 +295,7 @@ string generate_groups_text(Json groups_data) {
|
||||
text << "\n";
|
||||
}
|
||||
|
||||
if (version != "firered")
|
||||
text << "\t.align 2\n";
|
||||
|
||||
text << "gMapGroups::\n";
|
||||
text << "\t.align 2\n" << "gMapGroups::\n";
|
||||
for (auto &group : groups_data["group_order"].array_items())
|
||||
text << "\t.4byte " << group.string_value() << "\n";
|
||||
text << "\n";
|
||||
@@ -551,7 +429,6 @@ string generate_layout_headers_text(Json layouts_data) {
|
||||
ostringstream text;
|
||||
|
||||
for (auto &layout : layouts_data["layouts"].array_items()) {
|
||||
if (layout == Json::object()) continue;
|
||||
string border_label = layout["name"].string_value() + "_Border";
|
||||
string blockdata_label = layout["name"].string_value() + "_Blockdata";
|
||||
text << border_label << "::\n"
|
||||
@@ -565,15 +442,7 @@ string generate_layout_headers_text(Json layouts_data) {
|
||||
<< "\t.4byte " << border_label << "\n"
|
||||
<< "\t.4byte " << blockdata_label << "\n"
|
||||
<< "\t.4byte " << layout["primary_tileset"].string_value() << "\n"
|
||||
<< "\t.4byte " << layout["secondary_tileset"].string_value() << "\n";
|
||||
|
||||
if (version == "firered") {
|
||||
text << "\t.byte " << layout["border_width"].int_value() << "\n"
|
||||
<< "\t.byte " << layout["border_height"].int_value() << "\n"
|
||||
<< "\t.2byte 0\n";
|
||||
}
|
||||
|
||||
text << "\n";
|
||||
<< "\t.4byte " << layout["secondary_tileset"].string_value() << "\n\n";
|
||||
}
|
||||
|
||||
return text.str();
|
||||
@@ -585,11 +454,8 @@ string generate_layouts_table_text(Json layouts_data) {
|
||||
text << "\t.align 2\n"
|
||||
<< layouts_data["layouts_table_label"].string_value() << "::\n";
|
||||
|
||||
for (auto &layout : layouts_data["layouts"].array_items()) {
|
||||
string layout_name = layout["name"].string_value();
|
||||
if (layout_name.empty()) layout_name = "NULL";
|
||||
text << "\t.4byte " << layout_name << "\n";
|
||||
}
|
||||
for (auto &layout : layouts_data["layouts"].array_items())
|
||||
text << "\t.4byte " << layout["name"].string_value() << "\n";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
@@ -600,12 +466,9 @@ string generate_layouts_constants_text(Json layouts_data) {
|
||||
text << "#ifndef GUARD_CONSTANTS_LAYOUTS_H\n"
|
||||
<< "#define GUARD_CONSTANTS_LAYOUTS_H\n\n";
|
||||
|
||||
int i = 1;
|
||||
for (auto &layout : layouts_data["layouts"].array_items()) {
|
||||
if (layout != Json::object())
|
||||
text << "#define " << layout["id"].string_value() << " " << i << "\n";
|
||||
i++;
|
||||
}
|
||||
int i = 0;
|
||||
for (auto &layout : layouts_data["layouts"].array_items())
|
||||
text << "#define " << layout["id"].string_value() << " " << ++i << "\n";
|
||||
|
||||
text << "\n#endif // GUARD_CONSTANTS_LAYOUTS_H\n";
|
||||
|
||||
@@ -635,9 +498,10 @@ int main(int argc, char *argv[]) {
|
||||
if (argc < 3)
|
||||
FATAL_ERROR("USAGE: mapjson <mode> <game-version> [options]\n");
|
||||
|
||||
version = argv[2];
|
||||
if (version != "emerald" && version != "ruby" && version != "firered")
|
||||
FATAL_ERROR("ERROR: <game-version> must be 'emerald', 'firered', or 'ruby'.\n");
|
||||
char *version_arg = argv[2];
|
||||
string version(version_arg);
|
||||
if (version != "emerald" && version != "ruby")
|
||||
FATAL_ERROR("ERROR: <game-version> must be 'emerald' or 'ruby'.\n");
|
||||
|
||||
char *mode_arg = argv[1];
|
||||
string mode(mode_arg);
|
||||
@@ -651,7 +515,7 @@ int main(int argc, char *argv[]) {
|
||||
string filepath(argv[3]);
|
||||
string layouts_filepath(argv[4]);
|
||||
|
||||
process_map(filepath, layouts_filepath);
|
||||
process_map(filepath, layouts_filepath, version);
|
||||
}
|
||||
else if (mode == "groups") {
|
||||
if (argc != 4)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
||||
|
||||
@@ -8,7 +8,10 @@ SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \
|
||||
HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
|
||||
utf8.h
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: preproc
|
||||
@:
|
||||
|
||||
preproc: $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <stdexcept>
|
||||
#include "preproc.h"
|
||||
#include "asm_file.h"
|
||||
#include "char_util.h"
|
||||
@@ -475,9 +476,11 @@ void AsmFile::ExpectEmptyRestOfLine()
|
||||
m_lineStart = m_pos;
|
||||
m_lineNum++;
|
||||
}
|
||||
else if (m_buffer[m_pos] == '\r')
|
||||
else if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
|
||||
{
|
||||
RaiseError("only Unix-style LF newlines are supported");
|
||||
m_pos += 2;
|
||||
m_lineStart = m_pos;
|
||||
m_lineNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "preproc.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX := g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
||||
|
||||
@@ -6,7 +6,10 @@ SRCS := main.cpp sym_file.cpp elf.cpp
|
||||
|
||||
HEADERS := ramscrgen.h sym_file.h elf.h char_util.h
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: ramscrgen
|
||||
@:
|
||||
|
||||
ramscrgen: $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CC = gcc
|
||||
CC ?= gcc
|
||||
|
||||
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
|
||||
|
||||
@@ -6,7 +6,10 @@ LIBS = -lpng -lz
|
||||
|
||||
SRCS = main.c convert_png.c util.c font.c
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: rsfont
|
||||
@:
|
||||
|
||||
rsfont: $(SRCS) convert_png.h gfx.h global.h util.h font.h
|
||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CXX = g++
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS = -Wall -Werror -std=c++11 -O2
|
||||
|
||||
@@ -6,7 +6,10 @@ SRCS = scaninc.cpp c_file.cpp asm_file.cpp source_file.cpp
|
||||
|
||||
HEADERS := scaninc.h asm_file.h c_file.h source_file.h
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all clean
|
||||
|
||||
all: scaninc
|
||||
@:
|
||||
|
||||
scaninc: $(SRCS) $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
@@ -97,19 +97,26 @@ int main(int argc, char **argv)
|
||||
}
|
||||
for (auto include : file.GetIncludes())
|
||||
{
|
||||
bool exists = false;
|
||||
std::string path("");
|
||||
for (auto includeDir : includeDirs)
|
||||
{
|
||||
std::string path(includeDir + include);
|
||||
path = includeDir + include;
|
||||
if (CanOpenFile(path))
|
||||
{
|
||||
bool inserted = dependencies.insert(path).second;
|
||||
if (inserted)
|
||||
{
|
||||
filesToProcess.push(path);
|
||||
}
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists && file.FileType() == SourceFileType::Asm)
|
||||
{
|
||||
path = include;
|
||||
}
|
||||
bool inserted = dependencies.insert(path).second;
|
||||
if (inserted && exists)
|
||||
{
|
||||
filesToProcess.push(path);
|
||||
}
|
||||
}
|
||||
includeDirs.pop_back();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ SourceFile::SourceFile(std::string path)
|
||||
}
|
||||
}
|
||||
|
||||
SourceFileType SourceFile::FileType()
|
||||
{
|
||||
return m_file_type;
|
||||
}
|
||||
|
||||
SourceFile::~SourceFile()
|
||||
{
|
||||
if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header)
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
const std::set<std::string>& GetIncbins();
|
||||
const std::set<std::string>& GetIncludes();
|
||||
std::string& GetSrcDir();
|
||||
SourceFileType FileType();
|
||||
|
||||
private:
|
||||
union InnerUnion {
|
||||
|
||||
Reference in New Issue
Block a user