import from github

This commit is contained in:
2022-05-19 17:14:13 +00:00
parent 5247c34f50
commit ab32b30591
12612 changed files with 1905035 additions and 83 deletions

2
tools/aif2pcm/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
aif2pcm

20
tools/aif2pcm/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2016 huderlem
Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
tools/aif2pcm/Makefile Normal file
View File

@ -0,0 +1,24 @@
CC ?= gcc
CFLAGS = -Wall -Wextra -Wno-switch -Werror -std=c11 -O2
LIBS = -lm
SRCS = main.c extended.c
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
.PHONY: all clean
all: aif2pcm$(EXE)
@:
aif2pcm$(EXE): $(SRCS)
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
clean:
$(RM) aif2pcm aif2pcm.exe

172
tools/aif2pcm/extended.c Normal file
View File

@ -0,0 +1,172 @@
/* $Id: extended.c,v 1.8 2006/12/23 11:17:49 toad32767 Exp $ */
/*-
* Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
*
* Permission is hereby granted, free of charge, to any
* person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice
* shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <string.h>
#include <stdint.h>
/*
* Infinite & NAN values
* for non-IEEE systems
*/
#ifndef HUGE_VAL
#ifdef HUGE
#define INFINITE_VALUE HUGE
#define NAN_VALUE HUGE
#endif
#else
#define INFINITE_VALUE HUGE_VAL
#define NAN_VALUE HUGE_VAL
#endif
/*
* IEEE 754 Extended Precision
*
* Implementation here is the 80-bit extended precision
* format of Motorola 68881, Motorola 68882 and Motorola
* 68040 FPUs, as well as Intel 80x87 FPUs.
*
* See:
* http://www.freescale.com/files/32bit/doc/fact_sheet/BR509.pdf
*/
/*
* Exponent range: [-16383,16383]
* Precision for mantissa: 64 bits with no hidden bit
* Bias: 16383
*/
/*
* Write IEEE Extended Precision Numbers
*/
void
ieee754_write_extended(double in, uint8_t* out)
{
int sgn, exp, shift;
double fraction, t;
unsigned int lexp, hexp;
unsigned long low, high;
if (in == 0.0) {
memset(out, 0, 10);
return;
}
if (in < 0.0) {
in = fabs(in);
sgn = 1;
} else
sgn = 0;
fraction = frexp(in, &exp);
if (exp == 0 || exp > 16384) {
if (exp > 16384) /* infinite value */
low = high = 0;
else {
low = 0x80000000;
high = 0;
}
exp = 32767;
goto done;
}
fraction = ldexp(fraction, 32);
t = floor(fraction);
low = (unsigned long) t;
fraction -= t;
t = floor(ldexp(fraction, 32));
high = (unsigned long) t;
/* Convert exponents < -16382 to -16382 (then they will be
* stored as -16383) */
if (exp < -16382) {
shift = 0 - exp - 16382;
high >>= shift;
high |= (low << (32 - shift));
low >>= shift;
exp = -16382;
}
exp += 16383 - 1; /* bias */
done:
lexp = ((unsigned int) exp) >> 8;
hexp = ((unsigned int) exp) & 0xFF;
/* big endian */
out[0] = ((uint8_t) sgn) << 7;
out[0] |= (uint8_t) lexp;
out[1] = (uint8_t) hexp;
out[2] = (uint8_t) (low >> 24);
out[3] = (uint8_t) ((low >> 16) & 0xFF);
out[4] = (uint8_t) ((low >> 8) & 0xFF);
out[5] = (uint8_t) (low & 0xFF);
out[6] = (uint8_t) (high >> 24);
out[7] = (uint8_t) ((high >> 16) & 0xFF);
out[8] = (uint8_t) ((high >> 8) & 0xFF);
out[9] = (uint8_t) (high & 0xFF);
return;
}
/*
* Read IEEE Extended Precision Numbers
*/
double
ieee754_read_extended(uint8_t* in)
{
int sgn, exp;
unsigned long low, high;
double out;
/* Extract the components from the big endian buffer */
sgn = (int) (in[0] >> 7);
exp = ((int) (in[0] & 0x7F) << 8) | ((int) in[1]);
low = (((unsigned long) in[2]) << 24)
| (((unsigned long) in[3]) << 16)
| (((unsigned long) in[4]) << 8) | (unsigned long) in[5];
high = (((unsigned long) in[6]) << 24)
| (((unsigned long) in[7]) << 16)
| (((unsigned long) in[8]) << 8) | (unsigned long) in[9];
if (exp == 0 && low == 0 && high == 0)
return (sgn ? -0.0 : 0.0);
switch (exp) {
case 32767:
if (low == 0 && high == 0)
return (sgn ? -INFINITE_VALUE : INFINITE_VALUE);
else
return (sgn ? -NAN_VALUE : NAN_VALUE);
default:
exp -= 16383; /* unbias exponent */
}
out = ldexp((double) low, -31 + exp);
out += ldexp((double) high, -63 + exp);
return (sgn ? -out : out);
}

911
tools/aif2pcm/main.c Normal file
View File

@ -0,0 +1,911 @@
// Copyright(c) 2016 huderlem
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
/* extended.c */
void ieee754_write_extended (double, uint8_t*);
double ieee754_read_extended (uint8_t*);
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
typedef struct {
unsigned long num_samples;
uint8_t *samples;
uint8_t midi_note;
bool has_loop;
unsigned long loop_offset;
double sample_rate;
unsigned long real_num_samples;
} AifData;
struct Bytes {
unsigned long length;
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));
FILE *f = fopen(filename, "rb");
if (!f)
{
FATAL_ERROR("Failed to open '%s' for reading!\n", filename);
}
fseek(f, 0, SEEK_END);
bytes->length = ftell(f);
fseek(f, 0, SEEK_SET);
bytes->data = malloc(bytes->length);
unsigned long read = fread(bytes->data, bytes->length, 1, f);
fclose(f);
if (read <= 0)
{
FATAL_ERROR("Failed to read data from '%s'!\n", filename);
}
return bytes;
}
void write_bytearray(const char *filename, struct Bytes *bytes)
{
FILE *f = fopen(filename, "wb");
if (!f)
{
FATAL_ERROR("Failed to open '%s' for writing!\n", filename);
}
fwrite(bytes->data, bytes->length, 1, f);
fclose(f);
}
void free_bytearray(struct Bytes *bytes)
{
free(bytes->data);
free(bytes);
}
char *get_file_extension(char *filename)
{
char *index = strrchr(filename, '.');
if (!index || index == filename)
{
return NULL;
}
return index + 1;
}
char *new_file_extension(char *filename, char *ext)
{
char *index = strrchr(filename, '.');
if (!index || index == filename)
{
index = filename + strlen(filename);
}
int length = index - filename;
char *new_filename = malloc(length + 1 + strlen(ext) + 1);
if (new_filename)
{
strcpy(new_filename, filename);
new_filename[length] = '.';
strcpy(new_filename + length + 1, ext);
}
return new_filename;
}
void read_aif(struct Bytes *aif, AifData *aif_data)
{
aif_data->has_loop = false;
aif_data->num_samples = 0;
unsigned long pos = 0;
char chunk_name[5]; chunk_name[4] = '\0';
char chunk_type[5]; chunk_type[4] = '\0';
// Check for FORM Chunk
memcpy(chunk_name, &aif->data[pos], 4);
pos += 4;
if (strcmp(chunk_name, "FORM") != 0)
{
FATAL_ERROR("Input .aif file has invalid header Chunk '%s'!\n", chunk_name);
}
// Read size of whole file.
unsigned long whole_chunk_size = aif->data[pos++] << 24;
whole_chunk_size |= (aif->data[pos++] << 16);
whole_chunk_size |= (aif->data[pos++] << 8);
whole_chunk_size |= (uint8_t)aif->data[pos++];
unsigned long expected_whole_chunk_size = aif->length - 8;
if (whole_chunk_size != expected_whole_chunk_size)
{
FATAL_ERROR("FORM Chunk ckSize '%lu' doesn't match actual size '%lu'!\n", whole_chunk_size, expected_whole_chunk_size);
}
// Check for AIFF Form Type
memcpy(chunk_type, &aif->data[pos], 4);
pos += 4;
if (strcmp(chunk_type, "AIFF") != 0)
{
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.
while ((pos + 8) < aif->length)
{
// Read Chunk id
memcpy(chunk_name, &aif->data[pos], 4);
pos += 4;
unsigned long chunk_size = (aif->data[pos++] << 24);
chunk_size |= (aif->data[pos++] << 16);
chunk_size |= (aif->data[pos++] << 8);
chunk_size |= aif->data[pos++];
if ((pos + chunk_size) > aif->length)
{
FATAL_ERROR("%s chunk at 0x%lx reached end of file before finishing\n", chunk_name, pos);
}
if (strcmp(chunk_name, "COMM") == 0)
{
short num_channels = (aif->data[pos++] << 8);
num_channels |= (uint8_t)aif->data[pos++];
if (num_channels != 1)
{
FATAL_ERROR("numChannels (%d) in the COMM Chunk must be 1!\n", num_channels);
}
num_sample_frames = (aif->data[pos++] << 24);
num_sample_frames |= (aif->data[pos++] << 16);
num_sample_frames |= (aif->data[pos++] << 8);
num_sample_frames |= (uint8_t)aif->data[pos++];
short sample_size = (aif->data[pos++] << 8);
sample_size |= (uint8_t)aif->data[pos++];
if (sample_size != 8)
{
FATAL_ERROR("sampleSize (%d) in the COMM Chunk must be 8!\n", sample_size);
}
double sample_rate = ieee754_read_extended((uint8_t*)(aif->data + pos));
pos += 10;
aif_data->sample_rate = sample_rate;
if (aif_data->num_samples == 0)
{
aif_data->num_samples = num_sample_frames;
}
}
else if (strcmp(chunk_name, "MARK") == 0)
{
num_markers = (aif->data[pos++] << 8);
num_markers |= (uint8_t)aif->data[pos++];
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);
marker_id |= (uint8_t)aif->data[pos++];
unsigned long marker_position = (aif->data[pos++] << 24);
marker_position |= (aif->data[pos++] << 16);
marker_position |= (aif->data[pos++] << 8);
marker_position |= (uint8_t)aif->data[pos++];
// Marker name is a Pascal-style string.
uint8_t marker_name_size = aif->data[pos++];
// 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_size & 1);
markers[i].id = marker_id;
markers[i].position = marker_position;
}
}
else if (strcmp(chunk_name, "INST") == 0)
{
uint8_t midi_note = (uint8_t)aif->data[pos++];
aif_data->midi_note = midi_note;
// Skip over data we don't need.
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
pos += 8;
unsigned long num_samples = chunk_size - 8;
uint8_t *sample_data = (uint8_t *)malloc(num_samples * sizeof(uint8_t));
memcpy(sample_data, &aif->data[pos], num_samples);
aif_data->samples = sample_data;
aif_data->real_num_samples = num_samples;
pos += chunk_size - 8;
}
else
{
// Skip over unsupported chunks.
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.
const int gDeltaEncodingTable[] = {
0, 1, 4, 9, 16, 25, 36, 49,
-64, -49, -36, -25, -16, -9, -4, -1,
};
#define POSITIVE_DELTAS_START 0
#define POSITIVE_DELTAS_END 8
#define NEGATIVE_DELTAS_START 8
#define NEGATIVE_DELTAS_END 16
struct Bytes *delta_decompress(struct Bytes *delta, unsigned int expected_length)
{
struct Bytes *pcm = malloc(sizeof(struct Bytes));
pcm->length = expected_length;
pcm->data = malloc(pcm->length + 0x40);
uint8_t hi, lo;
unsigned int i = 0;
unsigned int j = 0;
int k;
int8_t base;
while (i < delta->length)
{
base = (int8_t)delta->data[i++];
pcm->data[j++] = (uint8_t)base;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
lo = delta->data[i] & 0xf;
base += gDeltaEncodingTable[lo];
pcm->data[j++] = base;
i++;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
for (k = 0; k < 31; k++)
{
hi = (delta->data[i] >> 4) & 0xf;
base += gDeltaEncodingTable[hi];
pcm->data[j++] = base;
if (j >= pcm->length)
{
break;
}
lo = delta->data[i] & 0xf;
base += gDeltaEncodingTable[lo];
pcm->data[j++] = base;
i++;
if (i >= delta->length)
{
break;
}
if (j >= pcm->length)
{
break;
}
}
if (j >= pcm->length)
{
break;
}
}
pcm->length = j;
return pcm;
}
#define U8_TO_S8(value) ((value) < 128 ? (value) : (value) - 256)
#define ABS(value) ((value) >= 0 ? (value) : -(value))
int get_delta_index(uint8_t sample, uint8_t prev_sample)
{
int best_error = INT_MAX;
int best_index = -1;
int delta_table_start_index;
int delta_table_end_index;
int sample_signed = U8_TO_S8(sample);
int prev_sample_signed = U8_TO_S8(prev_sample);
// if we're going up (or equal), only choose positive deltas
if (prev_sample_signed <= sample_signed) {
delta_table_start_index = POSITIVE_DELTAS_START;
delta_table_end_index = POSITIVE_DELTAS_END;
} else {
delta_table_start_index = NEGATIVE_DELTAS_START;
delta_table_end_index = NEGATIVE_DELTAS_END;
}
for (int i = delta_table_start_index; i < delta_table_end_index; i++)
{
uint8_t new_sample = prev_sample + gDeltaEncodingTable[i];
int new_sample_signed = U8_TO_S8(new_sample);
int error = ABS(new_sample_signed - sample_signed);
if (error < best_error)
{
best_error = error;
best_index = i;
}
}
return best_index;
}
struct Bytes *delta_compress(struct Bytes *pcm)
{
struct Bytes *delta = malloc(sizeof(struct Bytes));
// estimate the length so we can malloc
int num_blocks = pcm->length / 64;
delta->length = num_blocks * 33;
int extra = pcm->length % 64;
if (extra)
{
delta->length += 1;
extra -= 1;
}
if (extra)
{
delta->length += 1;
extra -= 1;
}
if (extra)
{
delta->length += (extra + 1) / 2;
}
delta->data = malloc(delta->length + 33);
unsigned int i = 0;
unsigned int j = 0;
int k;
uint8_t base;
int delta_index;
while (i < pcm->length)
{
base = pcm->data[i++];
delta->data[j++] = base;
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j++] = delta_index;
for (k = 0; k < 31; k++)
{
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j] = (delta_index << 4);
if (i >= pcm->length)
{
break;
}
delta_index = get_delta_index(pcm->data[i++], base);
base += gDeltaEncodingTable[delta_index];
delta->data[j++] |= delta_index;
}
}
delta->length = j;
return delta;
}
#define STORE_U32_LE(dest, value) \
do { \
*(dest) = (value) & 0xff; \
*((dest) + 1) = ((value) >> 8) & 0xff; \
*((dest) + 2) = ((value) >> 16) & 0xff; \
*((dest) + 3) = ((value) >> 24) & 0xff; \
} while (0)
#define LOAD_U32_LE(var, src) \
do { \
(var) = *(src); \
(var) |= (*((src) + 1) << 8); \
(var) |= (*((src) + 2) << 16); \
(var) |= (*((src) + 3) << 24); \
} while (0)
// Reads an .aif file and produces a .pcm file containing an array of 8-bit samples.
void aif2pcm(const char *aif_filename, const char *pcm_filename, bool compress)
{
struct Bytes *aif = read_bytearray(aif_filename);
AifData aif_data = {0,0,0,0,0,0,0};
read_aif(aif, &aif_data);
int header_size = 0x10;
struct Bytes *pcm;
struct Bytes output = {0,0};
if (compress)
{
struct Bytes *input = malloc(sizeof(struct Bytes));
input->data = aif_data.samples;
input->length = aif_data.real_num_samples;
pcm = delta_compress(input);
free(input);
}
else
{
pcm = malloc(sizeof(struct Bytes));
pcm->data = aif_data.samples;
pcm->length = aif_data.real_num_samples;
}
output.length = header_size + pcm->length;
output.data = malloc(output.length);
uint32_t pitch_adjust = (uint32_t)(aif_data.sample_rate * 1024);
uint32_t loop_offset = (uint32_t)(aif_data.loop_offset);
uint32_t adjusted_num_samples = (uint32_t)(aif_data.num_samples - 1);
uint32_t flags = 0;
if (aif_data.has_loop) flags |= 0x40000000;
if (compress) flags |= 1;
STORE_U32_LE(output.data + 0, flags);
STORE_U32_LE(output.data + 4, pitch_adjust);
STORE_U32_LE(output.data + 8, loop_offset);
STORE_U32_LE(output.data + 12, adjusted_num_samples);
memcpy(&output.data[header_size], pcm->data, pcm->length);
write_bytearray(pcm_filename, &output);
free(aif->data);
free(aif);
free(pcm);
free(output.data);
free(aif_data.samples);
}
// Reads a .pcm file containing an array of 8-bit samples and produces an .aif file.
// See http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf for .aif file specification.
void pcm2aif(const char *pcm_filename, const char *aif_filename, uint32_t base_note)
{
struct Bytes *pcm = read_bytearray(pcm_filename);
AifData *aif_data = malloc(sizeof(AifData));
uint32_t flags;
LOAD_U32_LE(flags, pcm->data + 0);
aif_data->has_loop = flags & 0x40000000;
bool compressed = flags & 1;
uint32_t pitch_adjust;
LOAD_U32_LE(pitch_adjust, pcm->data + 4);
aif_data->sample_rate = pitch_adjust / 1024.0;
LOAD_U32_LE(aif_data->loop_offset, pcm->data + 8);
LOAD_U32_LE(aif_data->num_samples, pcm->data + 12);
aif_data->num_samples += 1;
if (compressed)
{
struct Bytes *delta = pcm;
uint8_t *pcm_data = pcm->data;
delta->length -= 0x10;
delta->data += 0x10;
pcm = delta_decompress(delta, aif_data->num_samples);
free(pcm_data);
free(delta);
}
else
{
pcm->length -= 0x10;
pcm->data += 0x10;
}
aif_data->samples = malloc(pcm->length);
memcpy(aif_data->samples, pcm->data, pcm->length);
struct Bytes *aif = malloc(sizeof(struct Bytes));
aif->length = 54 + 60 + pcm->length;
aif->data = malloc(aif->length);
long pos = 0;
// First, write the FORM header chunk.
// FORM Chunk ckID
aif->data[pos++] = 'F';
aif->data[pos++] = 'O';
aif->data[pos++] = 'R';
aif->data[pos++] = 'M';
// FORM Chunk ckSize
unsigned long form_size = pos;
unsigned long data_size = aif->length - 8;
aif->data[pos++] = ((data_size >> 24) & 0xFF);
aif->data[pos++] = ((data_size >> 16) & 0xFF);
aif->data[pos++] = ((data_size >> 8) & 0xFF);
aif->data[pos++] = (data_size & 0xFF);
// FORM Chunk formType
aif->data[pos++] = 'A';
aif->data[pos++] = 'I';
aif->data[pos++] = 'F';
aif->data[pos++] = 'F';
// Next, write the Common Chunk
// Common Chunk ckID
aif->data[pos++] = 'C';
aif->data[pos++] = 'O';
aif->data[pos++] = 'M';
aif->data[pos++] = 'M';
// Common Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 18;
// Common Chunk numChannels
aif->data[pos++] = 0;
aif->data[pos++] = 1; // 1 channel
// Common Chunk numSampleFrames
aif->data[pos++] = ((aif_data->num_samples >> 24) & 0xFF);
aif->data[pos++] = ((aif_data->num_samples >> 16) & 0xFF);
aif->data[pos++] = ((aif_data->num_samples >> 8) & 0xFF);
aif->data[pos++] = (aif_data->num_samples & 0xFF);
// Common Chunk sampleSize
aif->data[pos++] = 0;
aif->data[pos++] = 8; // 8 bits per sample
// Common Chunk sampleRate
//double sample_rate = pitch_adjust / 1024.0;
uint8_t sample_rate_buffer[10];
ieee754_write_extended(aif_data->sample_rate, sample_rate_buffer);
for (int i = 0; i < 10; i++)
{
aif->data[pos++] = sample_rate_buffer[i];
}
if (aif_data->has_loop)
{
// Marker Chunk ckID
aif->data[pos++] = 'M';
aif->data[pos++] = 'A';
aif->data[pos++] = 'R';
aif->data[pos++] = 'K';
// Marker Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 12 + (aif_data->has_loop ? 12 : 0);
// Marker Chunk numMarkers
aif->data[pos++] = 0;
aif->data[pos++] = (aif_data->has_loop ? 2 : 1);
// Marker loop start
aif->data[pos++] = 0;
aif->data[pos++] = 1; // id = 1
long loop_start = aif_data->loop_offset;
aif->data[pos++] = ((loop_start >> 24) & 0xFF);
aif->data[pos++] = ((loop_start >> 16) & 0xFF);
aif->data[pos++] = ((loop_start >> 8) & 0xFF);
aif->data[pos++] = (loop_start & 0xFF); // position
aif->data[pos++] = 5; // pascal-style string length
aif->data[pos++] = 'S';
aif->data[pos++] = 'T';
aif->data[pos++] = 'A';
aif->data[pos++] = 'R';
aif->data[pos++] = 'T'; // markerName
// Marker loop end
aif->data[pos++] = 0;
aif->data[pos++] = (aif_data->has_loop ? 2 : 1); // id = 2
long loop_end = aif_data->num_samples;
aif->data[pos++] = ((loop_end >> 24) & 0xFF);
aif->data[pos++] = ((loop_end >> 16) & 0xFF);
aif->data[pos++] = ((loop_end >> 8) & 0xFF);
aif->data[pos++] = (loop_end & 0xFF); // position
aif->data[pos++] = 3; // pascal-style string length
aif->data[pos++] = 'E';
aif->data[pos++] = 'N';
aif->data[pos++] = 'D';
}
// Instrument Chunk ckID
aif->data[pos++] = 'I';
aif->data[pos++] = 'N';
aif->data[pos++] = 'S';
aif->data[pos++] = 'T';
// Instrument Chunk ckSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 20;
aif->data[pos++] = base_note; // baseNote
aif->data[pos++] = 0; // detune
aif->data[pos++] = 0; // lowNote
aif->data[pos++] = 127; // highNote
aif->data[pos++] = 1; // lowVelocity
aif->data[pos++] = 127; // highVelocity
aif->data[pos++] = 0; // gain (hi)
aif->data[pos++] = 0; // gain (lo)
// Instrument Chunk sustainLoop
aif->data[pos++] = 0;
aif->data[pos++] = 1; // playMode = ForwardLooping
aif->data[pos++] = 0;
aif->data[pos++] = 1; // beginLoop marker id
aif->data[pos++] = 0;
aif->data[pos++] = 2; // endLoop marker id
// Instrument Chunk releaseLoop
aif->data[pos++] = 0;
aif->data[pos++] = 1; // playMode = ForwardLooping
aif->data[pos++] = 0;
aif->data[pos++] = 1; // beginLoop marker id
aif->data[pos++] = 0;
aif->data[pos++] = 2; // endLoop marker id
// Finally, write the Sound Data Chunk
// Sound Data Chunk ckID
aif->data[pos++] = 'S';
aif->data[pos++] = 'S';
aif->data[pos++] = 'N';
aif->data[pos++] = 'D';
// Sound Data Chunk ckSize
unsigned long sound_data_size = pcm->length + 8;
aif->data[pos++] = ((sound_data_size >> 24) & 0xFF);
aif->data[pos++] = ((sound_data_size >> 16) & 0xFF);
aif->data[pos++] = ((sound_data_size >> 8) & 0xFF);
aif->data[pos++] = (sound_data_size & 0xFF);
// Sound Data Chunk offset
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
// Sound Data Chunk blockSize
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
aif->data[pos++] = 0;
// Sound Data Chunk soundData
for (unsigned int i = 0; i < aif_data->loop_offset; i++)
{
aif->data[pos++] = aif_data->samples[i];
}
int j = 0;
for (unsigned int i = aif_data->loop_offset; i < pcm->length; i++)
{
int pcm_index = aif_data->loop_offset + (j++ % (pcm->length - aif_data->loop_offset));
aif->data[pos++] = aif_data->samples[pcm_index];
}
aif->length = pos;
// Go back and rewrite ckSize
data_size = aif->length - 8;
aif->data[form_size + 0] = ((data_size >> 24) & 0xFF);
aif->data[form_size + 1] = ((data_size >> 16) & 0xFF);
aif->data[form_size + 2] = ((data_size >> 8) & 0xFF);
aif->data[form_size + 3] = (data_size & 0xFF);
write_bytearray(aif_filename, aif);
free(aif->data);
free(aif);
}
void usage(void)
{
fprintf(stderr, "Usage: aif2pcm bin_file [aif_file]\n");
fprintf(stderr, " aif2pcm aif_file [bin_file] [--compress]\n");
}
int main(int argc, char **argv)
{
if (argc < 2)
{
usage();
exit(1);
}
char *input_file = argv[1];
char *extension = get_file_extension(input_file);
char *output_file;
bool compressed = false;
if (argc > 3)
{
for (int i = 3; i < argc; i++)
{
if (strcmp(argv[i], "--compress") == 0)
{
compressed = true;
}
}
}
if (strcmp(extension, "aif") == 0 || strcmp(extension, "aiff") == 0)
{
if (argc >= 3)
{
output_file = argv[2];
aif2pcm(input_file, output_file, compressed);
}
else
{
output_file = new_file_extension(input_file, "bin");
aif2pcm(input_file, output_file, compressed);
free(output_file);
}
}
else if (strcmp(extension, "bin") == 0)
{
if (argc >= 3)
{
output_file = argv[2];
pcm2aif(input_file, output_file, 60);
}
else
{
output_file = new_file_extension(input_file, "aif");
pcm2aif(input_file, output_file, 60);
free(output_file);
}
}
else
{
FATAL_ERROR("Input file must be .aif or .bin: '%s'\n", input_file);
}
return 0;
}

1
tools/bin2c/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin2c

19
tools/bin2c/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

22
tools/bin2c/Makefile Normal file
View File

@ -0,0 +1,22 @@
CC ?= gcc
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2
.PHONY: all clean
SRCS = bin2c.c
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: bin2c$(EXE)
@:
bin2c$(EXE): $(SRCS)
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) bin2c bin2c.exe

201
tools/bin2c/bin2c.c Normal file
View File

@ -0,0 +1,201 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
int ExtractData(unsigned char *buffer, int offset, int size)
{
switch (size)
{
case 1:
return buffer[offset];
case 2:
return (buffer[offset + 1] << 8)
| buffer[offset];
case 4:
return (buffer[offset + 3] << 24)
| (buffer[offset + 2] << 16)
| (buffer[offset + 1] << 8)
| buffer[offset];
default:
FATAL_ERROR("Invalid size passed to ExtractData.\n");
}
}
int main(int argc, char **argv)
{
if (argc < 3)
FATAL_ERROR("Usage: bin2c INPUT_FILE VAR_NAME [OPTIONS...]\n");
int fileSize;
unsigned char *buffer = ReadWholeFile(argv[1], &fileSize);
char *var_name = argv[2];
int col = 1;
int pad = 0;
int size = 1;
bool isSigned = false;
bool isStatic = false;
bool isDecimal = false;
for (int i = 3; i < argc; i++)
{
if (!strcmp(argv[i], "-col"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-col'.\n");
col = atoi(argv[i]);
}
else if (!strcmp(argv[i], "-pad"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-pad'.\n");
pad = atoi(argv[i]);
}
else if (!strcmp(argv[i], "-size"))
{
i++;
if (i >= argc)
FATAL_ERROR("Missing argument after '-size'.\n");
size = atoi(argv[i]);
if (size != 1 && size != 2 && size != 4)
FATAL_ERROR("Size must be 1, 2, or 4.\n");
}
else if (!strcmp(argv[i], "-signed"))
{
isSigned = true;
isDecimal = true;
}
else if (!strcmp(argv[i], "-static"))
{
isStatic = true;
}
else if (!strcmp(argv[i], "-decimal"))
{
isDecimal = true;
}
else
{
FATAL_ERROR("Unrecognized option '%s'.\n", argv[i]);
}
}
if ((fileSize & (size - 1)) != 0)
FATAL_ERROR("Size %d doesn't evenly divide file size %d.\n", size, fileSize);
printf("// Generated file. Do not edit.\n\n");
if (isStatic)
printf("static ");
printf("const ");
if (isSigned)
printf("s%d ", 8 * size);
else
printf("u%d ", 8 * size);
printf("%s[] =\n{", var_name);
int count = fileSize / size;
int offset = 0;
for (int i = 0; i < count; i++)
{
if (i % col == 0)
printf("\n ");
int data = ExtractData(buffer, offset, size);
offset += size;
if (isDecimal)
{
if (isSigned)
printf("%*d, ", pad, data);
else
printf("%*uu, ", pad, data);
}
else
{
printf("%#*xu, ", pad, data);
}
}
printf("\n};\n");
return 0;
}

2
tools/gbafix/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
gbafix
README

674
tools/gbafix/COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

19
tools/gbafix/Makefile Normal file
View File

@ -0,0 +1,19 @@
CC ?= gcc
.PHONY: all clean
SRCS = gbafix.c
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: gbafix$(EXE)
@:
gbafix$(EXE): $(SRCS)
$(CC) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) gbafix gbafix.exe

3147
tools/gbafix/elf.h Normal file
View File

@ -0,0 +1,3147 @@
/*
From musl include/elf.h
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _ELF_H
#define _ELF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf32_Xword;
typedef int64_t Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;
#define EI_NIDENT (16)
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
#define EI_MAG0 0
#define ELFMAG0 0x7f
#define EI_MAG1 1
#define ELFMAG1 'E'
#define EI_MAG2 2
#define ELFMAG2 'L'
#define EI_MAG3 3
#define ELFMAG3 'F'
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
#define ELFCLASSNUM 3
#define EI_DATA 5
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
#define ELFDATANUM 3
#define EI_VERSION 6
#define EI_OSABI 7
#define ELFOSABI_NONE 0
#define ELFOSABI_SYSV 0
#define ELFOSABI_HPUX 1
#define ELFOSABI_NETBSD 2
#define ELFOSABI_LINUX 3
#define ELFOSABI_GNU 3
#define ELFOSABI_SOLARIS 6
#define ELFOSABI_AIX 7
#define ELFOSABI_IRIX 8
#define ELFOSABI_FREEBSD 9
#define ELFOSABI_TRU64 10
#define ELFOSABI_MODESTO 11
#define ELFOSABI_OPENBSD 12
#define ELFOSABI_ARM 97
#define ELFOSABI_STANDALONE 255
#define EI_ABIVERSION 8
#define EI_PAD 9
#define ET_NONE 0
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
#define ET_NUM 5
#define ET_LOOS 0xfe00
#define ET_HIOS 0xfeff
#define ET_LOPROC 0xff00
#define ET_HIPROC 0xffff
#define EM_NONE 0
#define EM_M32 1
#define EM_SPARC 2
#define EM_386 3
#define EM_68K 4
#define EM_88K 5
#define EM_860 7
#define EM_MIPS 8
#define EM_S370 9
#define EM_MIPS_RS3_LE 10
#define EM_PARISC 15
#define EM_VPP500 17
#define EM_SPARC32PLUS 18
#define EM_960 19
#define EM_PPC 20
#define EM_PPC64 21
#define EM_S390 22
#define EM_V800 36
#define EM_FR20 37
#define EM_RH32 38
#define EM_RCE 39
#define EM_ARM 40
#define EM_FAKE_ALPHA 41
#define EM_SH 42
#define EM_SPARCV9 43
#define EM_TRICORE 44
#define EM_ARC 45
#define EM_H8_300 46
#define EM_H8_300H 47
#define EM_H8S 48
#define EM_H8_500 49
#define EM_IA_64 50
#define EM_MIPS_X 51
#define EM_COLDFIRE 52
#define EM_68HC12 53
#define EM_MMA 54
#define EM_PCP 55
#define EM_NCPU 56
#define EM_NDR1 57
#define EM_STARCORE 58
#define EM_ME16 59
#define EM_ST100 60
#define EM_TINYJ 61
#define EM_X86_64 62
#define EM_PDSP 63
#define EM_FX66 66
#define EM_ST9PLUS 67
#define EM_ST7 68
#define EM_68HC16 69
#define EM_68HC11 70
#define EM_68HC08 71
#define EM_68HC05 72
#define EM_SVX 73
#define EM_ST19 74
#define EM_VAX 75
#define EM_CRIS 76
#define EM_JAVELIN 77
#define EM_FIREPATH 78
#define EM_ZSP 79
#define EM_MMIX 80
#define EM_HUANY 81
#define EM_PRISM 82
#define EM_AVR 83
#define EM_FR30 84
#define EM_D10V 85
#define EM_D30V 86
#define EM_V850 87
#define EM_M32R 88
#define EM_MN10300 89
#define EM_MN10200 90
#define EM_PJ 91
#define EM_OR1K 92
#define EM_OPENRISC 92
#define EM_ARC_A5 93
#define EM_ARC_COMPACT 93
#define EM_XTENSA 94
#define EM_VIDEOCORE 95
#define EM_TMM_GPP 96
#define EM_NS32K 97
#define EM_TPC 98
#define EM_SNP1K 99
#define EM_ST200 100
#define EM_IP2K 101
#define EM_MAX 102
#define EM_CR 103
#define EM_F2MC16 104
#define EM_MSP430 105
#define EM_BLACKFIN 106
#define EM_SE_C33 107
#define EM_SEP 108
#define EM_ARCA 109
#define EM_UNICORE 110
#define EM_EXCESS 111
#define EM_DXP 112
#define EM_ALTERA_NIOS2 113
#define EM_CRX 114
#define EM_XGATE 115
#define EM_C166 116
#define EM_M16C 117
#define EM_DSPIC30F 118
#define EM_CE 119
#define EM_M32C 120
#define EM_TSK3000 131
#define EM_RS08 132
#define EM_SHARC 133
#define EM_ECOG2 134
#define EM_SCORE7 135
#define EM_DSP24 136
#define EM_VIDEOCORE3 137
#define EM_LATTICEMICO32 138
#define EM_SE_C17 139
#define EM_TI_C6000 140
#define EM_TI_C2000 141
#define EM_TI_C5500 142
#define EM_TI_ARP32 143
#define EM_TI_PRU 144
#define EM_MMDSP_PLUS 160
#define EM_CYPRESS_M8C 161
#define EM_R32C 162
#define EM_TRIMEDIA 163
#define EM_QDSP6 164
#define EM_8051 165
#define EM_STXP7X 166
#define EM_NDS32 167
#define EM_ECOG1X 168
#define EM_MAXQ30 169
#define EM_XIMO16 170
#define EM_MANIK 171
#define EM_CRAYNV2 172
#define EM_RX 173
#define EM_METAG 174
#define EM_MCST_ELBRUS 175
#define EM_ECOG16 176
#define EM_CR16 177
#define EM_ETPU 178
#define EM_SLE9X 179
#define EM_L10M 180
#define EM_K10M 181
#define EM_AARCH64 183
#define EM_AVR32 185
#define EM_STM8 186
#define EM_TILE64 187
#define EM_TILEPRO 188
#define EM_MICROBLAZE 189
#define EM_CUDA 190
#define EM_TILEGX 191
#define EM_CLOUDSHIELD 192
#define EM_COREA_1ST 193
#define EM_COREA_2ND 194
#define EM_ARC_COMPACT2 195
#define EM_OPEN8 196
#define EM_RL78 197
#define EM_VIDEOCORE5 198
#define EM_78KOR 199
#define EM_56800EX 200
#define EM_BA1 201
#define EM_BA2 202
#define EM_XCORE 203
#define EM_MCHP_PIC 204
#define EM_KM32 210
#define EM_KMX32 211
#define EM_EMX16 212
#define EM_EMX8 213
#define EM_KVARC 214
#define EM_CDP 215
#define EM_COGE 216
#define EM_COOL 217
#define EM_NORC 218
#define EM_CSR_KALIMBA 219
#define EM_Z80 220
#define EM_VISIUM 221
#define EM_FT32 222
#define EM_MOXIE 223
#define EM_AMDGPU 224
#define EM_RISCV 243
#define EM_BPF 247
#define EM_NUM 248
#define EM_ALPHA 0x9026
#define EV_NONE 0
#define EV_CURRENT 1
#define EV_NUM 2
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct {
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;
#define SHN_UNDEF 0
#define SHN_LORESERVE 0xff00
#define SHN_LOPROC 0xff00
#define SHN_BEFORE 0xff00
#define SHN_AFTER 0xff01
#define SHN_HIPROC 0xff1f
#define SHN_LOOS 0xff20
#define SHN_HIOS 0xff3f
#define SHN_ABS 0xfff1
#define SHN_COMMON 0xfff2
#define SHN_XINDEX 0xffff
#define SHN_HIRESERVE 0xffff
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_INIT_ARRAY 14
#define SHT_FINI_ARRAY 15
#define SHT_PREINIT_ARRAY 16
#define SHT_GROUP 17
#define SHT_SYMTAB_SHNDX 18
#define SHT_NUM 19
#define SHT_LOOS 0x60000000
#define SHT_GNU_ATTRIBUTES 0x6ffffff5
#define SHT_GNU_HASH 0x6ffffff6
#define SHT_GNU_LIBLIST 0x6ffffff7
#define SHT_CHECKSUM 0x6ffffff8
#define SHT_LOSUNW 0x6ffffffa
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd
#define SHT_GNU_verneed 0x6ffffffe
#define SHT_GNU_versym 0x6fffffff
#define SHT_HISUNW 0x6fffffff
#define SHT_HIOS 0x6fffffff
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0x8fffffff
#define SHF_WRITE (1 << 0)
#define SHF_ALLOC (1 << 1)
#define SHF_EXECINSTR (1 << 2)
#define SHF_MERGE (1 << 4)
#define SHF_STRINGS (1 << 5)
#define SHF_INFO_LINK (1 << 6)
#define SHF_LINK_ORDER (1 << 7)
#define SHF_OS_NONCONFORMING (1 << 8)
#define SHF_GROUP (1 << 9)
#define SHF_TLS (1 << 10)
#define SHF_COMPRESSED (1 << 11)
#define SHF_MASKOS 0x0ff00000
#define SHF_MASKPROC 0xf0000000
#define SHF_ORDERED (1 << 30)
#define SHF_EXCLUDE (1U << 31)
typedef struct {
Elf32_Word ch_type;
Elf32_Word ch_size;
Elf32_Word ch_addralign;
} Elf32_Chdr;
typedef struct {
Elf64_Word ch_type;
Elf64_Word ch_reserved;
Elf64_Xword ch_size;
Elf64_Xword ch_addralign;
} Elf64_Chdr;
#define ELFCOMPRESS_ZLIB 1
#define ELFCOMPRESS_LOOS 0x60000000
#define ELFCOMPRESS_HIOS 0x6fffffff
#define ELFCOMPRESS_LOPROC 0x70000000
#define ELFCOMPRESS_HIPROC 0x7fffffff
#define GRP_COMDAT 0x1
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Section st_shndx;
} Elf32_Sym;
typedef struct {
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Section st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
typedef struct {
Elf32_Half si_boundto;
Elf32_Half si_flags;
} Elf32_Syminfo;
typedef struct {
Elf64_Half si_boundto;
Elf64_Half si_flags;
} Elf64_Syminfo;
#define SYMINFO_BT_SELF 0xffff
#define SYMINFO_BT_PARENT 0xfffe
#define SYMINFO_BT_LOWRESERVE 0xff00
#define SYMINFO_FLG_DIRECT 0x0001
#define SYMINFO_FLG_PASSTHRU 0x0002
#define SYMINFO_FLG_COPY 0x0004
#define SYMINFO_FLG_LAZYLOAD 0x0008
#define SYMINFO_NONE 0
#define SYMINFO_CURRENT 1
#define SYMINFO_NUM 2
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val) ((val) & 0xf)
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
#define STB_LOCAL 0
#define STB_GLOBAL 1
#define STB_WEAK 2
#define STB_NUM 3
#define STB_LOOS 10
#define STB_GNU_UNIQUE 10
#define STB_HIOS 12
#define STB_LOPROC 13
#define STB_HIPROC 15
#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_COMMON 5
#define STT_TLS 6
#define STT_NUM 7
#define STT_LOOS 10
#define STT_GNU_IFUNC 10
#define STT_HIOS 12
#define STT_LOPROC 13
#define STT_HIPROC 15
#define STN_UNDEF 0
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
#define STV_DEFAULT 0
#define STV_INTERNAL 1
#define STV_HIDDEN 2
#define STV_PROTECTED 3
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
} Elf64_Rel;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend;
} Elf64_Rela;
#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
typedef struct {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
} Elf64_Phdr;
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_TLS 7
#define PT_NUM 8
#define PT_LOOS 0x60000000
#define PT_GNU_EH_FRAME 0x6474e550
#define PT_GNU_STACK 0x6474e551
#define PT_GNU_RELRO 0x6474e552
#define PT_LOSUNW 0x6ffffffa
#define PT_SUNWBSS 0x6ffffffa
#define PT_SUNWSTACK 0x6ffffffb
#define PT_HISUNW 0x6fffffff
#define PT_HIOS 0x6fffffff
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
#define PN_XNUM 0xffff
#define PF_X (1 << 0)
#define PF_W (1 << 1)
#define PF_R (1 << 2)
#define PF_MASKOS 0x0ff00000
#define PF_MASKPROC 0xf0000000
#define NT_PRSTATUS 1
#define NT_FPREGSET 2
#define NT_PRPSINFO 3
#define NT_PRXREG 4
#define NT_TASKSTRUCT 4
#define NT_PLATFORM 5
#define NT_AUXV 6
#define NT_GWINDOWS 7
#define NT_ASRS 8
#define NT_PSTATUS 10
#define NT_PSINFO 13
#define NT_PRCRED 14
#define NT_UTSNAME 15
#define NT_LWPSTATUS 16
#define NT_LWPSINFO 17
#define NT_PRFPXREG 20
#define NT_SIGINFO 0x53494749
#define NT_FILE 0x46494c45
#define NT_PRXFPREG 0x46e62b7f
#define NT_PPC_VMX 0x100
#define NT_PPC_SPE 0x101
#define NT_PPC_VSX 0x102
#define NT_386_TLS 0x200
#define NT_386_IOPERM 0x201
#define NT_X86_XSTATE 0x202
#define NT_S390_HIGH_GPRS 0x300
#define NT_S390_TIMER 0x301
#define NT_S390_TODCMP 0x302
#define NT_S390_TODPREG 0x303
#define NT_S390_CTRS 0x304
#define NT_S390_PREFIX 0x305
#define NT_S390_LAST_BREAK 0x306
#define NT_S390_SYSTEM_CALL 0x307
#define NT_S390_TDB 0x308
#define NT_ARM_VFP 0x400
#define NT_ARM_TLS 0x401
#define NT_ARM_HW_BREAK 0x402
#define NT_ARM_HW_WATCH 0x403
#define NT_ARM_SYSTEM_CALL 0x404
#define NT_ARM_SVE 0x405
#define NT_METAG_CBUF 0x500
#define NT_METAG_RPIPE 0x501
#define NT_METAG_TLS 0x502
#define NT_VERSION 1
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
typedef struct {
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
#define DT_NULL 0
#define DT_NEEDED 1
#define DT_PLTRELSZ 2
#define DT_PLTGOT 3
#define DT_HASH 4
#define DT_STRTAB 5
#define DT_SYMTAB 6
#define DT_RELA 7
#define DT_RELASZ 8
#define DT_RELAENT 9
#define DT_STRSZ 10
#define DT_SYMENT 11
#define DT_INIT 12
#define DT_FINI 13
#define DT_SONAME 14
#define DT_RPATH 15
#define DT_SYMBOLIC 16
#define DT_REL 17
#define DT_RELSZ 18
#define DT_RELENT 19
#define DT_PLTREL 20
#define DT_DEBUG 21
#define DT_TEXTREL 22
#define DT_JMPREL 23
#define DT_BIND_NOW 24
#define DT_INIT_ARRAY 25
#define DT_FINI_ARRAY 26
#define DT_INIT_ARRAYSZ 27
#define DT_FINI_ARRAYSZ 28
#define DT_RUNPATH 29
#define DT_FLAGS 30
#define DT_ENCODING 32
#define DT_PREINIT_ARRAY 32
#define DT_PREINIT_ARRAYSZ 33
#define DT_NUM 34
#define DT_LOOS 0x6000000d
#define DT_HIOS 0x6ffff000
#define DT_LOPROC 0x70000000
#define DT_HIPROC 0x7fffffff
#define DT_PROCNUM DT_MIPS_NUM
#define DT_VALRNGLO 0x6ffffd00
#define DT_GNU_PRELINKED 0x6ffffdf5
#define DT_GNU_CONFLICTSZ 0x6ffffdf6
#define DT_GNU_LIBLISTSZ 0x6ffffdf7
#define DT_CHECKSUM 0x6ffffdf8
#define DT_PLTPADSZ 0x6ffffdf9
#define DT_MOVEENT 0x6ffffdfa
#define DT_MOVESZ 0x6ffffdfb
#define DT_FEATURE_1 0x6ffffdfc
#define DT_POSFLAG_1 0x6ffffdfd
#define DT_SYMINSZ 0x6ffffdfe
#define DT_SYMINENT 0x6ffffdff
#define DT_VALRNGHI 0x6ffffdff
#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag))
#define DT_VALNUM 12
#define DT_ADDRRNGLO 0x6ffffe00
#define DT_GNU_HASH 0x6ffffef5
#define DT_TLSDESC_PLT 0x6ffffef6
#define DT_TLSDESC_GOT 0x6ffffef7
#define DT_GNU_CONFLICT 0x6ffffef8
#define DT_GNU_LIBLIST 0x6ffffef9
#define DT_CONFIG 0x6ffffefa
#define DT_DEPAUDIT 0x6ffffefb
#define DT_AUDIT 0x6ffffefc
#define DT_PLTPAD 0x6ffffefd
#define DT_MOVETAB 0x6ffffefe
#define DT_SYMINFO 0x6ffffeff
#define DT_ADDRRNGHI 0x6ffffeff
#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag))
#define DT_ADDRNUM 11
#define DT_VERSYM 0x6ffffff0
#define DT_RELACOUNT 0x6ffffff9
#define DT_RELCOUNT 0x6ffffffa
#define DT_FLAGS_1 0x6ffffffb
#define DT_VERDEF 0x6ffffffc
#define DT_VERDEFNUM 0x6ffffffd
#define DT_VERNEED 0x6ffffffe
#define DT_VERNEEDNUM 0x6fffffff
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag))
#define DT_VERSIONTAGNUM 16
#define DT_AUXILIARY 0x7ffffffd
#define DT_FILTER 0x7fffffff
#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
#define DT_EXTRANUM 3
#define DF_ORIGIN 0x00000001
#define DF_SYMBOLIC 0x00000002
#define DF_TEXTREL 0x00000004
#define DF_BIND_NOW 0x00000008
#define DF_STATIC_TLS 0x00000010
#define DF_1_NOW 0x00000001
#define DF_1_GLOBAL 0x00000002
#define DF_1_GROUP 0x00000004
#define DF_1_NODELETE 0x00000008
#define DF_1_LOADFLTR 0x00000010
#define DF_1_INITFIRST 0x00000020
#define DF_1_NOOPEN 0x00000040
#define DF_1_ORIGIN 0x00000080
#define DF_1_DIRECT 0x00000100
#define DF_1_TRANS 0x00000200
#define DF_1_INTERPOSE 0x00000400
#define DF_1_NODEFLIB 0x00000800
#define DF_1_NODUMP 0x00001000
#define DF_1_CONFALT 0x00002000
#define DF_1_ENDFILTEE 0x00004000
#define DF_1_DISPRELDNE 0x00008000
#define DF_1_DISPRELPND 0x00010000
#define DF_1_NODIRECT 0x00020000
#define DF_1_IGNMULDEF 0x00040000
#define DF_1_NOKSYMS 0x00080000
#define DF_1_NOHDR 0x00100000
#define DF_1_EDITED 0x00200000
#define DF_1_NORELOC 0x00400000
#define DF_1_SYMINTPOSE 0x00800000
#define DF_1_GLOBAUDIT 0x01000000
#define DF_1_SINGLETON 0x02000000
#define DTF_1_PARINIT 0x00000001
#define DTF_1_CONFEXP 0x00000002
#define DF_P1_LAZYLOAD 0x00000001
#define DF_P1_GROUPPERM 0x00000002
typedef struct {
Elf32_Half vd_version;
Elf32_Half vd_flags;
Elf32_Half vd_ndx;
Elf32_Half vd_cnt;
Elf32_Word vd_hash;
Elf32_Word vd_aux;
Elf32_Word vd_next;
} Elf32_Verdef;
typedef struct {
Elf64_Half vd_version;
Elf64_Half vd_flags;
Elf64_Half vd_ndx;
Elf64_Half vd_cnt;
Elf64_Word vd_hash;
Elf64_Word vd_aux;
Elf64_Word vd_next;
} Elf64_Verdef;
#define VER_DEF_NONE 0
#define VER_DEF_CURRENT 1
#define VER_DEF_NUM 2
#define VER_FLG_BASE 0x1
#define VER_FLG_WEAK 0x2
#define VER_NDX_LOCAL 0
#define VER_NDX_GLOBAL 1
#define VER_NDX_LORESERVE 0xff00
#define VER_NDX_ELIMINATE 0xff01
typedef struct {
Elf32_Word vda_name;
Elf32_Word vda_next;
} Elf32_Verdaux;
typedef struct {
Elf64_Word vda_name;
Elf64_Word vda_next;
} Elf64_Verdaux;
typedef struct {
Elf32_Half vn_version;
Elf32_Half vn_cnt;
Elf32_Word vn_file;
Elf32_Word vn_aux;
Elf32_Word vn_next;
} Elf32_Verneed;
typedef struct {
Elf64_Half vn_version;
Elf64_Half vn_cnt;
Elf64_Word vn_file;
Elf64_Word vn_aux;
Elf64_Word vn_next;
} Elf64_Verneed;
#define VER_NEED_NONE 0
#define VER_NEED_CURRENT 1
#define VER_NEED_NUM 2
typedef struct {
Elf32_Word vna_hash;
Elf32_Half vna_flags;
Elf32_Half vna_other;
Elf32_Word vna_name;
Elf32_Word vna_next;
} Elf32_Vernaux;
typedef struct {
Elf64_Word vna_hash;
Elf64_Half vna_flags;
Elf64_Half vna_other;
Elf64_Word vna_name;
Elf64_Word vna_next;
} Elf64_Vernaux;
#define VER_FLG_WEAK 0x2
typedef struct {
uint32_t a_type;
union {
uint32_t a_val;
} a_un;
} Elf32_auxv_t;
typedef struct {
uint64_t a_type;
union {
uint64_t a_val;
} a_un;
} Elf64_auxv_t;
#define AT_NULL 0
#define AT_IGNORE 1
#define AT_EXECFD 2
#define AT_PHDR 3
#define AT_PHENT 4
#define AT_PHNUM 5
#define AT_PAGESZ 6
#define AT_BASE 7
#define AT_FLAGS 8
#define AT_ENTRY 9
#define AT_NOTELF 10
#define AT_UID 11
#define AT_EUID 12
#define AT_GID 13
#define AT_EGID 14
#define AT_CLKTCK 17
#define AT_PLATFORM 15
#define AT_HWCAP 16
#define AT_FPUCW 18
#define AT_DCACHEBSIZE 19
#define AT_ICACHEBSIZE 20
#define AT_UCACHEBSIZE 21
#define AT_IGNOREPPC 22
#define AT_SECURE 23
#define AT_BASE_PLATFORM 24
#define AT_RANDOM 25
#define AT_HWCAP2 26
#define AT_EXECFN 31
#define AT_SYSINFO 32
#define AT_SYSINFO_EHDR 33
#define AT_L1I_CACHESHAPE 34
#define AT_L1D_CACHESHAPE 35
#define AT_L2_CACHESHAPE 36
#define AT_L3_CACHESHAPE 37
typedef struct {
Elf32_Word n_namesz;
Elf32_Word n_descsz;
Elf32_Word n_type;
} Elf32_Nhdr;
typedef struct {
Elf64_Word n_namesz;
Elf64_Word n_descsz;
Elf64_Word n_type;
} Elf64_Nhdr;
#define ELF_NOTE_SOLARIS "SUNW Solaris"
#define ELF_NOTE_GNU "GNU"
#define ELF_NOTE_PAGESIZE_HINT 1
#define NT_GNU_ABI_TAG 1
#define ELF_NOTE_ABI NT_GNU_ABI_TAG
#define ELF_NOTE_OS_LINUX 0
#define ELF_NOTE_OS_GNU 1
#define ELF_NOTE_OS_SOLARIS2 2
#define ELF_NOTE_OS_FREEBSD 3
#define NT_GNU_BUILD_ID 3
#define NT_GNU_GOLD_VERSION 4
typedef struct {
Elf32_Xword m_value;
Elf32_Word m_info;
Elf32_Word m_poffset;
Elf32_Half m_repeat;
Elf32_Half m_stride;
} Elf32_Move;
typedef struct {
Elf64_Xword m_value;
Elf64_Xword m_info;
Elf64_Xword m_poffset;
Elf64_Half m_repeat;
Elf64_Half m_stride;
} Elf64_Move;
#define ELF32_M_SYM(info) ((info) >> 8)
#define ELF32_M_SIZE(info) ((unsigned char) (info))
#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
#define ELF64_M_SYM(info) ELF32_M_SYM (info)
#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
#define EF_CPU32 0x00810000
#define R_68K_NONE 0
#define R_68K_32 1
#define R_68K_16 2
#define R_68K_8 3
#define R_68K_PC32 4
#define R_68K_PC16 5
#define R_68K_PC8 6
#define R_68K_GOT32 7
#define R_68K_GOT16 8
#define R_68K_GOT8 9
#define R_68K_GOT32O 10
#define R_68K_GOT16O 11
#define R_68K_GOT8O 12
#define R_68K_PLT32 13
#define R_68K_PLT16 14
#define R_68K_PLT8 15
#define R_68K_PLT32O 16
#define R_68K_PLT16O 17
#define R_68K_PLT8O 18
#define R_68K_COPY 19
#define R_68K_GLOB_DAT 20
#define R_68K_JMP_SLOT 21
#define R_68K_RELATIVE 22
#define R_68K_NUM 23
#define R_386_NONE 0
#define R_386_32 1
#define R_386_PC32 2
#define R_386_GOT32 3
#define R_386_PLT32 4
#define R_386_COPY 5
#define R_386_GLOB_DAT 6
#define R_386_JMP_SLOT 7
#define R_386_RELATIVE 8
#define R_386_GOTOFF 9
#define R_386_GOTPC 10
#define R_386_32PLT 11
#define R_386_TLS_TPOFF 14
#define R_386_TLS_IE 15
#define R_386_TLS_GOTIE 16
#define R_386_TLS_LE 17
#define R_386_TLS_GD 18
#define R_386_TLS_LDM 19
#define R_386_16 20
#define R_386_PC16 21
#define R_386_8 22
#define R_386_PC8 23
#define R_386_TLS_GD_32 24
#define R_386_TLS_GD_PUSH 25
#define R_386_TLS_GD_CALL 26
#define R_386_TLS_GD_POP 27
#define R_386_TLS_LDM_32 28
#define R_386_TLS_LDM_PUSH 29
#define R_386_TLS_LDM_CALL 30
#define R_386_TLS_LDM_POP 31
#define R_386_TLS_LDO_32 32
#define R_386_TLS_IE_32 33
#define R_386_TLS_LE_32 34
#define R_386_TLS_DTPMOD32 35
#define R_386_TLS_DTPOFF32 36
#define R_386_TLS_TPOFF32 37
#define R_386_SIZE32 38
#define R_386_TLS_GOTDESC 39
#define R_386_TLS_DESC_CALL 40
#define R_386_TLS_DESC 41
#define R_386_IRELATIVE 42
#define R_386_GOT32X 43
#define R_386_NUM 44
#define STT_SPARC_REGISTER 13
#define EF_SPARCV9_MM 3
#define EF_SPARCV9_TSO 0
#define EF_SPARCV9_PSO 1
#define EF_SPARCV9_RMO 2
#define EF_SPARC_LEDATA 0x800000
#define EF_SPARC_EXT_MASK 0xFFFF00
#define EF_SPARC_32PLUS 0x000100
#define EF_SPARC_SUN_US1 0x000200
#define EF_SPARC_HAL_R1 0x000400
#define EF_SPARC_SUN_US3 0x000800
#define R_SPARC_NONE 0
#define R_SPARC_8 1
#define R_SPARC_16 2
#define R_SPARC_32 3
#define R_SPARC_DISP8 4
#define R_SPARC_DISP16 5
#define R_SPARC_DISP32 6
#define R_SPARC_WDISP30 7
#define R_SPARC_WDISP22 8
#define R_SPARC_HI22 9
#define R_SPARC_22 10
#define R_SPARC_13 11
#define R_SPARC_LO10 12
#define R_SPARC_GOT10 13
#define R_SPARC_GOT13 14
#define R_SPARC_GOT22 15
#define R_SPARC_PC10 16
#define R_SPARC_PC22 17
#define R_SPARC_WPLT30 18
#define R_SPARC_COPY 19
#define R_SPARC_GLOB_DAT 20
#define R_SPARC_JMP_SLOT 21
#define R_SPARC_RELATIVE 22
#define R_SPARC_UA32 23
#define R_SPARC_PLT32 24
#define R_SPARC_HIPLT22 25
#define R_SPARC_LOPLT10 26
#define R_SPARC_PCPLT32 27
#define R_SPARC_PCPLT22 28
#define R_SPARC_PCPLT10 29
#define R_SPARC_10 30
#define R_SPARC_11 31
#define R_SPARC_64 32
#define R_SPARC_OLO10 33
#define R_SPARC_HH22 34
#define R_SPARC_HM10 35
#define R_SPARC_LM22 36
#define R_SPARC_PC_HH22 37
#define R_SPARC_PC_HM10 38
#define R_SPARC_PC_LM22 39
#define R_SPARC_WDISP16 40
#define R_SPARC_WDISP19 41
#define R_SPARC_GLOB_JMP 42
#define R_SPARC_7 43
#define R_SPARC_5 44
#define R_SPARC_6 45
#define R_SPARC_DISP64 46
#define R_SPARC_PLT64 47
#define R_SPARC_HIX22 48
#define R_SPARC_LOX10 49
#define R_SPARC_H44 50
#define R_SPARC_M44 51
#define R_SPARC_L44 52
#define R_SPARC_REGISTER 53
#define R_SPARC_UA64 54
#define R_SPARC_UA16 55
#define R_SPARC_TLS_GD_HI22 56
#define R_SPARC_TLS_GD_LO10 57
#define R_SPARC_TLS_GD_ADD 58
#define R_SPARC_TLS_GD_CALL 59
#define R_SPARC_TLS_LDM_HI22 60
#define R_SPARC_TLS_LDM_LO10 61
#define R_SPARC_TLS_LDM_ADD 62
#define R_SPARC_TLS_LDM_CALL 63
#define R_SPARC_TLS_LDO_HIX22 64
#define R_SPARC_TLS_LDO_LOX10 65
#define R_SPARC_TLS_LDO_ADD 66
#define R_SPARC_TLS_IE_HI22 67
#define R_SPARC_TLS_IE_LO10 68
#define R_SPARC_TLS_IE_LD 69
#define R_SPARC_TLS_IE_LDX 70
#define R_SPARC_TLS_IE_ADD 71
#define R_SPARC_TLS_LE_HIX22 72
#define R_SPARC_TLS_LE_LOX10 73
#define R_SPARC_TLS_DTPMOD32 74
#define R_SPARC_TLS_DTPMOD64 75
#define R_SPARC_TLS_DTPOFF32 76
#define R_SPARC_TLS_DTPOFF64 77
#define R_SPARC_TLS_TPOFF32 78
#define R_SPARC_TLS_TPOFF64 79
#define R_SPARC_GOTDATA_HIX22 80
#define R_SPARC_GOTDATA_LOX10 81
#define R_SPARC_GOTDATA_OP_HIX22 82
#define R_SPARC_GOTDATA_OP_LOX10 83
#define R_SPARC_GOTDATA_OP 84
#define R_SPARC_H34 85
#define R_SPARC_SIZE32 86
#define R_SPARC_SIZE64 87
#define R_SPARC_GNU_VTINHERIT 250
#define R_SPARC_GNU_VTENTRY 251
#define R_SPARC_REV32 252
#define R_SPARC_NUM 253
#define DT_SPARC_REGISTER 0x70000001
#define DT_SPARC_NUM 2
#define EF_MIPS_NOREORDER 1
#define EF_MIPS_PIC 2
#define EF_MIPS_CPIC 4
#define EF_MIPS_XGOT 8
#define EF_MIPS_64BIT_WHIRL 16
#define EF_MIPS_ABI2 32
#define EF_MIPS_ABI_ON32 64
#define EF_MIPS_FP64 512
#define EF_MIPS_NAN2008 1024
#define EF_MIPS_ARCH 0xf0000000
#define EF_MIPS_ARCH_1 0x00000000
#define EF_MIPS_ARCH_2 0x10000000
#define EF_MIPS_ARCH_3 0x20000000
#define EF_MIPS_ARCH_4 0x30000000
#define EF_MIPS_ARCH_5 0x40000000
#define EF_MIPS_ARCH_32 0x50000000
#define EF_MIPS_ARCH_64 0x60000000
#define EF_MIPS_ARCH_32R2 0x70000000
#define EF_MIPS_ARCH_64R2 0x80000000
#define E_MIPS_ARCH_1 0x00000000
#define E_MIPS_ARCH_2 0x10000000
#define E_MIPS_ARCH_3 0x20000000
#define E_MIPS_ARCH_4 0x30000000
#define E_MIPS_ARCH_5 0x40000000
#define E_MIPS_ARCH_32 0x50000000
#define E_MIPS_ARCH_64 0x60000000
#define SHN_MIPS_ACOMMON 0xff00
#define SHN_MIPS_TEXT 0xff01
#define SHN_MIPS_DATA 0xff02
#define SHN_MIPS_SCOMMON 0xff03
#define SHN_MIPS_SUNDEFINED 0xff04
#define SHT_MIPS_LIBLIST 0x70000000
#define SHT_MIPS_MSYM 0x70000001
#define SHT_MIPS_CONFLICT 0x70000002
#define SHT_MIPS_GPTAB 0x70000003
#define SHT_MIPS_UCODE 0x70000004
#define SHT_MIPS_DEBUG 0x70000005
#define SHT_MIPS_REGINFO 0x70000006
#define SHT_MIPS_PACKAGE 0x70000007
#define SHT_MIPS_PACKSYM 0x70000008
#define SHT_MIPS_RELD 0x70000009
#define SHT_MIPS_IFACE 0x7000000b
#define SHT_MIPS_CONTENT 0x7000000c
#define SHT_MIPS_OPTIONS 0x7000000d
#define SHT_MIPS_SHDR 0x70000010
#define SHT_MIPS_FDESC 0x70000011
#define SHT_MIPS_EXTSYM 0x70000012
#define SHT_MIPS_DENSE 0x70000013
#define SHT_MIPS_PDESC 0x70000014
#define SHT_MIPS_LOCSYM 0x70000015
#define SHT_MIPS_AUXSYM 0x70000016
#define SHT_MIPS_OPTSYM 0x70000017
#define SHT_MIPS_LOCSTR 0x70000018
#define SHT_MIPS_LINE 0x70000019
#define SHT_MIPS_RFDESC 0x7000001a
#define SHT_MIPS_DELTASYM 0x7000001b
#define SHT_MIPS_DELTAINST 0x7000001c
#define SHT_MIPS_DELTACLASS 0x7000001d
#define SHT_MIPS_DWARF 0x7000001e
#define SHT_MIPS_DELTADECL 0x7000001f
#define SHT_MIPS_SYMBOL_LIB 0x70000020
#define SHT_MIPS_EVENTS 0x70000021
#define SHT_MIPS_TRANSLATE 0x70000022
#define SHT_MIPS_PIXIE 0x70000023
#define SHT_MIPS_XLATE 0x70000024
#define SHT_MIPS_XLATE_DEBUG 0x70000025
#define SHT_MIPS_WHIRL 0x70000026
#define SHT_MIPS_EH_REGION 0x70000027
#define SHT_MIPS_XLATE_OLD 0x70000028
#define SHT_MIPS_PDR_EXCEPTION 0x70000029
#define SHF_MIPS_GPREL 0x10000000
#define SHF_MIPS_MERGE 0x20000000
#define SHF_MIPS_ADDR 0x40000000
#define SHF_MIPS_STRINGS 0x80000000
#define SHF_MIPS_NOSTRIP 0x08000000
#define SHF_MIPS_LOCAL 0x04000000
#define SHF_MIPS_NAMES 0x02000000
#define SHF_MIPS_NODUPE 0x01000000
#define STO_MIPS_DEFAULT 0x0
#define STO_MIPS_INTERNAL 0x1
#define STO_MIPS_HIDDEN 0x2
#define STO_MIPS_PROTECTED 0x3
#define STO_MIPS_PLT 0x8
#define STO_MIPS_SC_ALIGN_UNUSED 0xff
#define STB_MIPS_SPLIT_COMMON 13
typedef union {
struct {
Elf32_Word gt_current_g_value;
Elf32_Word gt_unused;
} gt_header;
struct {
Elf32_Word gt_g_value;
Elf32_Word gt_bytes;
} gt_entry;
} Elf32_gptab;
typedef struct {
Elf32_Word ri_gprmask;
Elf32_Word ri_cprmask[4];
Elf32_Sword ri_gp_value;
} Elf32_RegInfo;
typedef struct {
unsigned char kind;
unsigned char size;
Elf32_Section section;
Elf32_Word info;
} Elf_Options;
#define ODK_NULL 0
#define ODK_REGINFO 1
#define ODK_EXCEPTIONS 2
#define ODK_PAD 3
#define ODK_HWPATCH 4
#define ODK_FILL 5
#define ODK_TAGS 6
#define ODK_HWAND 7
#define ODK_HWOR 8
#define OEX_FPU_MIN 0x1f
#define OEX_FPU_MAX 0x1f00
#define OEX_PAGE0 0x10000
#define OEX_SMM 0x20000
#define OEX_FPDBUG 0x40000
#define OEX_PRECISEFP OEX_FPDBUG
#define OEX_DISMISS 0x80000
#define OEX_FPU_INVAL 0x10
#define OEX_FPU_DIV0 0x08
#define OEX_FPU_OFLO 0x04
#define OEX_FPU_UFLO 0x02
#define OEX_FPU_INEX 0x01
#define OHW_R4KEOP 0x1
#define OHW_R8KPFETCH 0x2
#define OHW_R5KEOP 0x4
#define OHW_R5KCVTL 0x8
#define OPAD_PREFIX 0x1
#define OPAD_POSTFIX 0x2
#define OPAD_SYMBOL 0x4
typedef struct {
Elf32_Word hwp_flags1;
Elf32_Word hwp_flags2;
} Elf_Options_Hw;
#define OHWA0_R4KEOP_CHECKED 0x00000001
#define OHWA1_R4KEOP_CLEAN 0x00000002
#define R_MIPS_NONE 0
#define R_MIPS_16 1
#define R_MIPS_32 2
#define R_MIPS_REL32 3
#define R_MIPS_26 4
#define R_MIPS_HI16 5
#define R_MIPS_LO16 6
#define R_MIPS_GPREL16 7
#define R_MIPS_LITERAL 8
#define R_MIPS_GOT16 9
#define R_MIPS_PC16 10
#define R_MIPS_CALL16 11
#define R_MIPS_GPREL32 12
#define R_MIPS_SHIFT5 16
#define R_MIPS_SHIFT6 17
#define R_MIPS_64 18
#define R_MIPS_GOT_DISP 19
#define R_MIPS_GOT_PAGE 20
#define R_MIPS_GOT_OFST 21
#define R_MIPS_GOT_HI16 22
#define R_MIPS_GOT_LO16 23
#define R_MIPS_SUB 24
#define R_MIPS_INSERT_A 25
#define R_MIPS_INSERT_B 26
#define R_MIPS_DELETE 27
#define R_MIPS_HIGHER 28
#define R_MIPS_HIGHEST 29
#define R_MIPS_CALL_HI16 30
#define R_MIPS_CALL_LO16 31
#define R_MIPS_SCN_DISP 32
#define R_MIPS_REL16 33
#define R_MIPS_ADD_IMMEDIATE 34
#define R_MIPS_PJUMP 35
#define R_MIPS_RELGOT 36
#define R_MIPS_JALR 37
#define R_MIPS_TLS_DTPMOD32 38
#define R_MIPS_TLS_DTPREL32 39
#define R_MIPS_TLS_DTPMOD64 40
#define R_MIPS_TLS_DTPREL64 41
#define R_MIPS_TLS_GD 42
#define R_MIPS_TLS_LDM 43
#define R_MIPS_TLS_DTPREL_HI16 44
#define R_MIPS_TLS_DTPREL_LO16 45
#define R_MIPS_TLS_GOTTPREL 46
#define R_MIPS_TLS_TPREL32 47
#define R_MIPS_TLS_TPREL64 48
#define R_MIPS_TLS_TPREL_HI16 49
#define R_MIPS_TLS_TPREL_LO16 50
#define R_MIPS_GLOB_DAT 51
#define R_MIPS_COPY 126
#define R_MIPS_JUMP_SLOT 127
#define R_MIPS_NUM 128
#define PT_MIPS_REGINFO 0x70000000
#define PT_MIPS_RTPROC 0x70000001
#define PT_MIPS_OPTIONS 0x70000002
#define PT_MIPS_ABIFLAGS 0x70000003
#define PF_MIPS_LOCAL 0x10000000
#define DT_MIPS_RLD_VERSION 0x70000001
#define DT_MIPS_TIME_STAMP 0x70000002
#define DT_MIPS_ICHECKSUM 0x70000003
#define DT_MIPS_IVERSION 0x70000004
#define DT_MIPS_FLAGS 0x70000005
#define DT_MIPS_BASE_ADDRESS 0x70000006
#define DT_MIPS_MSYM 0x70000007
#define DT_MIPS_CONFLICT 0x70000008
#define DT_MIPS_LIBLIST 0x70000009
#define DT_MIPS_LOCAL_GOTNO 0x7000000a
#define DT_MIPS_CONFLICTNO 0x7000000b
#define DT_MIPS_LIBLISTNO 0x70000010
#define DT_MIPS_SYMTABNO 0x70000011
#define DT_MIPS_UNREFEXTNO 0x70000012
#define DT_MIPS_GOTSYM 0x70000013
#define DT_MIPS_HIPAGENO 0x70000014
#define DT_MIPS_RLD_MAP 0x70000016
#define DT_MIPS_DELTA_CLASS 0x70000017
#define DT_MIPS_DELTA_CLASS_NO 0x70000018
#define DT_MIPS_DELTA_INSTANCE 0x70000019
#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a
#define DT_MIPS_DELTA_RELOC 0x7000001b
#define DT_MIPS_DELTA_RELOC_NO 0x7000001c
#define DT_MIPS_DELTA_SYM 0x7000001d
#define DT_MIPS_DELTA_SYM_NO 0x7000001e
#define DT_MIPS_DELTA_CLASSSYM 0x70000020
#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021
#define DT_MIPS_CXX_FLAGS 0x70000022
#define DT_MIPS_PIXIE_INIT 0x70000023
#define DT_MIPS_SYMBOL_LIB 0x70000024
#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
#define DT_MIPS_LOCAL_GOTIDX 0x70000026
#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
#define DT_MIPS_OPTIONS 0x70000029
#define DT_MIPS_INTERFACE 0x7000002a
#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
#define DT_MIPS_INTERFACE_SIZE 0x7000002c
#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d
#define DT_MIPS_PERF_SUFFIX 0x7000002e
#define DT_MIPS_COMPACT_SIZE 0x7000002f
#define DT_MIPS_GP_VALUE 0x70000030
#define DT_MIPS_AUX_DYNAMIC 0x70000031
#define DT_MIPS_PLTGOT 0x70000032
#define DT_MIPS_RWPLT 0x70000034
#define DT_MIPS_RLD_MAP_REL 0x70000035
#define DT_MIPS_NUM 0x36
#define RHF_NONE 0
#define RHF_QUICKSTART (1 << 0)
#define RHF_NOTPOT (1 << 1)
#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2)
#define RHF_NO_MOVE (1 << 3)
#define RHF_SGI_ONLY (1 << 4)
#define RHF_GUARANTEE_INIT (1 << 5)
#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
#define RHF_GUARANTEE_START_INIT (1 << 7)
#define RHF_PIXIE (1 << 8)
#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
#define RHF_REQUICKSTART (1 << 10)
#define RHF_REQUICKSTARTED (1 << 11)
#define RHF_CORD (1 << 12)
#define RHF_NO_UNRES_UNDEF (1 << 13)
#define RHF_RLD_ORDER_SAFE (1 << 14)
typedef struct {
Elf32_Word l_name;
Elf32_Word l_time_stamp;
Elf32_Word l_checksum;
Elf32_Word l_version;
Elf32_Word l_flags;
} Elf32_Lib;
typedef struct {
Elf64_Word l_name;
Elf64_Word l_time_stamp;
Elf64_Word l_checksum;
Elf64_Word l_version;
Elf64_Word l_flags;
} Elf64_Lib;
#define LL_NONE 0
#define LL_EXACT_MATCH (1 << 0)
#define LL_IGNORE_INT_VER (1 << 1)
#define LL_REQUIRE_MINOR (1 << 2)
#define LL_EXPORTS (1 << 3)
#define LL_DELAY_LOAD (1 << 4)
#define LL_DELTA (1 << 5)
typedef Elf32_Addr Elf32_Conflict;
typedef struct {
Elf32_Half version;
unsigned char isa_level;
unsigned char isa_rev;
unsigned char gpr_size;
unsigned char cpr1_size;
unsigned char cpr2_size;
unsigned char fp_abi;
Elf32_Word isa_ext;
Elf32_Word ases;
Elf32_Word flags1;
Elf32_Word flags2;
} Elf_MIPS_ABIFlags_v0;
#define MIPS_AFL_REG_NONE 0x00
#define MIPS_AFL_REG_32 0x01
#define MIPS_AFL_REG_64 0x02
#define MIPS_AFL_REG_128 0x03
#define MIPS_AFL_ASE_DSP 0x00000001
#define MIPS_AFL_ASE_DSPR2 0x00000002
#define MIPS_AFL_ASE_EVA 0x00000004
#define MIPS_AFL_ASE_MCU 0x00000008
#define MIPS_AFL_ASE_MDMX 0x00000010
#define MIPS_AFL_ASE_MIPS3D 0x00000020
#define MIPS_AFL_ASE_MT 0x00000040
#define MIPS_AFL_ASE_SMARTMIPS 0x00000080
#define MIPS_AFL_ASE_VIRT 0x00000100
#define MIPS_AFL_ASE_MSA 0x00000200
#define MIPS_AFL_ASE_MIPS16 0x00000400
#define MIPS_AFL_ASE_MICROMIPS 0x00000800
#define MIPS_AFL_ASE_XPA 0x00001000
#define MIPS_AFL_ASE_MASK 0x00001fff
#define MIPS_AFL_EXT_XLR 1
#define MIPS_AFL_EXT_OCTEON2 2
#define MIPS_AFL_EXT_OCTEONP 3
#define MIPS_AFL_EXT_LOONGSON_3A 4
#define MIPS_AFL_EXT_OCTEON 5
#define MIPS_AFL_EXT_5900 6
#define MIPS_AFL_EXT_4650 7
#define MIPS_AFL_EXT_4010 8
#define MIPS_AFL_EXT_4100 9
#define MIPS_AFL_EXT_3900 10
#define MIPS_AFL_EXT_10000 11
#define MIPS_AFL_EXT_SB1 12
#define MIPS_AFL_EXT_4111 13
#define MIPS_AFL_EXT_4120 14
#define MIPS_AFL_EXT_5400 15
#define MIPS_AFL_EXT_5500 16
#define MIPS_AFL_EXT_LOONGSON_2E 17
#define MIPS_AFL_EXT_LOONGSON_2F 18
#define MIPS_AFL_FLAGS1_ODDSPREG 1
enum
{
Val_GNU_MIPS_ABI_FP_ANY = 0,
Val_GNU_MIPS_ABI_FP_DOUBLE = 1,
Val_GNU_MIPS_ABI_FP_SINGLE = 2,
Val_GNU_MIPS_ABI_FP_SOFT = 3,
Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
Val_GNU_MIPS_ABI_FP_XX = 5,
Val_GNU_MIPS_ABI_FP_64 = 6,
Val_GNU_MIPS_ABI_FP_64A = 7,
Val_GNU_MIPS_ABI_FP_MAX = 7
};
#define EF_PARISC_TRAPNIL 0x00010000
#define EF_PARISC_EXT 0x00020000
#define EF_PARISC_LSB 0x00040000
#define EF_PARISC_WIDE 0x00080000
#define EF_PARISC_NO_KABP 0x00100000
#define EF_PARISC_LAZYSWAP 0x00400000
#define EF_PARISC_ARCH 0x0000ffff
#define EFA_PARISC_1_0 0x020b
#define EFA_PARISC_1_1 0x0210
#define EFA_PARISC_2_0 0x0214
#define SHN_PARISC_ANSI_COMMON 0xff00
#define SHN_PARISC_HUGE_COMMON 0xff01
#define SHT_PARISC_EXT 0x70000000
#define SHT_PARISC_UNWIND 0x70000001
#define SHT_PARISC_DOC 0x70000002
#define SHF_PARISC_SHORT 0x20000000
#define SHF_PARISC_HUGE 0x40000000
#define SHF_PARISC_SBP 0x80000000
#define STT_PARISC_MILLICODE 13
#define STT_HP_OPAQUE (STT_LOOS + 0x1)
#define STT_HP_STUB (STT_LOOS + 0x2)
#define R_PARISC_NONE 0
#define R_PARISC_DIR32 1
#define R_PARISC_DIR21L 2
#define R_PARISC_DIR17R 3
#define R_PARISC_DIR17F 4
#define R_PARISC_DIR14R 6
#define R_PARISC_PCREL32 9
#define R_PARISC_PCREL21L 10
#define R_PARISC_PCREL17R 11
#define R_PARISC_PCREL17F 12
#define R_PARISC_PCREL14R 14
#define R_PARISC_DPREL21L 18
#define R_PARISC_DPREL14R 22
#define R_PARISC_GPREL21L 26
#define R_PARISC_GPREL14R 30
#define R_PARISC_LTOFF21L 34
#define R_PARISC_LTOFF14R 38
#define R_PARISC_SECREL32 41
#define R_PARISC_SEGBASE 48
#define R_PARISC_SEGREL32 49
#define R_PARISC_PLTOFF21L 50
#define R_PARISC_PLTOFF14R 54
#define R_PARISC_LTOFF_FPTR32 57
#define R_PARISC_LTOFF_FPTR21L 58
#define R_PARISC_LTOFF_FPTR14R 62
#define R_PARISC_FPTR64 64
#define R_PARISC_PLABEL32 65
#define R_PARISC_PLABEL21L 66
#define R_PARISC_PLABEL14R 70
#define R_PARISC_PCREL64 72
#define R_PARISC_PCREL22F 74
#define R_PARISC_PCREL14WR 75
#define R_PARISC_PCREL14DR 76
#define R_PARISC_PCREL16F 77
#define R_PARISC_PCREL16WF 78
#define R_PARISC_PCREL16DF 79
#define R_PARISC_DIR64 80
#define R_PARISC_DIR14WR 83
#define R_PARISC_DIR14DR 84
#define R_PARISC_DIR16F 85
#define R_PARISC_DIR16WF 86
#define R_PARISC_DIR16DF 87
#define R_PARISC_GPREL64 88
#define R_PARISC_GPREL14WR 91
#define R_PARISC_GPREL14DR 92
#define R_PARISC_GPREL16F 93
#define R_PARISC_GPREL16WF 94
#define R_PARISC_GPREL16DF 95
#define R_PARISC_LTOFF64 96
#define R_PARISC_LTOFF14WR 99
#define R_PARISC_LTOFF14DR 100
#define R_PARISC_LTOFF16F 101
#define R_PARISC_LTOFF16WF 102
#define R_PARISC_LTOFF16DF 103
#define R_PARISC_SECREL64 104
#define R_PARISC_SEGREL64 112
#define R_PARISC_PLTOFF14WR 115
#define R_PARISC_PLTOFF14DR 116
#define R_PARISC_PLTOFF16F 117
#define R_PARISC_PLTOFF16WF 118
#define R_PARISC_PLTOFF16DF 119
#define R_PARISC_LTOFF_FPTR64 120
#define R_PARISC_LTOFF_FPTR14WR 123
#define R_PARISC_LTOFF_FPTR14DR 124
#define R_PARISC_LTOFF_FPTR16F 125
#define R_PARISC_LTOFF_FPTR16WF 126
#define R_PARISC_LTOFF_FPTR16DF 127
#define R_PARISC_LORESERVE 128
#define R_PARISC_COPY 128
#define R_PARISC_IPLT 129
#define R_PARISC_EPLT 130
#define R_PARISC_TPREL32 153
#define R_PARISC_TPREL21L 154
#define R_PARISC_TPREL14R 158
#define R_PARISC_LTOFF_TP21L 162
#define R_PARISC_LTOFF_TP14R 166
#define R_PARISC_LTOFF_TP14F 167
#define R_PARISC_TPREL64 216
#define R_PARISC_TPREL14WR 219
#define R_PARISC_TPREL14DR 220
#define R_PARISC_TPREL16F 221
#define R_PARISC_TPREL16WF 222
#define R_PARISC_TPREL16DF 223
#define R_PARISC_LTOFF_TP64 224
#define R_PARISC_LTOFF_TP14WR 227
#define R_PARISC_LTOFF_TP14DR 228
#define R_PARISC_LTOFF_TP16F 229
#define R_PARISC_LTOFF_TP16WF 230
#define R_PARISC_LTOFF_TP16DF 231
#define R_PARISC_GNU_VTENTRY 232
#define R_PARISC_GNU_VTINHERIT 233
#define R_PARISC_TLS_GD21L 234
#define R_PARISC_TLS_GD14R 235
#define R_PARISC_TLS_GDCALL 236
#define R_PARISC_TLS_LDM21L 237
#define R_PARISC_TLS_LDM14R 238
#define R_PARISC_TLS_LDMCALL 239
#define R_PARISC_TLS_LDO21L 240
#define R_PARISC_TLS_LDO14R 241
#define R_PARISC_TLS_DTPMOD32 242
#define R_PARISC_TLS_DTPMOD64 243
#define R_PARISC_TLS_DTPOFF32 244
#define R_PARISC_TLS_DTPOFF64 245
#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L
#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R
#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L
#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R
#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32
#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64
#define R_PARISC_HIRESERVE 255
#define PT_HP_TLS (PT_LOOS + 0x0)
#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
#define PT_HP_PARALLEL (PT_LOOS + 0x10)
#define PT_HP_FASTBIND (PT_LOOS + 0x11)
#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
#define PT_HP_STACK (PT_LOOS + 0x14)
#define PT_PARISC_ARCHEXT 0x70000000
#define PT_PARISC_UNWIND 0x70000001
#define PF_PARISC_SBP 0x08000000
#define PF_HP_PAGE_SIZE 0x00100000
#define PF_HP_FAR_SHARED 0x00200000
#define PF_HP_NEAR_SHARED 0x00400000
#define PF_HP_CODE 0x01000000
#define PF_HP_MODIFY 0x02000000
#define PF_HP_LAZYSWAP 0x04000000
#define PF_HP_SBP 0x08000000
#define EF_ALPHA_32BIT 1
#define EF_ALPHA_CANRELAX 2
#define SHT_ALPHA_DEBUG 0x70000001
#define SHT_ALPHA_REGINFO 0x70000002
#define SHF_ALPHA_GPREL 0x10000000
#define STO_ALPHA_NOPV 0x80
#define STO_ALPHA_STD_GPLOAD 0x88
#define R_ALPHA_NONE 0
#define R_ALPHA_REFLONG 1
#define R_ALPHA_REFQUAD 2
#define R_ALPHA_GPREL32 3
#define R_ALPHA_LITERAL 4
#define R_ALPHA_LITUSE 5
#define R_ALPHA_GPDISP 6
#define R_ALPHA_BRADDR 7
#define R_ALPHA_HINT 8
#define R_ALPHA_SREL16 9
#define R_ALPHA_SREL32 10
#define R_ALPHA_SREL64 11
#define R_ALPHA_GPRELHIGH 17
#define R_ALPHA_GPRELLOW 18
#define R_ALPHA_GPREL16 19
#define R_ALPHA_COPY 24
#define R_ALPHA_GLOB_DAT 25
#define R_ALPHA_JMP_SLOT 26
#define R_ALPHA_RELATIVE 27
#define R_ALPHA_TLS_GD_HI 28
#define R_ALPHA_TLSGD 29
#define R_ALPHA_TLS_LDM 30
#define R_ALPHA_DTPMOD64 31
#define R_ALPHA_GOTDTPREL 32
#define R_ALPHA_DTPREL64 33
#define R_ALPHA_DTPRELHI 34
#define R_ALPHA_DTPRELLO 35
#define R_ALPHA_DTPREL16 36
#define R_ALPHA_GOTTPREL 37
#define R_ALPHA_TPREL64 38
#define R_ALPHA_TPRELHI 39
#define R_ALPHA_TPRELLO 40
#define R_ALPHA_TPREL16 41
#define R_ALPHA_NUM 46
#define LITUSE_ALPHA_ADDR 0
#define LITUSE_ALPHA_BASE 1
#define LITUSE_ALPHA_BYTOFF 2
#define LITUSE_ALPHA_JSR 3
#define LITUSE_ALPHA_TLS_GD 4
#define LITUSE_ALPHA_TLS_LDM 5
#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
#define DT_ALPHA_NUM 1
#define EF_PPC_EMB 0x80000000
#define EF_PPC_RELOCATABLE 0x00010000
#define EF_PPC_RELOCATABLE_LIB 0x00008000
#define R_PPC_NONE 0
#define R_PPC_ADDR32 1
#define R_PPC_ADDR24 2
#define R_PPC_ADDR16 3
#define R_PPC_ADDR16_LO 4
#define R_PPC_ADDR16_HI 5
#define R_PPC_ADDR16_HA 6
#define R_PPC_ADDR14 7
#define R_PPC_ADDR14_BRTAKEN 8
#define R_PPC_ADDR14_BRNTAKEN 9
#define R_PPC_REL24 10
#define R_PPC_REL14 11
#define R_PPC_REL14_BRTAKEN 12
#define R_PPC_REL14_BRNTAKEN 13
#define R_PPC_GOT16 14
#define R_PPC_GOT16_LO 15
#define R_PPC_GOT16_HI 16
#define R_PPC_GOT16_HA 17
#define R_PPC_PLTREL24 18
#define R_PPC_COPY 19
#define R_PPC_GLOB_DAT 20
#define R_PPC_JMP_SLOT 21
#define R_PPC_RELATIVE 22
#define R_PPC_LOCAL24PC 23
#define R_PPC_UADDR32 24
#define R_PPC_UADDR16 25
#define R_PPC_REL32 26
#define R_PPC_PLT32 27
#define R_PPC_PLTREL32 28
#define R_PPC_PLT16_LO 29
#define R_PPC_PLT16_HI 30
#define R_PPC_PLT16_HA 31
#define R_PPC_SDAREL16 32
#define R_PPC_SECTOFF 33
#define R_PPC_SECTOFF_LO 34
#define R_PPC_SECTOFF_HI 35
#define R_PPC_SECTOFF_HA 36
#define R_PPC_TLS 67
#define R_PPC_DTPMOD32 68
#define R_PPC_TPREL16 69
#define R_PPC_TPREL16_LO 70
#define R_PPC_TPREL16_HI 71
#define R_PPC_TPREL16_HA 72
#define R_PPC_TPREL32 73
#define R_PPC_DTPREL16 74
#define R_PPC_DTPREL16_LO 75
#define R_PPC_DTPREL16_HI 76
#define R_PPC_DTPREL16_HA 77
#define R_PPC_DTPREL32 78
#define R_PPC_GOT_TLSGD16 79
#define R_PPC_GOT_TLSGD16_LO 80
#define R_PPC_GOT_TLSGD16_HI 81
#define R_PPC_GOT_TLSGD16_HA 82
#define R_PPC_GOT_TLSLD16 83
#define R_PPC_GOT_TLSLD16_LO 84
#define R_PPC_GOT_TLSLD16_HI 85
#define R_PPC_GOT_TLSLD16_HA 86
#define R_PPC_GOT_TPREL16 87
#define R_PPC_GOT_TPREL16_LO 88
#define R_PPC_GOT_TPREL16_HI 89
#define R_PPC_GOT_TPREL16_HA 90
#define R_PPC_GOT_DTPREL16 91
#define R_PPC_GOT_DTPREL16_LO 92
#define R_PPC_GOT_DTPREL16_HI 93
#define R_PPC_GOT_DTPREL16_HA 94
#define R_PPC_TLSGD 95
#define R_PPC_TLSLD 96
#define R_PPC_EMB_NADDR32 101
#define R_PPC_EMB_NADDR16 102
#define R_PPC_EMB_NADDR16_LO 103
#define R_PPC_EMB_NADDR16_HI 104
#define R_PPC_EMB_NADDR16_HA 105
#define R_PPC_EMB_SDAI16 106
#define R_PPC_EMB_SDA2I16 107
#define R_PPC_EMB_SDA2REL 108
#define R_PPC_EMB_SDA21 109
#define R_PPC_EMB_MRKREF 110
#define R_PPC_EMB_RELSEC16 111
#define R_PPC_EMB_RELST_LO 112
#define R_PPC_EMB_RELST_HI 113
#define R_PPC_EMB_RELST_HA 114
#define R_PPC_EMB_BIT_FLD 115
#define R_PPC_EMB_RELSDA 116
#define R_PPC_DIAB_SDA21_LO 180
#define R_PPC_DIAB_SDA21_HI 181
#define R_PPC_DIAB_SDA21_HA 182
#define R_PPC_DIAB_RELSDA_LO 183
#define R_PPC_DIAB_RELSDA_HI 184
#define R_PPC_DIAB_RELSDA_HA 185
#define R_PPC_IRELATIVE 248
#define R_PPC_REL16 249
#define R_PPC_REL16_LO 250
#define R_PPC_REL16_HI 251
#define R_PPC_REL16_HA 252
#define R_PPC_TOC16 255
#define DT_PPC_GOT (DT_LOPROC + 0)
#define DT_PPC_OPT (DT_LOPROC + 1)
#define DT_PPC_NUM 2
#define PPC_OPT_TLS 1
#define R_PPC64_NONE R_PPC_NONE
#define R_PPC64_ADDR32 R_PPC_ADDR32
#define R_PPC64_ADDR24 R_PPC_ADDR24
#define R_PPC64_ADDR16 R_PPC_ADDR16
#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO
#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI
#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA
#define R_PPC64_ADDR14 R_PPC_ADDR14
#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
#define R_PPC64_REL24 R_PPC_REL24
#define R_PPC64_REL14 R_PPC_REL14
#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
#define R_PPC64_GOT16 R_PPC_GOT16
#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
#define R_PPC64_COPY R_PPC_COPY
#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
#define R_PPC64_RELATIVE R_PPC_RELATIVE
#define R_PPC64_UADDR32 R_PPC_UADDR32
#define R_PPC64_UADDR16 R_PPC_UADDR16
#define R_PPC64_REL32 R_PPC_REL32
#define R_PPC64_PLT32 R_PPC_PLT32
#define R_PPC64_PLTREL32 R_PPC_PLTREL32
#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
#define R_PPC64_SECTOFF R_PPC_SECTOFF
#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
#define R_PPC64_ADDR30 37
#define R_PPC64_ADDR64 38
#define R_PPC64_ADDR16_HIGHER 39
#define R_PPC64_ADDR16_HIGHERA 40
#define R_PPC64_ADDR16_HIGHEST 41
#define R_PPC64_ADDR16_HIGHESTA 42
#define R_PPC64_UADDR64 43
#define R_PPC64_REL64 44
#define R_PPC64_PLT64 45
#define R_PPC64_PLTREL64 46
#define R_PPC64_TOC16 47
#define R_PPC64_TOC16_LO 48
#define R_PPC64_TOC16_HI 49
#define R_PPC64_TOC16_HA 50
#define R_PPC64_TOC 51
#define R_PPC64_PLTGOT16 52
#define R_PPC64_PLTGOT16_LO 53
#define R_PPC64_PLTGOT16_HI 54
#define R_PPC64_PLTGOT16_HA 55
#define R_PPC64_ADDR16_DS 56
#define R_PPC64_ADDR16_LO_DS 57
#define R_PPC64_GOT16_DS 58
#define R_PPC64_GOT16_LO_DS 59
#define R_PPC64_PLT16_LO_DS 60
#define R_PPC64_SECTOFF_DS 61
#define R_PPC64_SECTOFF_LO_DS 62
#define R_PPC64_TOC16_DS 63
#define R_PPC64_TOC16_LO_DS 64
#define R_PPC64_PLTGOT16_DS 65
#define R_PPC64_PLTGOT16_LO_DS 66
#define R_PPC64_TLS 67
#define R_PPC64_DTPMOD64 68
#define R_PPC64_TPREL16 69
#define R_PPC64_TPREL16_LO 70
#define R_PPC64_TPREL16_HI 71
#define R_PPC64_TPREL16_HA 72
#define R_PPC64_TPREL64 73
#define R_PPC64_DTPREL16 74
#define R_PPC64_DTPREL16_LO 75
#define R_PPC64_DTPREL16_HI 76
#define R_PPC64_DTPREL16_HA 77
#define R_PPC64_DTPREL64 78
#define R_PPC64_GOT_TLSGD16 79
#define R_PPC64_GOT_TLSGD16_LO 80
#define R_PPC64_GOT_TLSGD16_HI 81
#define R_PPC64_GOT_TLSGD16_HA 82
#define R_PPC64_GOT_TLSLD16 83
#define R_PPC64_GOT_TLSLD16_LO 84
#define R_PPC64_GOT_TLSLD16_HI 85
#define R_PPC64_GOT_TLSLD16_HA 86
#define R_PPC64_GOT_TPREL16_DS 87
#define R_PPC64_GOT_TPREL16_LO_DS 88
#define R_PPC64_GOT_TPREL16_HI 89
#define R_PPC64_GOT_TPREL16_HA 90
#define R_PPC64_GOT_DTPREL16_DS 91
#define R_PPC64_GOT_DTPREL16_LO_DS 92
#define R_PPC64_GOT_DTPREL16_HI 93
#define R_PPC64_GOT_DTPREL16_HA 94
#define R_PPC64_TPREL16_DS 95
#define R_PPC64_TPREL16_LO_DS 96
#define R_PPC64_TPREL16_HIGHER 97
#define R_PPC64_TPREL16_HIGHERA 98
#define R_PPC64_TPREL16_HIGHEST 99
#define R_PPC64_TPREL16_HIGHESTA 100
#define R_PPC64_DTPREL16_DS 101
#define R_PPC64_DTPREL16_LO_DS 102
#define R_PPC64_DTPREL16_HIGHER 103
#define R_PPC64_DTPREL16_HIGHERA 104
#define R_PPC64_DTPREL16_HIGHEST 105
#define R_PPC64_DTPREL16_HIGHESTA 106
#define R_PPC64_TLSGD 107
#define R_PPC64_TLSLD 108
#define R_PPC64_TOCSAVE 109
#define R_PPC64_ADDR16_HIGH 110
#define R_PPC64_ADDR16_HIGHA 111
#define R_PPC64_TPREL16_HIGH 112
#define R_PPC64_TPREL16_HIGHA 113
#define R_PPC64_DTPREL16_HIGH 114
#define R_PPC64_DTPREL16_HIGHA 115
#define R_PPC64_JMP_IREL 247
#define R_PPC64_IRELATIVE 248
#define R_PPC64_REL16 249
#define R_PPC64_REL16_LO 250
#define R_PPC64_REL16_HI 251
#define R_PPC64_REL16_HA 252
#define EF_PPC64_ABI 3
#define DT_PPC64_GLINK (DT_LOPROC + 0)
#define DT_PPC64_OPD (DT_LOPROC + 1)
#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
#define DT_PPC64_OPT (DT_LOPROC + 3)
#define DT_PPC64_NUM 4
#define PPC64_OPT_TLS 1
#define PPC64_OPT_MULTI_TOC 2
#define STO_PPC64_LOCAL_BIT 5
#define STO_PPC64_LOCAL_MASK 0xe0
#define PPC64_LOCAL_ENTRY_OFFSET(x) (1 << (((x)&0xe0)>>5) & 0xfc)
#define EF_ARM_RELEXEC 0x01
#define EF_ARM_HASENTRY 0x02
#define EF_ARM_INTERWORK 0x04
#define EF_ARM_APCS_26 0x08
#define EF_ARM_APCS_FLOAT 0x10
#define EF_ARM_PIC 0x20
#define EF_ARM_ALIGN8 0x40
#define EF_ARM_NEW_ABI 0x80
#define EF_ARM_OLD_ABI 0x100
#define EF_ARM_SOFT_FLOAT 0x200
#define EF_ARM_VFP_FLOAT 0x400
#define EF_ARM_MAVERICK_FLOAT 0x800
#define EF_ARM_ABI_FLOAT_SOFT 0x200
#define EF_ARM_ABI_FLOAT_HARD 0x400
#define EF_ARM_SYMSARESORTED 0x04
#define EF_ARM_DYNSYMSUSESEGIDX 0x08
#define EF_ARM_MAPSYMSFIRST 0x10
#define EF_ARM_EABIMASK 0XFF000000
#define EF_ARM_BE8 0x00800000
#define EF_ARM_LE8 0x00400000
#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
#define EF_ARM_EABI_UNKNOWN 0x00000000
#define EF_ARM_EABI_VER1 0x01000000
#define EF_ARM_EABI_VER2 0x02000000
#define EF_ARM_EABI_VER3 0x03000000
#define EF_ARM_EABI_VER4 0x04000000
#define EF_ARM_EABI_VER5 0x05000000
#define STT_ARM_TFUNC STT_LOPROC
#define STT_ARM_16BIT STT_HIPROC
#define SHF_ARM_ENTRYSECT 0x10000000
#define SHF_ARM_COMDEF 0x80000000
#define PF_ARM_SB 0x10000000
#define PF_ARM_PI 0x20000000
#define PF_ARM_ABS 0x40000000
#define PT_ARM_EXIDX (PT_LOPROC + 1)
#define SHT_ARM_EXIDX (SHT_LOPROC + 1)
#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2)
#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3)
#define R_AARCH64_NONE 0
#define R_AARCH64_P32_ABS32 1
#define R_AARCH64_P32_COPY 180
#define R_AARCH64_P32_GLOB_DAT 181
#define R_AARCH64_P32_JUMP_SLOT 182
#define R_AARCH64_P32_RELATIVE 183
#define R_AARCH64_P32_TLS_DTPMOD 184
#define R_AARCH64_P32_TLS_DTPREL 185
#define R_AARCH64_P32_TLS_TPREL 186
#define R_AARCH64_P32_TLSDESC 187
#define R_AARCH64_P32_IRELATIVE 188
#define R_AARCH64_ABS64 257
#define R_AARCH64_ABS32 258
#define R_AARCH64_ABS16 259
#define R_AARCH64_PREL64 260
#define R_AARCH64_PREL32 261
#define R_AARCH64_PREL16 262
#define R_AARCH64_MOVW_UABS_G0 263
#define R_AARCH64_MOVW_UABS_G0_NC 264
#define R_AARCH64_MOVW_UABS_G1 265
#define R_AARCH64_MOVW_UABS_G1_NC 266
#define R_AARCH64_MOVW_UABS_G2 267
#define R_AARCH64_MOVW_UABS_G2_NC 268
#define R_AARCH64_MOVW_UABS_G3 269
#define R_AARCH64_MOVW_SABS_G0 270
#define R_AARCH64_MOVW_SABS_G1 271
#define R_AARCH64_MOVW_SABS_G2 272
#define R_AARCH64_LD_PREL_LO19 273
#define R_AARCH64_ADR_PREL_LO21 274
#define R_AARCH64_ADR_PREL_PG_HI21 275
#define R_AARCH64_ADR_PREL_PG_HI21_NC 276
#define R_AARCH64_ADD_ABS_LO12_NC 277
#define R_AARCH64_LDST8_ABS_LO12_NC 278
#define R_AARCH64_TSTBR14 279
#define R_AARCH64_CONDBR19 280
#define R_AARCH64_JUMP26 282
#define R_AARCH64_CALL26 283
#define R_AARCH64_LDST16_ABS_LO12_NC 284
#define R_AARCH64_LDST32_ABS_LO12_NC 285
#define R_AARCH64_LDST64_ABS_LO12_NC 286
#define R_AARCH64_MOVW_PREL_G0 287
#define R_AARCH64_MOVW_PREL_G0_NC 288
#define R_AARCH64_MOVW_PREL_G1 289
#define R_AARCH64_MOVW_PREL_G1_NC 290
#define R_AARCH64_MOVW_PREL_G2 291
#define R_AARCH64_MOVW_PREL_G2_NC 292
#define R_AARCH64_MOVW_PREL_G3 293
#define R_AARCH64_LDST128_ABS_LO12_NC 299
#define R_AARCH64_MOVW_GOTOFF_G0 300
#define R_AARCH64_MOVW_GOTOFF_G0_NC 301
#define R_AARCH64_MOVW_GOTOFF_G1 302
#define R_AARCH64_MOVW_GOTOFF_G1_NC 303
#define R_AARCH64_MOVW_GOTOFF_G2 304
#define R_AARCH64_MOVW_GOTOFF_G2_NC 305
#define R_AARCH64_MOVW_GOTOFF_G3 306
#define R_AARCH64_GOTREL64 307
#define R_AARCH64_GOTREL32 308
#define R_AARCH64_GOT_LD_PREL19 309
#define R_AARCH64_LD64_GOTOFF_LO15 310
#define R_AARCH64_ADR_GOT_PAGE 311
#define R_AARCH64_LD64_GOT_LO12_NC 312
#define R_AARCH64_LD64_GOTPAGE_LO15 313
#define R_AARCH64_TLSGD_ADR_PREL21 512
#define R_AARCH64_TLSGD_ADR_PAGE21 513
#define R_AARCH64_TLSGD_ADD_LO12_NC 514
#define R_AARCH64_TLSGD_MOVW_G1 515
#define R_AARCH64_TLSGD_MOVW_G0_NC 516
#define R_AARCH64_TLSLD_ADR_PREL21 517
#define R_AARCH64_TLSLD_ADR_PAGE21 518
#define R_AARCH64_TLSLD_ADD_LO12_NC 519
#define R_AARCH64_TLSLD_MOVW_G1 520
#define R_AARCH64_TLSLD_MOVW_G0_NC 521
#define R_AARCH64_TLSLD_LD_PREL19 522
#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524
#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526
#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527
#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529
#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531
#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533
#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535
#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537
#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539
#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540
#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541
#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542
#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543
#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544
#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545
#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546
#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547
#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548
#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549
#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550
#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552
#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554
#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556
#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558
#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559
#define R_AARCH64_TLSDESC_LD_PREL19 560
#define R_AARCH64_TLSDESC_ADR_PREL21 561
#define R_AARCH64_TLSDESC_ADR_PAGE21 562
#define R_AARCH64_TLSDESC_LD64_LO12 563
#define R_AARCH64_TLSDESC_ADD_LO12 564
#define R_AARCH64_TLSDESC_OFF_G1 565
#define R_AARCH64_TLSDESC_OFF_G0_NC 566
#define R_AARCH64_TLSDESC_LDR 567
#define R_AARCH64_TLSDESC_ADD 568
#define R_AARCH64_TLSDESC_CALL 569
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570
#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572
#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573
#define R_AARCH64_COPY 1024
#define R_AARCH64_GLOB_DAT 1025
#define R_AARCH64_JUMP_SLOT 1026
#define R_AARCH64_RELATIVE 1027
#define R_AARCH64_TLS_DTPMOD 1028
#define R_AARCH64_TLS_DTPMOD64 1028
#define R_AARCH64_TLS_DTPREL 1029
#define R_AARCH64_TLS_DTPREL64 1029
#define R_AARCH64_TLS_TPREL 1030
#define R_AARCH64_TLS_TPREL64 1030
#define R_AARCH64_TLSDESC 1031
#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_ARM_REL32 3
#define R_ARM_PC13 4
#define R_ARM_ABS16 5
#define R_ARM_ABS12 6
#define R_ARM_THM_ABS5 7
#define R_ARM_ABS8 8
#define R_ARM_SBREL32 9
#define R_ARM_THM_PC22 10
#define R_ARM_THM_PC8 11
#define R_ARM_AMP_VCALL9 12
#define R_ARM_TLS_DESC 13
#define R_ARM_THM_SWI8 14
#define R_ARM_XPC25 15
#define R_ARM_THM_XPC22 16
#define R_ARM_TLS_DTPMOD32 17
#define R_ARM_TLS_DTPOFF32 18
#define R_ARM_TLS_TPOFF32 19
#define R_ARM_COPY 20
#define R_ARM_GLOB_DAT 21
#define R_ARM_JUMP_SLOT 22
#define R_ARM_RELATIVE 23
#define R_ARM_GOTOFF 24
#define R_ARM_GOTPC 25
#define R_ARM_GOT32 26
#define R_ARM_PLT32 27
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
#define R_ARM_THM_JUMP24 30
#define R_ARM_BASE_ABS 31
#define R_ARM_ALU_PCREL_7_0 32
#define R_ARM_ALU_PCREL_15_8 33
#define R_ARM_ALU_PCREL_23_15 34
#define R_ARM_LDR_SBREL_11_0 35
#define R_ARM_ALU_SBREL_19_12 36
#define R_ARM_ALU_SBREL_27_20 37
#define R_ARM_TARGET1 38
#define R_ARM_SBREL31 39
#define R_ARM_V4BX 40
#define R_ARM_TARGET2 41
#define R_ARM_PREL31 42
#define R_ARM_MOVW_ABS_NC 43
#define R_ARM_MOVT_ABS 44
#define R_ARM_MOVW_PREL_NC 45
#define R_ARM_MOVT_PREL 46
#define R_ARM_THM_MOVW_ABS_NC 47
#define R_ARM_THM_MOVT_ABS 48
#define R_ARM_THM_MOVW_PREL_NC 49
#define R_ARM_THM_MOVT_PREL 50
#define R_ARM_THM_JUMP19 51
#define R_ARM_THM_JUMP6 52
#define R_ARM_THM_ALU_PREL_11_0 53
#define R_ARM_THM_PC12 54
#define R_ARM_ABS32_NOI 55
#define R_ARM_REL32_NOI 56
#define R_ARM_ALU_PC_G0_NC 57
#define R_ARM_ALU_PC_G0 58
#define R_ARM_ALU_PC_G1_NC 59
#define R_ARM_ALU_PC_G1 60
#define R_ARM_ALU_PC_G2 61
#define R_ARM_LDR_PC_G1 62
#define R_ARM_LDR_PC_G2 63
#define R_ARM_LDRS_PC_G0 64
#define R_ARM_LDRS_PC_G1 65
#define R_ARM_LDRS_PC_G2 66
#define R_ARM_LDC_PC_G0 67
#define R_ARM_LDC_PC_G1 68
#define R_ARM_LDC_PC_G2 69
#define R_ARM_ALU_SB_G0_NC 70
#define R_ARM_ALU_SB_G0 71
#define R_ARM_ALU_SB_G1_NC 72
#define R_ARM_ALU_SB_G1 73
#define R_ARM_ALU_SB_G2 74
#define R_ARM_LDR_SB_G0 75
#define R_ARM_LDR_SB_G1 76
#define R_ARM_LDR_SB_G2 77
#define R_ARM_LDRS_SB_G0 78
#define R_ARM_LDRS_SB_G1 79
#define R_ARM_LDRS_SB_G2 80
#define R_ARM_LDC_SB_G0 81
#define R_ARM_LDC_SB_G1 82
#define R_ARM_LDC_SB_G2 83
#define R_ARM_MOVW_BREL_NC 84
#define R_ARM_MOVT_BREL 85
#define R_ARM_MOVW_BREL 86
#define R_ARM_THM_MOVW_BREL_NC 87
#define R_ARM_THM_MOVT_BREL 88
#define R_ARM_THM_MOVW_BREL 89
#define R_ARM_TLS_GOTDESC 90
#define R_ARM_TLS_CALL 91
#define R_ARM_TLS_DESCSEQ 92
#define R_ARM_THM_TLS_CALL 93
#define R_ARM_PLT32_ABS 94
#define R_ARM_GOT_ABS 95
#define R_ARM_GOT_PREL 96
#define R_ARM_GOT_BREL12 97
#define R_ARM_GOTOFF12 98
#define R_ARM_GOTRELAX 99
#define R_ARM_GNU_VTENTRY 100
#define R_ARM_GNU_VTINHERIT 101
#define R_ARM_THM_PC11 102
#define R_ARM_THM_PC9 103
#define R_ARM_TLS_GD32 104
#define R_ARM_TLS_LDM32 105
#define R_ARM_TLS_LDO32 106
#define R_ARM_TLS_IE32 107
#define R_ARM_TLS_LE32 108
#define R_ARM_TLS_LDO12 109
#define R_ARM_TLS_LE12 110
#define R_ARM_TLS_IE12GP 111
#define R_ARM_ME_TOO 128
#define R_ARM_THM_TLS_DESCSEQ 129
#define R_ARM_THM_TLS_DESCSEQ16 129
#define R_ARM_THM_TLS_DESCSEQ32 130
#define R_ARM_THM_GOT_BREL12 131
#define R_ARM_IRELATIVE 160
#define R_ARM_RXPC25 249
#define R_ARM_RSBREL32 250
#define R_ARM_THM_RPC22 251
#define R_ARM_RREL32 252
#define R_ARM_RABS22 253
#define R_ARM_RPC24 254
#define R_ARM_RBASE 255
#define R_ARM_NUM 256
#define EF_IA_64_MASKOS 0x0000000f
#define EF_IA_64_ABI64 0x00000010
#define EF_IA_64_ARCH 0xff000000
#define PT_IA_64_ARCHEXT (PT_LOPROC + 0)
#define PT_IA_64_UNWIND (PT_LOPROC + 1)
#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
#define PF_IA_64_NORECOV 0x80000000
#define SHT_IA_64_EXT (SHT_LOPROC + 0)
#define SHT_IA_64_UNWIND (SHT_LOPROC + 1)
#define SHF_IA_64_SHORT 0x10000000
#define SHF_IA_64_NORECOV 0x20000000
#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
#define DT_IA_64_NUM 1
#define R_IA64_NONE 0x00
#define R_IA64_IMM14 0x21
#define R_IA64_IMM22 0x22
#define R_IA64_IMM64 0x23
#define R_IA64_DIR32MSB 0x24
#define R_IA64_DIR32LSB 0x25
#define R_IA64_DIR64MSB 0x26
#define R_IA64_DIR64LSB 0x27
#define R_IA64_GPREL22 0x2a
#define R_IA64_GPREL64I 0x2b
#define R_IA64_GPREL32MSB 0x2c
#define R_IA64_GPREL32LSB 0x2d
#define R_IA64_GPREL64MSB 0x2e
#define R_IA64_GPREL64LSB 0x2f
#define R_IA64_LTOFF22 0x32
#define R_IA64_LTOFF64I 0x33
#define R_IA64_PLTOFF22 0x3a
#define R_IA64_PLTOFF64I 0x3b
#define R_IA64_PLTOFF64MSB 0x3e
#define R_IA64_PLTOFF64LSB 0x3f
#define R_IA64_FPTR64I 0x43
#define R_IA64_FPTR32MSB 0x44
#define R_IA64_FPTR32LSB 0x45
#define R_IA64_FPTR64MSB 0x46
#define R_IA64_FPTR64LSB 0x47
#define R_IA64_PCREL60B 0x48
#define R_IA64_PCREL21B 0x49
#define R_IA64_PCREL21M 0x4a
#define R_IA64_PCREL21F 0x4b
#define R_IA64_PCREL32MSB 0x4c
#define R_IA64_PCREL32LSB 0x4d
#define R_IA64_PCREL64MSB 0x4e
#define R_IA64_PCREL64LSB 0x4f
#define R_IA64_LTOFF_FPTR22 0x52
#define R_IA64_LTOFF_FPTR64I 0x53
#define R_IA64_LTOFF_FPTR32MSB 0x54
#define R_IA64_LTOFF_FPTR32LSB 0x55
#define R_IA64_LTOFF_FPTR64MSB 0x56
#define R_IA64_LTOFF_FPTR64LSB 0x57
#define R_IA64_SEGREL32MSB 0x5c
#define R_IA64_SEGREL32LSB 0x5d
#define R_IA64_SEGREL64MSB 0x5e
#define R_IA64_SEGREL64LSB 0x5f
#define R_IA64_SECREL32MSB 0x64
#define R_IA64_SECREL32LSB 0x65
#define R_IA64_SECREL64MSB 0x66
#define R_IA64_SECREL64LSB 0x67
#define R_IA64_REL32MSB 0x6c
#define R_IA64_REL32LSB 0x6d
#define R_IA64_REL64MSB 0x6e
#define R_IA64_REL64LSB 0x6f
#define R_IA64_LTV32MSB 0x74
#define R_IA64_LTV32LSB 0x75
#define R_IA64_LTV64MSB 0x76
#define R_IA64_LTV64LSB 0x77
#define R_IA64_PCREL21BI 0x79
#define R_IA64_PCREL22 0x7a
#define R_IA64_PCREL64I 0x7b
#define R_IA64_IPLTMSB 0x80
#define R_IA64_IPLTLSB 0x81
#define R_IA64_COPY 0x84
#define R_IA64_SUB 0x85
#define R_IA64_LTOFF22X 0x86
#define R_IA64_LDXMOV 0x87
#define R_IA64_TPREL14 0x91
#define R_IA64_TPREL22 0x92
#define R_IA64_TPREL64I 0x93
#define R_IA64_TPREL64MSB 0x96
#define R_IA64_TPREL64LSB 0x97
#define R_IA64_LTOFF_TPREL22 0x9a
#define R_IA64_DTPMOD64MSB 0xa6
#define R_IA64_DTPMOD64LSB 0xa7
#define R_IA64_LTOFF_DTPMOD22 0xaa
#define R_IA64_DTPREL14 0xb1
#define R_IA64_DTPREL22 0xb2
#define R_IA64_DTPREL64I 0xb3
#define R_IA64_DTPREL32MSB 0xb4
#define R_IA64_DTPREL32LSB 0xb5
#define R_IA64_DTPREL64MSB 0xb6
#define R_IA64_DTPREL64LSB 0xb7
#define R_IA64_LTOFF_DTPREL22 0xba
#define EF_SH_MACH_MASK 0x1f
#define EF_SH_UNKNOWN 0x0
#define EF_SH1 0x1
#define EF_SH2 0x2
#define EF_SH3 0x3
#define EF_SH_DSP 0x4
#define EF_SH3_DSP 0x5
#define EF_SH4AL_DSP 0x6
#define EF_SH3E 0x8
#define EF_SH4 0x9
#define EF_SH2E 0xb
#define EF_SH4A 0xc
#define EF_SH2A 0xd
#define EF_SH4_NOFPU 0x10
#define EF_SH4A_NOFPU 0x11
#define EF_SH4_NOMMU_NOFPU 0x12
#define EF_SH2A_NOFPU 0x13
#define EF_SH3_NOMMU 0x14
#define EF_SH2A_SH4_NOFPU 0x15
#define EF_SH2A_SH3_NOFPU 0x16
#define EF_SH2A_SH4 0x17
#define EF_SH2A_SH3E 0x18
#define R_SH_NONE 0
#define R_SH_DIR32 1
#define R_SH_REL32 2
#define R_SH_DIR8WPN 3
#define R_SH_IND12W 4
#define R_SH_DIR8WPL 5
#define R_SH_DIR8WPZ 6
#define R_SH_DIR8BP 7
#define R_SH_DIR8W 8
#define R_SH_DIR8L 9
#define R_SH_SWITCH16 25
#define R_SH_SWITCH32 26
#define R_SH_USES 27
#define R_SH_COUNT 28
#define R_SH_ALIGN 29
#define R_SH_CODE 30
#define R_SH_DATA 31
#define R_SH_LABEL 32
#define R_SH_SWITCH8 33
#define R_SH_GNU_VTINHERIT 34
#define R_SH_GNU_VTENTRY 35
#define R_SH_TLS_GD_32 144
#define R_SH_TLS_LD_32 145
#define R_SH_TLS_LDO_32 146
#define R_SH_TLS_IE_32 147
#define R_SH_TLS_LE_32 148
#define R_SH_TLS_DTPMOD32 149
#define R_SH_TLS_DTPOFF32 150
#define R_SH_TLS_TPOFF32 151
#define R_SH_GOT32 160
#define R_SH_PLT32 161
#define R_SH_COPY 162
#define R_SH_GLOB_DAT 163
#define R_SH_JMP_SLOT 164
#define R_SH_RELATIVE 165
#define R_SH_GOTOFF 166
#define R_SH_GOTPC 167
#define R_SH_GOT20 201
#define R_SH_GOTOFF20 202
#define R_SH_GOTFUNCDESC 203
#define R_SH_GOTFUNCDEST20 204
#define R_SH_GOTOFFFUNCDESC 205
#define R_SH_GOTOFFFUNCDEST20 206
#define R_SH_FUNCDESC 207
#define R_SH_FUNCDESC_VALUE 208
#define R_SH_NUM 256
#define R_390_NONE 0
#define R_390_8 1
#define R_390_12 2
#define R_390_16 3
#define R_390_32 4
#define R_390_PC32 5
#define R_390_GOT12 6
#define R_390_GOT32 7
#define R_390_PLT32 8
#define R_390_COPY 9
#define R_390_GLOB_DAT 10
#define R_390_JMP_SLOT 11
#define R_390_RELATIVE 12
#define R_390_GOTOFF32 13
#define R_390_GOTPC 14
#define R_390_GOT16 15
#define R_390_PC16 16
#define R_390_PC16DBL 17
#define R_390_PLT16DBL 18
#define R_390_PC32DBL 19
#define R_390_PLT32DBL 20
#define R_390_GOTPCDBL 21
#define R_390_64 22
#define R_390_PC64 23
#define R_390_GOT64 24
#define R_390_PLT64 25
#define R_390_GOTENT 26
#define R_390_GOTOFF16 27
#define R_390_GOTOFF64 28
#define R_390_GOTPLT12 29
#define R_390_GOTPLT16 30
#define R_390_GOTPLT32 31
#define R_390_GOTPLT64 32
#define R_390_GOTPLTENT 33
#define R_390_PLTOFF16 34
#define R_390_PLTOFF32 35
#define R_390_PLTOFF64 36
#define R_390_TLS_LOAD 37
#define R_390_TLS_GDCALL 38
#define R_390_TLS_LDCALL 39
#define R_390_TLS_GD32 40
#define R_390_TLS_GD64 41
#define R_390_TLS_GOTIE12 42
#define R_390_TLS_GOTIE32 43
#define R_390_TLS_GOTIE64 44
#define R_390_TLS_LDM32 45
#define R_390_TLS_LDM64 46
#define R_390_TLS_IE32 47
#define R_390_TLS_IE64 48
#define R_390_TLS_IEENT 49
#define R_390_TLS_LE32 50
#define R_390_TLS_LE64 51
#define R_390_TLS_LDO32 52
#define R_390_TLS_LDO64 53
#define R_390_TLS_DTPMOD 54
#define R_390_TLS_DTPOFF 55
#define R_390_TLS_TPOFF 56
#define R_390_20 57
#define R_390_GOT20 58
#define R_390_GOTPLT20 59
#define R_390_TLS_GOTIE20 60
#define R_390_NUM 61
#define R_CRIS_NONE 0
#define R_CRIS_8 1
#define R_CRIS_16 2
#define R_CRIS_32 3
#define R_CRIS_8_PCREL 4
#define R_CRIS_16_PCREL 5
#define R_CRIS_32_PCREL 6
#define R_CRIS_GNU_VTINHERIT 7
#define R_CRIS_GNU_VTENTRY 8
#define R_CRIS_COPY 9
#define R_CRIS_GLOB_DAT 10
#define R_CRIS_JUMP_SLOT 11
#define R_CRIS_RELATIVE 12
#define R_CRIS_16_GOT 13
#define R_CRIS_32_GOT 14
#define R_CRIS_16_GOTPLT 15
#define R_CRIS_32_GOTPLT 16
#define R_CRIS_32_GOTREL 17
#define R_CRIS_32_PLT_GOTREL 18
#define R_CRIS_32_PLT_PCREL 19
#define R_CRIS_NUM 20
#define R_X86_64_NONE 0
#define R_X86_64_64 1
#define R_X86_64_PC32 2
#define R_X86_64_GOT32 3
#define R_X86_64_PLT32 4
#define R_X86_64_COPY 5
#define R_X86_64_GLOB_DAT 6
#define R_X86_64_JUMP_SLOT 7
#define R_X86_64_RELATIVE 8
#define R_X86_64_GOTPCREL 9
#define R_X86_64_32 10
#define R_X86_64_32S 11
#define R_X86_64_16 12
#define R_X86_64_PC16 13
#define R_X86_64_8 14
#define R_X86_64_PC8 15
#define R_X86_64_DTPMOD64 16
#define R_X86_64_DTPOFF64 17
#define R_X86_64_TPOFF64 18
#define R_X86_64_TLSGD 19
#define R_X86_64_TLSLD 20
#define R_X86_64_DTPOFF32 21
#define R_X86_64_GOTTPOFF 22
#define R_X86_64_TPOFF32 23
#define R_X86_64_PC64 24
#define R_X86_64_GOTOFF64 25
#define R_X86_64_GOTPC32 26
#define R_X86_64_GOT64 27
#define R_X86_64_GOTPCREL64 28
#define R_X86_64_GOTPC64 29
#define R_X86_64_GOTPLT64 30
#define R_X86_64_PLTOFF64 31
#define R_X86_64_SIZE32 32
#define R_X86_64_SIZE64 33
#define R_X86_64_GOTPC32_TLSDESC 34
#define R_X86_64_TLSDESC_CALL 35
#define R_X86_64_TLSDESC 36
#define R_X86_64_IRELATIVE 37
#define R_X86_64_RELATIVE64 38
#define R_X86_64_GOTPCRELX 41
#define R_X86_64_REX_GOTPCRELX 42
#define R_X86_64_NUM 43
#define R_MN10300_NONE 0
#define R_MN10300_32 1
#define R_MN10300_16 2
#define R_MN10300_8 3
#define R_MN10300_PCREL32 4
#define R_MN10300_PCREL16 5
#define R_MN10300_PCREL8 6
#define R_MN10300_GNU_VTINHERIT 7
#define R_MN10300_GNU_VTENTRY 8
#define R_MN10300_24 9
#define R_MN10300_GOTPC32 10
#define R_MN10300_GOTPC16 11
#define R_MN10300_GOTOFF32 12
#define R_MN10300_GOTOFF24 13
#define R_MN10300_GOTOFF16 14
#define R_MN10300_PLT32 15
#define R_MN10300_PLT16 16
#define R_MN10300_GOT32 17
#define R_MN10300_GOT24 18
#define R_MN10300_GOT16 19
#define R_MN10300_COPY 20
#define R_MN10300_GLOB_DAT 21
#define R_MN10300_JMP_SLOT 22
#define R_MN10300_RELATIVE 23
#define R_MN10300_NUM 24
#define R_M32R_NONE 0
#define R_M32R_16 1
#define R_M32R_32 2
#define R_M32R_24 3
#define R_M32R_10_PCREL 4
#define R_M32R_18_PCREL 5
#define R_M32R_26_PCREL 6
#define R_M32R_HI16_ULO 7
#define R_M32R_HI16_SLO 8
#define R_M32R_LO16 9
#define R_M32R_SDA16 10
#define R_M32R_GNU_VTINHERIT 11
#define R_M32R_GNU_VTENTRY 12
#define R_M32R_16_RELA 33
#define R_M32R_32_RELA 34
#define R_M32R_24_RELA 35
#define R_M32R_10_PCREL_RELA 36
#define R_M32R_18_PCREL_RELA 37
#define R_M32R_26_PCREL_RELA 38
#define R_M32R_HI16_ULO_RELA 39
#define R_M32R_HI16_SLO_RELA 40
#define R_M32R_LO16_RELA 41
#define R_M32R_SDA16_RELA 42
#define R_M32R_RELA_GNU_VTINHERIT 43
#define R_M32R_RELA_GNU_VTENTRY 44
#define R_M32R_REL32 45
#define R_M32R_GOT24 48
#define R_M32R_26_PLTREL 49
#define R_M32R_COPY 50
#define R_M32R_GLOB_DAT 51
#define R_M32R_JMP_SLOT 52
#define R_M32R_RELATIVE 53
#define R_M32R_GOTOFF 54
#define R_M32R_GOTPC24 55
#define R_M32R_GOT16_HI_ULO 56
#define R_M32R_GOT16_HI_SLO 57
#define R_M32R_GOT16_LO 58
#define R_M32R_GOTPC_HI_ULO 59
#define R_M32R_GOTPC_HI_SLO 60
#define R_M32R_GOTPC_LO 61
#define R_M32R_GOTOFF_HI_ULO 62
#define R_M32R_GOTOFF_HI_SLO 63
#define R_M32R_GOTOFF_LO 64
#define R_M32R_NUM 256
#define R_MICROBLAZE_NONE 0
#define R_MICROBLAZE_32 1
#define R_MICROBLAZE_32_PCREL 2
#define R_MICROBLAZE_64_PCREL 3
#define R_MICROBLAZE_32_PCREL_LO 4
#define R_MICROBLAZE_64 5
#define R_MICROBLAZE_32_LO 6
#define R_MICROBLAZE_SRO32 7
#define R_MICROBLAZE_SRW32 8
#define R_MICROBLAZE_64_NONE 9
#define R_MICROBLAZE_32_SYM_OP_SYM 10
#define R_MICROBLAZE_GNU_VTINHERIT 11
#define R_MICROBLAZE_GNU_VTENTRY 12
#define R_MICROBLAZE_GOTPC_64 13
#define R_MICROBLAZE_GOT_64 14
#define R_MICROBLAZE_PLT_64 15
#define R_MICROBLAZE_REL 16
#define R_MICROBLAZE_JUMP_SLOT 17
#define R_MICROBLAZE_GLOB_DAT 18
#define R_MICROBLAZE_GOTOFF_64 19
#define R_MICROBLAZE_GOTOFF_32 20
#define R_MICROBLAZE_COPY 21
#define R_MICROBLAZE_TLS 22
#define R_MICROBLAZE_TLSGD 23
#define R_MICROBLAZE_TLSLD 24
#define R_MICROBLAZE_TLSDTPMOD32 25
#define R_MICROBLAZE_TLSDTPREL32 26
#define R_MICROBLAZE_TLSDTPREL64 27
#define R_MICROBLAZE_TLSGOTTPREL32 28
#define R_MICROBLAZE_TLSTPREL32 29
#define DT_NIOS2_GP 0x70000002
#define R_NIOS2_NONE 0
#define R_NIOS2_S16 1
#define R_NIOS2_U16 2
#define R_NIOS2_PCREL16 3
#define R_NIOS2_CALL26 4
#define R_NIOS2_IMM5 5
#define R_NIOS2_CACHE_OPX 6
#define R_NIOS2_IMM6 7
#define R_NIOS2_IMM8 8
#define R_NIOS2_HI16 9
#define R_NIOS2_LO16 10
#define R_NIOS2_HIADJ16 11
#define R_NIOS2_BFD_RELOC_32 12
#define R_NIOS2_BFD_RELOC_16 13
#define R_NIOS2_BFD_RELOC_8 14
#define R_NIOS2_GPREL 15
#define R_NIOS2_GNU_VTINHERIT 16
#define R_NIOS2_GNU_VTENTRY 17
#define R_NIOS2_UJMP 18
#define R_NIOS2_CJMP 19
#define R_NIOS2_CALLR 20
#define R_NIOS2_ALIGN 21
#define R_NIOS2_GOT16 22
#define R_NIOS2_CALL16 23
#define R_NIOS2_GOTOFF_LO 24
#define R_NIOS2_GOTOFF_HA 25
#define R_NIOS2_PCREL_LO 26
#define R_NIOS2_PCREL_HA 27
#define R_NIOS2_TLS_GD16 28
#define R_NIOS2_TLS_LDM16 29
#define R_NIOS2_TLS_LDO16 30
#define R_NIOS2_TLS_IE16 31
#define R_NIOS2_TLS_LE16 32
#define R_NIOS2_TLS_DTPMOD 33
#define R_NIOS2_TLS_DTPREL 34
#define R_NIOS2_TLS_TPREL 35
#define R_NIOS2_COPY 36
#define R_NIOS2_GLOB_DAT 37
#define R_NIOS2_JUMP_SLOT 38
#define R_NIOS2_RELATIVE 39
#define R_NIOS2_GOTOFF 40
#define R_NIOS2_CALL26_NOAT 41
#define R_NIOS2_GOT_LO 42
#define R_NIOS2_GOT_HA 43
#define R_NIOS2_CALL_LO 44
#define R_NIOS2_CALL_HA 45
#define R_OR1K_NONE 0
#define R_OR1K_32 1
#define R_OR1K_16 2
#define R_OR1K_8 3
#define R_OR1K_LO_16_IN_INSN 4
#define R_OR1K_HI_16_IN_INSN 5
#define R_OR1K_INSN_REL_26 6
#define R_OR1K_GNU_VTENTRY 7
#define R_OR1K_GNU_VTINHERIT 8
#define R_OR1K_32_PCREL 9
#define R_OR1K_16_PCREL 10
#define R_OR1K_8_PCREL 11
#define R_OR1K_GOTPC_HI16 12
#define R_OR1K_GOTPC_LO16 13
#define R_OR1K_GOT16 14
#define R_OR1K_PLT26 15
#define R_OR1K_GOTOFF_HI16 16
#define R_OR1K_GOTOFF_LO16 17
#define R_OR1K_COPY 18
#define R_OR1K_GLOB_DAT 19
#define R_OR1K_JMP_SLOT 20
#define R_OR1K_RELATIVE 21
#define R_OR1K_TLS_GD_HI16 22
#define R_OR1K_TLS_GD_LO16 23
#define R_OR1K_TLS_LDM_HI16 24
#define R_OR1K_TLS_LDM_LO16 25
#define R_OR1K_TLS_LDO_HI16 26
#define R_OR1K_TLS_LDO_LO16 27
#define R_OR1K_TLS_IE_HI16 28
#define R_OR1K_TLS_IE_LO16 29
#define R_OR1K_TLS_LE_HI16 30
#define R_OR1K_TLS_LE_LO16 31
#define R_OR1K_TLS_TPOFF 32
#define R_OR1K_TLS_DTPOFF 33
#define R_OR1K_TLS_DTPMOD 34
#define R_BPF_NONE 0
#define R_BPF_MAP_FD 1
#ifdef __cplusplus
}
#endif
#endif

317
tools/gbafix/gbafix.c Normal file
View File

@ -0,0 +1,317 @@
/*
"$Id: gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
DevkitPro GBA ROM fix utility
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
Please report all bugs and problems through the bug tracker at
"http://sourceforge.net/tracker/?group_id=114505&atid=668551".
"$Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/tools/gba/gbatools/gbafix.c,v 1.2 2008-07-30 17:12:51 wntrmute Exp $"
*/
//---------------------------------------------------------------------------------
// gbafix.c
//---------------------------------------------------------------------------------
/*
Gameboy Advance ROM fixer (by Dark Fader / BlackThunder / WinterMute / Sierraffinity)
Validates header of GBA roms.
History
-------
v1.07 - added support for ELF input, (PikalaxALT)
v1.06 - added output silencing, (Sierraffinity)
v1.05 - added debug offset argument, (Sierraffinity)
v1.04 - converted to plain C, (WinterMute)
v1.03 - header.fixed, header.device_type
v1.02 - redefined the options (rgbfix style), checksum=0
v1.01 - fix in parameters
v1.00 - logo, complement
*/
#pragma pack(1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "elf.h"
#define VER "1.07"
#define ARGV argv[arg]
#define VALUE (ARGV+2)
#define NUMBER strtoul(VALUE, NULL, 0)
typedef struct
{
uint32_t start_code; // B instruction
uint8_t logo[0xA0-0x04]; // logo data
uint8_t title[0xC]; // game title name
uint32_t game_code; //
uint16_t maker_code; //
uint8_t fixed; // 0x96
uint8_t unit_code; // 0x00
uint8_t device_type; // 0x00
uint8_t unused[7]; //
uint8_t game_version; // 0x00
uint8_t complement; // 800000A0..800000BC
uint16_t checksum; // 0x0000
} Header;
Header header;
unsigned short checksum_without_header = 0;
const Header good_header =
{
// start_code
0xEA00002E,
// logo
{ 0x24,0xFF,0xAE,0x51,0x69,0x9A,0xA2,0x21,0x3D,0x84,0x82,0x0A,0x84,0xE4,0x09,0xAD,
0x11,0x24,0x8B,0x98,0xC0,0x81,0x7F,0x21,0xA3,0x52,0xBE,0x19,0x93,0x09,0xCE,0x20,
0x10,0x46,0x4A,0x4A,0xF8,0x27,0x31,0xEC,0x58,0xC7,0xE8,0x33,0x82,0xE3,0xCE,0xBF,
0x85,0xF4,0xDF,0x94,0xCE,0x4B,0x09,0xC1,0x94,0x56,0x8A,0xC0,0x13,0x72,0xA7,0xFC,
0x9F,0x84,0x4D,0x73,0xA3,0xCA,0x9A,0x61,0x58,0x97,0xA3,0x27,0xFC,0x03,0x98,0x76,
0x23,0x1D,0xC7,0x61,0x03,0x04,0xAE,0x56,0xBF,0x38,0x84,0x00,0x40,0xA7,0x0E,0xFD,
0xFF,0x52,0xFE,0x03,0x6F,0x95,0x30,0xF1,0x97,0xFB,0xC0,0x85,0x60,0xD6,0x80,0x25,
0xA9,0x63,0xBE,0x03,0x01,0x4E,0x38,0xE2,0xF9,0xA2,0x34,0xFF,0xBB,0x3E,0x03,0x44,
0x78,0x00,0x90,0xCB,0x88,0x11,0x3A,0x94,0x65,0xC0,0x7C,0x63,0x87,0xF0,0x3C,0xAF,
0xD6,0x25,0xE4,0x8B,0x38,0x0A,0xAC,0x72,0x21,0xD4,0xF8,0x07 } ,
// title
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game code
0x00000000,
// maker code
0x3130,
// fixed
0x96,
// unit_code
0x00,
// device type
0x00,
// unused
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
// game version
0x00,
// complement
0x00,
// checksum
0x0000
};
//---------------------------------------------------------------------------------
char HeaderComplement()
/*---------------------------------------------------------------------------------
Calculate Header complement check
---------------------------------------------------------------------------------*/
{
int n;
char c = 0;
char *p = (char *)&header + 0xA0;
for (n=0; n<0xBD-0xA0; n++)
{
c += *p++;
}
return -(0x19+c);
}
//---------------------------------------------------------------------------------
int main(int argc, char *argv[])
//---------------------------------------------------------------------------------
{
int arg;
char *argfile = 0;
FILE *infile;
int silent = 0;
int schedule_pad = 0;
int size,bit;
// show syntax
if (argc <= 1)
{
printf("GBA ROM fixer v"VER" by Dark Fader / BlackThunder / WinterMute / Sierraffinity \n");
printf("Syntax: gbafix <rom.gba> [-p] [-t[title]] [-c<game_code>] [-m<maker_code>] [-r<version>] [-d<debug>] [--silent]\n");
printf("\n");
printf("parameters:\n");
printf(" -p Pad to next exact power of 2. No minimum size!\n");
printf(" -t[<title>] Patch title. Stripped filename if none given.\n");
printf(" -c<game_code> Patch game code (four characters)\n");
printf(" -m<maker_code> Patch maker code (two characters)\n");
printf(" -r<version> Patch game version (number)\n");
printf(" -d<debug> Enable debugging handler and set debug entry point (0 or 1)\n");
printf(" --silent Silence non-error output\n");
return -1;
}
// get filename
for (arg=1; arg<argc; arg++)
{
if (ARGV[0] != '-') { argfile=ARGV; }
if (strncmp("--silent", &ARGV[0], 7) == 0) { silent = 1; }
}
// check filename
if (!argfile)
{
fprintf(stderr, "Filename needed!\n");
return -1;
}
uint32_t sh_offset = 0;
// read file
infile = fopen(argfile, "r+b");
if (!infile) { fprintf(stderr, "Error opening input file!\n"); return -1; }
fseek(infile, sh_offset, SEEK_SET);
fread(&header, sizeof(header), 1, infile);
// elf check
Elf32_Shdr secHeader;
if (memcmp(&header, ELFMAG, 4) == 0) {
Elf32_Ehdr *elfHeader = (Elf32_Ehdr *)&header;
fseek(infile, elfHeader->e_shoff, SEEK_SET);
int i;
for (i = 0; i < elfHeader->e_shnum; i++) {
fread(&secHeader, sizeof(Elf32_Shdr), 1, infile);
if (secHeader.sh_type == SHT_PROGBITS && secHeader.sh_addr == elfHeader->e_entry) break;
}
if (i == elfHeader->e_shnum) { fprintf(stderr, "Error finding entry point!\n"); return 1; }
fseek(infile, secHeader.sh_offset, SEEK_SET);
sh_offset = secHeader.sh_offset;
fread(&header, sizeof(header), 1, infile);
}
// fix some data
memcpy(header.logo, good_header.logo, sizeof(header.logo));
memcpy(&header.fixed, &good_header.fixed, sizeof(header.fixed));
memcpy(&header.device_type, &good_header.device_type, sizeof(header.device_type));
// parse command line
for (arg=1; arg<argc; arg++)
{
if (ARGV[0] == '-')
{
switch (ARGV[1])
{
case 'p': // pad
{
schedule_pad = 1;
break;
}
case 't': // title
{
char title[256];
memset(title, 0, sizeof(title));
if (VALUE[0])
{
strncpy(title, VALUE, sizeof(header.title));
}
else
{
// use filename
char s[256], *begin=s, *t; strcpy(s, argfile);
t = strrchr(s, '\\'); if (t) begin = t+1;
t = strrchr(s, '/'); if (t) begin = t+1;
t = strrchr(s, '.'); if (t) *t = 0;
strncpy(title, begin, sizeof(header.title));
if (!silent) printf("%s\n",begin);
}
memcpy(header.title, title, sizeof(header.title)); // copy
break;
}
case 'c': // game code
{
//if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
//header.game_code = NUMBER;
header.game_code = VALUE[0] | VALUE[1]<<8 | VALUE[2]<<16 | VALUE[3]<<24;
break;
}
case 'm': // maker code
{
//if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
//header.maker_code = (unsigned short)NUMBER;
header.maker_code = VALUE[0] | VALUE[1]<<8;
break;
}
case 'v': // ignored, compatability with other gbafix
{
break;
}
case 'r': // version
{
if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
header.game_version = (unsigned char)NUMBER;
break;
}
case 'd': // debug
{
if (!VALUE[0]) { fprintf(stderr, "Need value for %s\n", ARGV); break; }
header.logo[0x9C-0x04] = 0xA5; // debug enable
header.device_type = (unsigned char)((NUMBER & 1) << 7); // debug handler entry point
break;
}
case '-': // long arguments
{
if (strncmp("silent", &ARGV[2], 6) == 0) { continue; }
break;
}
default:
{
printf("Invalid option: %s\n", ARGV);
}
}
}
}
// update complement check & total checksum
header.complement = 0;
header.checksum = 0; // must be 0
header.complement = HeaderComplement();
//header.checksum = checksum_without_header + HeaderChecksum();
if (schedule_pad) {
if (sh_offset != 0) {
fprintf(stderr, "Warning: Cannot safely pad an ELF\n");
} else {
fseek(infile, 0, SEEK_END);
size = ftell(infile);
for (bit=31; bit>=0; bit--) if (size & (1<<bit)) break;
if (size != (1<<bit))
{
int todo = (1<<(bit+1)) - size;
while (todo--) fputc(0xFF, infile);
}
}
}
fseek(infile, sh_offset, SEEK_SET);
fwrite(&header, sizeof(header), 1, infile);
fclose(infile);
if (!silent) printf("ROM fixed!\n");
return 0;
}

1
tools/gbagfx/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
gbagfx

19
tools/gbagfx/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2015 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

27
tools/gbagfx/Makefile Normal file
View File

@ -0,0 +1,27 @@
CC = gcc
CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
LIBS = -lpng -lz
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$(EXE)
@:
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$(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:
$(RM) gbagfx gbagfx.exe

254
tools/gbagfx/convert_png.c Normal file
View File

@ -0,0 +1,254 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <setjmp.h>
#include <png.h>
#include "global.h"
#include "convert_png.h"
#include "gfx.h"
static FILE *PngReadOpen(char *path, png_structp *pngStruct, png_infop *pngInfo)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
unsigned char sig[8];
if (fread(sig, 8, 1, fp) != 1)
FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
if (png_sig_cmp(sig, 0, 8))
FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG read struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
*pngStruct = png_ptr;
*pngInfo = info_ptr;
return fp;
}
static unsigned char *ConvertBitDepth(unsigned char *src, int srcBitDepth, int destBitDepth, int numPixels)
{
// Round the number of bits up to the next 8 and divide by 8 to get the number of bytes.
int srcSize = ((numPixels * srcBitDepth + 7) & ~7) / 8;
int destSize = ((numPixels * destBitDepth + 7) & ~7) / 8;
unsigned char *output = calloc(destSize, 1);
unsigned char *dest = output;
int i;
int j;
int destBit = 8 - destBitDepth;
for (i = 0; i < srcSize; i++)
{
unsigned char srcByte = src[i];
for (j = 8 - srcBitDepth; j >= 0; j -= srcBitDepth)
{
unsigned char pixel = (srcByte >> j) % (1 << srcBitDepth);
if (pixel >= (1 << destBitDepth))
FATAL_ERROR("Image exceeds the maximum color value for a %ibpp image.\n", destBitDepth);
*dest |= pixel << destBit;
destBit -= destBitDepth;
if (destBit < 0)
{
dest++;
destBit = 8 - destBitDepth;
}
}
}
return output;
}
void ReadPng(char *path, struct Image *image)
{
png_structp png_ptr;
png_infop info_ptr;
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
int color_type = png_get_color_type(png_ptr, info_ptr);
if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("\"%s\" has an unsupported color type.\n", path);
// Check if the image has a palette so that we can tell if the colors need to be inverted later.
// Don't read the palette because it's not needed for now.
image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
image->width = png_get_image_width(png_ptr, info_ptr);
image->height = png_get_image_height(png_ptr, info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image->pixels = malloc(image->height * rowbytes);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate pixel buffer.\n");
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error reading from \"%s\".\n", path);
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(row_pointers);
fclose(fp);
if (bit_depth != image->bitDepth && image->tilemap.data.affine == NULL)
{
unsigned char *src = image->pixels;
if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && bit_depth != 8)
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;
}
}
void ReadPngPalette(char *path, struct Palette *palette)
{
png_structp png_ptr;
png_infop info_ptr;
png_colorp colors;
int numColors;
FILE *fp = PngReadOpen(path, &png_ptr, &info_ptr);
if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("The image \"%s\" does not contain a palette.\n", path);
if (png_get_PLTE(png_ptr, info_ptr, &colors, &numColors) != PNG_INFO_PLTE)
FATAL_ERROR("Failed to retrieve palette from \"%s\".\n", path);
if (numColors > 256)
FATAL_ERROR("Images with more than 256 colors are not supported.\n");
palette->numColors = numColors;
for (int i = 0; i < numColors; i++) {
palette->colors[i].red = colors[i].red;
palette->colors[i].green = colors[i].green;
palette->colors[i].blue = colors[i].blue;
}
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
}
void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette)
{
png_colorp colors = malloc(palette->numColors * sizeof(png_color));
if (colors == NULL)
FATAL_ERROR("Failed to allocate PNG palette.\n");
for (int i = 0; i < palette->numColors; i++) {
colors[i].red = palette->colors[i].red;
colors[i].green = palette->colors[i].green;
colors[i].blue = palette->colors[i].blue;
}
png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors);
free(colors);
}
void WritePng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG write struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing header for \"%s\".\n", path);
int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
image->bitDepth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (image->hasPalette) {
SetPngPalette(png_ptr, info_ptr, &image->palette);
if (image->hasTransparency) {
png_byte trans = 0;
png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0);
}
}
png_write_info(png_ptr, info_ptr);
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing \"%s\".\n", path);
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error ending write of \"%s\".\n", path);
png_write_end(png_ptr, NULL);
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2015 YamaArashi
#ifndef CONVERT_PNG_H
#define CONVERT_PNG_H
#include "gfx.h"
void ReadPng(char *path, struct Image *image);
void WritePng(char *path, struct Image *image);
void ReadPngPalette(char *path, struct Palette *palette);
#endif // CONVERT_PNG_H

326
tools/gbagfx/font.c Normal file
View File

@ -0,0 +1,326 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "font.h"
#include "gfx.h"
#include "util.h"
unsigned char gFontPalette[][3] = {
{0x90, 0xC8, 0xFF}, // bg (saturated blue that contrasts well with the shadow color)
{0x38, 0x38, 0x38}, // fg (dark grey)
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
{0xFF, 0xFF, 0xFF} // box (white)
};
static void ConvertFromLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
unsigned int srcPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToLatinFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
unsigned int destPixelsOffset = 0;
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void ConvertFromHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8;
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToHalfwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 2; glyphTile++) {
unsigned int pixelsX = column * 8;
unsigned int destPixelsOffset = 512 * (glyphIndex >> 4) + 16 * (glyphIndex & 0xF) + 256 * glyphTile;
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + (glyphTile * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 32) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void ConvertFromFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int srcPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int destPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
srcPixelsOffset += 2;
}
}
}
}
}
static void ConvertToFullwidthJapaneseFont(unsigned char *src, unsigned char *dest, unsigned int numRows)
{
for (unsigned int row = 0; row < numRows; row++) {
for (unsigned int column = 0; column < 16; column++) {
unsigned int glyphIndex = (row * 16) + column;
for (unsigned int glyphTile = 0; glyphTile < 4; glyphTile++) {
unsigned int pixelsX = (column * 16) + ((glyphTile & 1) * 8);
unsigned int destPixelsOffset = 512 * (glyphIndex >> 3) + 32 * (glyphIndex & 7) + 256 * (glyphTile >> 1) + 16 * (glyphTile & 1);
for (unsigned int i = 0; i < 8; i++) {
unsigned int pixelsY = (row * 16) + ((glyphTile >> 1) * 8) + i;
unsigned int srcPixelsOffset = (pixelsY * 64) + (pixelsX / 4);
dest[destPixelsOffset] = src[srcPixelsOffset + 1];
dest[destPixelsOffset + 1] = src[srcPixelsOffset];
destPixelsOffset += 2;
}
}
}
}
}
static void SetFontPalette(struct Image *image)
{
image->hasPalette = true;
image->palette.numColors = 4;
for (int i = 0; i < image->palette.numColors; i++) {
image->palette.colors[i].red = gFontPalette[i][0];
image->palette.colors[i].green = gFontPalette[i][1];
image->palette.colors[i].blue = gFontPalette[i][2];
}
image->hasTransparency = false;
}
void ReadLatinFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 256;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromLatinFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteLatinFont(char *path, struct Image *image)
{
if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToLatinFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void ReadHalfwidthJapaneseFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int glyphSize = 32;
if (fileSize % glyphSize != 0)
FATAL_ERROR("The file size (%d) is not a multiple of %d.\n", fileSize, glyphSize);
int numGlyphs = fileSize / glyphSize;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 128;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromHalfwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteHalfwidthJapaneseFont(char *path, struct Image *image)
{
if (image->width != 128)
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 32;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToHalfwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
void ReadFullwidthJapaneseFont(char *path, struct Image *image)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int numGlyphs = fileSize / 64;
if (numGlyphs % 16 != 0)
FATAL_ERROR("The number of glyphs (%d) is not a multiple of 16.\n", numGlyphs);
int numRows = numGlyphs / 16;
image->width = 256;
image->height = numRows * 16;
image->bitDepth = 2;
image->pixels = malloc(fileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertFromFullwidthJapaneseFont(buffer, image->pixels, numRows);
free(buffer);
SetFontPalette(image);
}
void WriteFullwidthJapaneseFont(char *path, struct Image *image)
{
if (image->width != 256)
FATAL_ERROR("The width of the font image (%d) is not 256.\n", image->width);
if (image->height % 16 != 0)
FATAL_ERROR("The height of the font image (%d) is not a multiple of 16.\n", image->height);
int numRows = image->height / 16;
int bufferSize = numRows * 16 * 64;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
ConvertToFullwidthJapaneseFont(image->pixels, buffer, numRows);
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}

16
tools/gbagfx/font.h Normal file
View File

@ -0,0 +1,16 @@
// Copyright (c) 2015 YamaArashi
#ifndef FONT_H
#define FONT_H
#include <stdbool.h>
#include "gfx.h"
void ReadLatinFont(char *path, struct Image *image);
void WriteLatinFont(char *path, struct Image *image);
void ReadHalfwidthJapaneseFont(char *path, struct Image *image);
void WriteHalfwidthJapaneseFont(char *path, struct Image *image);
void ReadFullwidthJapaneseFont(char *path, struct Image *image);
void WriteFullwidthJapaneseFont(char *path, struct Image *image);
#endif // FONT_H

507
tools/gbagfx/gfx.c Normal file
View File

@ -0,0 +1,507 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
#define GET_GBA_PAL_RED(x) (((x) >> 0) & 0x1F)
#define GET_GBA_PAL_GREEN(x) (((x) >> 5) & 0x1F)
#define GET_GBA_PAL_BLUE(x) (((x) >> 10) & 0x1F)
#define SET_GBA_PAL(r, g, b) (((b) << 10) | ((g) << 5) | (r))
#define UPCONVERT_BIT_DEPTH(x) (((x) * 255) / 31)
#define DOWNCONVERT_BIT_DEPTH(x) ((x) / 8)
static void AdvanceMetatilePosition(int *subTileX, int *subTileY, int *metatileX, int *metatileY, int metatilesWide, int metatileWidth, int metatileHeight)
{
(*subTileX)++;
if (*subTileX == metatileWidth) {
*subTileX = 0;
(*subTileY)++;
if (*subTileY == metatileHeight) {
*subTileY = 0;
(*metatileX)++;
if (*metatileX == metatilesWide) {
*metatileX = 0;
(*metatileY)++;
}
}
}
}
static void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = metatilesWide * metatileWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
int destX = metatileX * metatileWidth + subTileX;
unsigned char srcPixelOctet = *src++;
unsigned char *destPixelOctet = &dest[destY * pitch + destX];
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 4; k++) {
int destX = (metatileX * metatileWidth + subTileX) * 4 + k;
unsigned char srcPixelPair = *src++;
unsigned char leftPixel = srcPixelPair & 0xF;
unsigned char rightPixel = srcPixelPair >> 4;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
dest[destY * pitch + destX] = (leftPixel << 4) | rightPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertFromTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int destY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 8; k++) {
int destX = (metatileX * metatileWidth + subTileX) * 8 + k;
unsigned char srcPixel = *src++;
if (invertColors)
srcPixel = 255 - srcPixel;
dest[destY * pitch + destX] = srcPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = metatilesWide * metatileWidth;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
int srcX = metatileX * metatileWidth + subTileX;
unsigned char srcPixelOctet = src[srcY * pitch + srcX];
unsigned char *destPixelOctet = dest++;
for (int k = 0; k < 8; k++) {
*destPixelOctet <<= 1;
*destPixelOctet |= (srcPixelOctet & 1) ^ invertColors;
srcPixelOctet >>= 1;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 4;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 4; k++) {
int srcX = (metatileX * metatileWidth + subTileX) * 4 + k;
unsigned char srcPixelPair = src[srcY * pitch + srcX];
unsigned char leftPixel = srcPixelPair >> 4;
unsigned char rightPixel = srcPixelPair & 0xF;
if (invertColors) {
leftPixel = 15 - leftPixel;
rightPixel = 15 - rightPixel;
}
*dest++ = (rightPixel << 4) | leftPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
static void ConvertToTiles8Bpp(unsigned char *src, unsigned char *dest, int numTiles, int metatilesWide, int metatileWidth, int metatileHeight, bool invertColors)
{
int subTileX = 0;
int subTileY = 0;
int metatileX = 0;
int metatileY = 0;
int pitch = (metatilesWide * metatileWidth) * 8;
for (int i = 0; i < numTiles; i++) {
for (int j = 0; j < 8; j++) {
int srcY = (metatileY * metatileHeight + subTileY) * 8 + j;
for (int k = 0; k < 8; k++) {
int srcX = (metatileX * metatileWidth + subTileX) * 8 + k;
unsigned char srcPixel = src[srcY * pitch + srcX];
if (invertColors)
srcPixel = 255 - srcPixel;
*dest++ = srcPixel;
}
}
AdvanceMetatilePosition(&subTileX, &subTileY, &metatileX, &metatileY, metatilesWide, metatileWidth, metatileHeight);
}
}
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;
int fileSize;
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;
if (tilesWidth % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", 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);
image->width = tilesWidth * 8;
image->height = tilesHeight * 8;
image->bitDepth = bitDepth;
image->pixels = calloc(tilesWidth * tilesHeight, tileSize);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
int metatilesWide = tilesWidth / metatileWidth;
switch (bitDepth) {
case 1:
ConvertFromTiles1Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 4:
ConvertFromTiles4Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 8:
ConvertFromTiles8Bpp(buffer, image->pixels, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
}
free(buffer);
}
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors)
{
int tileSize = bitDepth * 8;
if (image->width % 8 != 0)
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
if (image->height % 8 != 0)
FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height);
int tilesWidth = image->width / 8;
int tilesHeight = image->height / 8;
if (tilesWidth % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", 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);
int maxNumTiles = tilesWidth * tilesHeight;
if (numTiles == 0)
numTiles = maxNumTiles;
else if (numTiles > maxNumTiles)
FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles);
int bufferSize = numTiles * tileSize;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
int metatilesWide = tilesWidth / metatileWidth;
switch (bitDepth) {
case 1:
ConvertToTiles1Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 4:
ConvertToTiles4Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
case 8:
ConvertToTiles8Bpp(image->pixels, buffer, numTiles, metatilesWide, metatileWidth, metatileHeight, invertColors);
break;
}
WriteWholeFile(path, buffer, bufferSize);
free(buffer);
}
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;
}
void ReadGbaPalette(char *path, struct Palette *palette)
{
int fileSize;
unsigned char *data = ReadWholeFile(path, &fileSize);
if (fileSize % 2 != 0)
FATAL_ERROR("The file size (%d) is not a multiple of 2.\n", fileSize);
palette->numColors = fileSize / 2;
for (int i = 0; i < palette->numColors; i++) {
uint16_t paletteEntry = (data[i * 2 + 1] << 8) | data[i * 2];
palette->colors[i].red = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_RED(paletteEntry));
palette->colors[i].green = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_GREEN(paletteEntry));
palette->colors[i].blue = UPCONVERT_BIT_DEPTH(GET_GBA_PAL_BLUE(paletteEntry));
}
// 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);
}
void WriteGbaPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
for (int i = 0; i < palette->numColors; i++) {
unsigned char red = DOWNCONVERT_BIT_DEPTH(palette->colors[i].red);
unsigned char green = DOWNCONVERT_BIT_DEPTH(palette->colors[i].green);
unsigned char blue = DOWNCONVERT_BIT_DEPTH(palette->colors[i].blue);
uint16_t paletteEntry = SET_GBA_PAL(red, green, blue);
fputc(paletteEntry & 0xFF, fp);
fputc(paletteEntry >> 8, fp);
}
fclose(fp);
}

53
tools/gbagfx/gfx.h Normal file
View File

@ -0,0 +1,53 @@
// Copyright (c) 2015 YamaArashi
#ifndef GFX_H
#define GFX_H
#include <stdint.h>
#include <stdbool.h>
struct Color {
unsigned char red;
unsigned char green;
unsigned char blue;
};
struct Palette {
struct Color colors[256];
int numColors;
};
struct 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;
int bitDepth;
unsigned char *pixels;
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);
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void WriteGbaPalette(char *path, struct Palette *palette);
#endif // GFX_H

31
tools/gbagfx/global.h Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2015 YamaArashi
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED
#else
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED __attribute__((__unused__))
#endif // _MSC_VER
#endif // GLOBAL_H

398
tools/gbagfx/huff.c Normal file
View File

@ -0,0 +1,398 @@
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include "global.h"
#include "huff.h"
static int cmp_tree(const void * a0, const void * b0) {
return ((struct HuffData *)a0)->value - ((struct HuffData *)b0)->value;
}
typedef int (*cmpfun)(const void *, const void *);
int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) {
/*
* Out-of-place mergesort (stable sort)
* Returns 1 on success, 0 on failure
*/
void * leftPtr;
void * rightPtr;
void * leftEnd;
void * rightEnd;
int i;
switch (count) {
case 0:
// Should never be here
return 0;
case 1:
// Nothing to do here
break;
case 2:
// Swap the two entries if the right one compares higher.
if (cmp(data, data + size) > 0) {
memcpy(buffer, data, size);
memcpy(data, data + size, size);
memcpy(data + size, buffer, size);
}
break;
default:
// Merge sort out-of-place.
leftPtr = data;
leftEnd = rightPtr = data + count / 2 * size;
rightEnd = data + count * size;
// Sort the left half
if (!msort_r(leftPtr, count / 2, size, cmp, buffer))
return 0;
// Sort the right half
if (!msort_r(rightPtr, count / 2 + (count & 1), size, cmp, buffer))
return 0;
// Merge the sorted halves out of place
i = 0;
do {
if (cmp(leftPtr, rightPtr) <= 0) {
memcpy(buffer + i * size, leftPtr, size);
leftPtr += size;
} else {
memcpy(buffer + i * size, rightPtr, size);
rightPtr += size;
}
} while (++i < count && leftPtr < leftEnd && rightPtr < rightEnd);
// Copy the remainder
if (i < count) {
if (leftPtr < leftEnd) {
memcpy(buffer + i * size, leftPtr, leftEnd - leftPtr);
}
else {
memcpy(buffer + i * size, rightPtr, rightEnd - rightPtr);
}
}
// Copy the merged data back
memcpy(data, buffer, count * size);
break;
}
return 1;
}
int msort(void * data, size_t count, size_t size, cmpfun cmp) {
void * buffer = malloc(count * size);
if (buffer == NULL) return 0;
int result = msort_r(data, count, size, cmp, buffer);
free(buffer);
return result;
}
static void write_tree(unsigned char * dest, HuffNode_t * tree, int nitems, struct BitEncoding * encoding) {
/*
* The example used to guide this function encodes the tree in a
* breadth-first manner. We attempt to emulate that here.
*/
int i, j, k;
// There are (2 * nitems - 1) nodes in the binary tree. Allocate that.
HuffNode_t * traversal = calloc(2 * nitems - 1, sizeof(HuffNode_t));
if (traversal == NULL)
FATAL_ERROR("Fatal error while compressing Huff file.\n");
// The first node is the root of the tree.
traversal[0] = *tree;
i = 1;
// Copy the tree into a breadth-first ordering using brute force.
for (int depth = 1; i < 2 * nitems - 1; depth++) {
// Consider every possible path up to the current depth.
for (j = 0; i < 2 * nitems - 1 && j < 1 << depth; j++) {
// The index of the path is used to encode the path itself.
// Start from the most significant relevant bit and work our way down.
// Keep track of the current and previous nodes.
HuffNode_t * currNode = traversal;
HuffNode_t * parent = NULL;
for (k = 0; k < depth; k++) {
if (currNode->header.isLeaf)
break;
parent = currNode;
if ((j >> (depth - k - 1)) & 1)
currNode = currNode->branch.right;
else
currNode = currNode->branch.left;
}
// Check that the length of the current path equals the current depth.
if (k == depth) {
// Make sure we can encode the current branch.
// Bail here if we cannot.
// This is only applicable for 8-bit encodings.
if (traversal + i - parent > 128)
FATAL_ERROR("Fatal error while compressing Huff file: unable to encode binary tree.\n");
// Copy the current node, and update its parent.
traversal[i] = *currNode;
if (parent != NULL) {
if ((j & 1) == 1)
parent->branch.right = traversal + i;
else
parent->branch.left = traversal + i;
}
// Encode the path through the tree in the lookup table
if (traversal[i].header.isLeaf) {
encoding[traversal[i].leaf.key].nbits = depth;
encoding[traversal[i].leaf.key].bitstring = j;
}
i++;
}
}
}
// Encode the size of the tree.
// This is used by the decompressor to skip the tree.
dest[4] = nitems - 1;
// Encode each node in the tree.
for (i = 0; i < 2 * nitems - 1; i++) {
HuffNode_t * currNode = traversal + i;
if (currNode->header.isLeaf) {
dest[5 + i] = traversal[i].leaf.key;
} else {
dest[5 + i] = (((currNode->branch.right - traversal - i) / 2) - 1);
if (currNode->branch.left->header.isLeaf)
dest[5 + i] |= 0x80;
if (currNode->branch.right->header.isLeaf)
dest[5 + i] |= 0x40;
}
}
free(traversal);
}
static inline void write_32_le(unsigned char * dest, int * destPos, uint32_t * buff, int * buffPos) {
dest[*destPos] = *buff;
dest[*destPos + 1] = *buff >> 8;
dest[*destPos + 2] = *buff >> 16;
dest[*destPos + 3] = *buff >> 24;
*destPos += 4;
*buff = 0;
*buffPos = 0;
}
static inline void read_32_le(unsigned char * src, int * srcPos, uint32_t * buff) {
uint32_t tmp = src[*srcPos];
tmp |= src[*srcPos + 1] << 8;
tmp |= src[*srcPos + 2] << 16;
tmp |= src[*srcPos + 3] << 24;
*srcPos += 4;
*buff = tmp;
}
static void write_bits(unsigned char * dest, int * destPos, struct BitEncoding * encoding, int value, uint32_t * buff, int * buffBits) {
int nbits = encoding[value].nbits;
uint32_t bitstring = encoding[value].bitstring;
if (*buffBits + nbits >= 32) {
int diff = *buffBits + nbits - 32;
*buff <<= nbits - diff;
*buff |= bitstring >> diff;
bitstring &= ~(1 << diff);
nbits = diff;
write_32_le(dest, destPos, buff, buffBits);
}
if (nbits != 0) {
*buff <<= nbits;
*buff |= bitstring;
*buffBits += nbits;
}
}
/*
=======================================
MAIN COMPRESSION/DECOMPRESSION ROUTINES
=======================================
*/
unsigned char * HuffCompress(unsigned char * src, int srcSize, int * compressedSize_p, int bitDepth) {
if (srcSize <= 0)
goto fail;
int worstCaseDestSize = 4 + (2 << bitDepth) + srcSize * 3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
goto fail;
int nitems = 1 << bitDepth;
HuffNode_t * freqs = calloc(nitems, sizeof(HuffNode_t));
if (freqs == NULL)
goto fail;
struct BitEncoding * encoding = calloc(nitems, sizeof(struct BitEncoding));
if (encoding == NULL)
goto fail;
// Set up the frequencies table. This will inform the tree.
for (int i = 0; i < nitems; i++) {
freqs[i].header.isLeaf = 1;
freqs[i].header.value = 0;
freqs[i].leaf.key = i;
}
// Count each nybble or byte.
for (int i = 0; i < srcSize; i++) {
if (bitDepth == 8) {
freqs[src[i]].header.value++;
} else {
freqs[src[i] >> 4].header.value++;
freqs[src[i] & 0xF].header.value++;
}
}
#ifdef DEBUG
for (int i = 0; i < nitems; i++) {
fprintf(stderr, "%d: %d\n", i, freqs[i].header.value);
}
#endif // DEBUG
// Sort the frequency table.
if (!msort(freqs, nitems, sizeof(HuffNode_t), cmp_tree))
goto fail;
// Prune zero-frequency values.
for (int i = 0; i < nitems; i++) {
if (freqs[i].header.value != 0) {
if (i > 0) {
for (int j = i; j < nitems; j++) {
freqs[j - i] = freqs[j];
}
nitems -= i;
}
break;
}
// This should never happen:
if (i == nitems - 1)
goto fail;
}
HuffNode_t * tree = calloc(nitems * 2 - 1, sizeof(HuffNode_t));
if (tree == NULL)
goto fail;
// Iteratively collapse the two least frequent nodes.
HuffNode_t * endptr = freqs + nitems - 2;
for (int i = 0; i < nitems - 1; i++) {
HuffNode_t * left = freqs;
HuffNode_t * right = freqs + 1;
tree[i * 2] = *right;
tree[i * 2 + 1] = *left;
for (int j = 0; j < nitems - i - 2; j++)
freqs[j] = freqs[j + 2];
endptr->header.isLeaf = 0;
endptr->header.value = tree[i * 2].header.value + tree[i * 2 + 1].header.value;
endptr->branch.left = tree + i * 2;
endptr->branch.right = tree + i * 2 + 1;
endptr--;
if (i < nitems - 2 && !msort(freqs, nitems - i - 1, sizeof(HuffNode_t), cmp_tree))
goto fail;
}
// Write the tree breadth-first, and create the path lookup table.
write_tree(dest, freqs, nitems, encoding);
free(tree);
free(freqs);
// Encode the data itself.
int destPos = 4 + nitems * 2;
uint32_t destBuf = 0;
uint32_t srcBuf = 0;
int destBitPos = 0;
for (int srcPos = 0; srcPos < srcSize;) {
read_32_le(src, &srcPos, &srcBuf);
for (int i = 0; i < 32 / bitDepth; i++) {
write_bits(dest, &destPos, encoding, srcBuf & (0xFF >> (8 - bitDepth)), &destBuf, &destBitPos);
srcBuf >>= bitDepth;
}
}
if (destBitPos != 0) {
write_32_le(dest, &destPos, &destBuf, &destBitPos);
}
free(encoding);
// Write the header.
dest[0] = bitDepth | 0x20;
dest[1] = srcSize;
dest[2] = srcSize >> 8;
dest[3] = srcSize >> 16;
*compressedSize_p = (destPos + 3) & ~3;
return dest;
fail:
FATAL_ERROR("Fatal error while compressing Huff file.\n");
}
unsigned char * HuffDecompress(unsigned char * src, int srcSize, int * uncompressedSize_p) {
if (srcSize < 4)
goto fail;
int bitDepth = *src & 15;
if (bitDepth != 4 && bitDepth != 8)
goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
goto fail;
int treePos = 5;
int treeSize = (src[4] + 1) * 2;
int srcPos = 4 + treeSize;
int destPos = 0;
int curValPos = 0;
uint32_t destTmp = 0;
uint32_t window;
for (;;)
{
if (srcPos >= srcSize)
goto fail;
read_32_le(src, &srcPos, &window);
for (int i = 0; i < 32; i++) {
int curBit = (window >> 31) & 1;
unsigned char treeView = src[treePos];
bool isLeaf = ((treeView << curBit) & 0x80) != 0;
treePos &= ~1; // align
treePos += ((treeView & 0x3F) + 1) * 2 + curBit;
if (isLeaf) {
destTmp >>= bitDepth;
destTmp |= (src[treePos] << (32 - bitDepth));
curValPos++;
if (curValPos == 32 / bitDepth) {
write_32_le(dest, &destPos, &destTmp, &curValPos);
if (destPos == destSize) {
*uncompressedSize_p = destSize;
return dest;
}
}
treePos = 5;
}
window <<= 1;
}
}
fail:
FATAL_ERROR("Fatal error while decompressing Huff file.\n");
}

38
tools/gbagfx/huff.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef HUFF_H
#define HUFF_H
union HuffNode;
struct HuffData {
unsigned value:31;
unsigned isLeaf:1;
};
struct HuffLeaf {
struct HuffData header;
unsigned char key;
};
struct HuffBranch {
struct HuffData header;
union HuffNode * left;
union HuffNode * right;
};
union HuffNode {
struct HuffData header;
struct HuffLeaf leaf;
struct HuffBranch branch;
};
typedef union HuffNode HuffNode_t;
struct BitEncoding {
unsigned long long nbits:6;
unsigned long long bitstring:58;
};
unsigned char * HuffCompress(unsigned char * buffer, int srcSize, int * compressedSize_p, int bitDepth);
unsigned char * HuffDecompress(unsigned char * buffer, int srcSize, int * uncompressedSize_p);
#endif //HUFF_H

172
tools/gbagfx/jasc_pal.c Normal file
View File

@ -0,0 +1,172 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "gfx.h"
#include "util.h"
// Read/write Paint Shop Pro palette files.
// Format of a Paint Shop Pro palette file, line by line:
// "JASC-PAL\r\n" (signature)
// "0100\r\n" (version; seems to always be "0100")
// "<NUMBER_OF_COLORS>\r\n" (number of colors in decimal)
//
// <NUMBER_OF_COLORS> times:
// "<RED> <GREEN> <BLUE>\r\n" (color entry)
//
// Each color component is a decimal number from 0 to 255.
// Examples:
// Black - "0 0 0\r\n"
// Blue - "0 0 255\r\n"
// Brown - "150 75 0\r\n"
#define MAX_LINE_LENGTH 11
void ReadJascPaletteLine(FILE *fp, char *line)
{
int c;
int length = 0;
for (;;)
{
c = fgetc(fp);
if (c == '\r')
{
c = fgetc(fp);
if (c != '\n')
FATAL_ERROR("CR line endings aren't supported.\n");
line[length] = 0;
return;
}
if (c == '\n')
FATAL_ERROR("LF line endings aren't supported.\n");
if (c == EOF)
FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n");
if (c == 0)
FATAL_ERROR("NUL character in file.\n");
if (length == MAX_LINE_LENGTH)
{
line[length] = 0;
FATAL_ERROR("The line \"%s\" is too long.\n", line);
}
line[length++] = c;
}
}
void ReadJascPalette(char *path, struct Palette *palette)
{
char line[MAX_LINE_LENGTH + 1];
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open JASC-PAL file \"%s\" for reading.\n", path);
ReadJascPaletteLine(fp, line);
if (strcmp(line, "JASC-PAL") != 0)
FATAL_ERROR("Invalid JASC-PAL signature.\n");
ReadJascPaletteLine(fp, line);
if (strcmp(line, "0100") != 0)
FATAL_ERROR("Unsuported JASC-PAL version.\n");
ReadJascPaletteLine(fp, line);
if (!ParseNumber(line, NULL, 10, &palette->numColors))
FATAL_ERROR("Failed to parse number of colors.\n");
if (palette->numColors < 1 || palette->numColors > 256)
FATAL_ERROR("%d is an invalid number of colors. The number of colors must be in the range [1, 256].\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++)
{
ReadJascPaletteLine(fp, line);
char *s = line;
char *end;
int red;
int green;
int blue;
if (!ParseNumber(s, &end, 10, &red))
FATAL_ERROR("Failed to parse red color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after red color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between red and green color components.\n");
if (!ParseNumber(s, &end, 10, &green))
FATAL_ERROR("Failed to parse green color component.\n");
s = end;
if (*s != ' ')
FATAL_ERROR("Expected a space after green color component.\n");
s++;
if (*s < '0' || *s > '9')
FATAL_ERROR("Expected only a space between green and blue color components.\n");
if (!ParseNumber(s, &end, 10, &blue))
FATAL_ERROR("Failed to parse blue color component.\n");
if (*end != 0)
FATAL_ERROR("Garbage after blue color component.\n");
if (red < 0 || red > 255)
FATAL_ERROR("Red color component (%d) is outside the range [0, 255].\n", red);
if (green < 0 || green > 255)
FATAL_ERROR("Green color component (%d) is outside the range [0, 255].\n", green);
if (blue < 0 || blue > 255)
FATAL_ERROR("Blue color component (%d) is outside the range [0, 255].\n", blue);
palette->colors[i].red = red;
palette->colors[i].green = green;
palette->colors[i].blue = blue;
}
if (fgetc(fp) != EOF)
FATAL_ERROR("Garbage after color data.\n");
fclose(fp);
}
void WriteJascPalette(char *path, struct Palette *palette)
{
FILE *fp = fopen(path, "wb");
fputs("JASC-PAL\r\n", fp);
fputs("0100\r\n", fp);
fprintf(fp, "%d\r\n", palette->numColors);
for (int i = 0; i < palette->numColors; i++)
{
struct Color *color = &palette->colors[i];
fprintf(fp, "%d %d %d\r\n", color->red, color->green, color->blue);
}
fclose(fp);
}

9
tools/gbagfx/jasc_pal.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef JASC_PAL_H
#define JASC_PAL_H
void ReadJascPalette(char *path, struct Palette *palette);
void WriteJascPalette(char *path, struct Palette *palette);
#endif // JASC_PAL_H

153
tools/gbagfx/lz.c Normal file
View File

@ -0,0 +1,153 @@
// Copyright (c) 2015 YamaArashi
#include <stdlib.h>
#include <stdbool.h>
#include "global.h"
#include "lz.h"
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{
if (srcSize < 4)
goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
goto fail;
int srcPos = 4;
int destPos = 0;
for (;;) {
if (srcPos >= srcSize)
goto fail;
unsigned char flags = src[srcPos++];
for (int i = 0; i < 8; i++) {
if (flags & 0x80) {
if (srcPos + 1 >= srcSize)
goto fail;
int blockSize = (src[srcPos] >> 4) + 3;
int blockDistance = (((src[srcPos] & 0xF) << 8) | src[srcPos + 1]) + 1;
srcPos += 2;
int blockPos = destPos - blockDistance;
// Some Ruby/Sapphire tilesets overflow.
if (destPos + blockSize > destSize) {
blockSize = destSize - destPos;
fprintf(stderr, "Destination buffer overflow.\n");
}
if (blockPos < 0)
goto fail;
for (int j = 0; j < blockSize; j++)
dest[destPos++] = dest[blockPos + j];
} else {
if (srcPos >= srcSize || destPos >= destSize)
goto fail;
dest[destPos++] = src[srcPos++];
}
if (destPos == destSize) {
*uncompressedSize = destSize;
return dest;
}
flags <<= 1;
}
}
fail:
FATAL_ERROR("Fatal error while decompressing LZ file.\n");
}
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance)
{
if (srcSize <= 0)
goto fail;
int worstCaseDestSize = 4 + srcSize + ((srcSize + 7) / 8);
// Round up to the next multiple of four.
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
goto fail;
// header
dest[0] = 0x10; // LZ compression type
dest[1] = (unsigned char)srcSize;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
for (;;) {
unsigned char *flags = &dest[destPos++];
*flags = 0;
for (int i = 0; i < 8; i++) {
int bestBlockDistance = 0;
int bestBlockSize = 0;
int blockDistance = minDistance;
while (blockDistance <= srcPos && blockDistance <= 0x1000) {
int blockStart = srcPos - blockDistance;
int blockSize = 0;
while (blockSize < 18
&& srcPos + blockSize < srcSize
&& src[blockStart + blockSize] == src[srcPos + blockSize])
blockSize++;
if (blockSize > bestBlockSize) {
bestBlockDistance = blockDistance;
bestBlockSize = blockSize;
if (blockSize == 18)
break;
}
blockDistance++;
}
if (bestBlockSize >= 3) {
*flags |= (0x80 >> i);
srcPos += bestBlockSize;
bestBlockSize -= 3;
bestBlockDistance--;
dest[destPos++] = (bestBlockSize << 4) | ((unsigned int)bestBlockDistance >> 8);
dest[destPos++] = (unsigned char)bestBlockDistance;
} else {
dest[destPos++] = src[srcPos++];
}
if (srcPos == srcSize) {
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0) {
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
}
fail:
FATAL_ERROR("Fatal error while compressing LZ file.\n");
}

9
tools/gbagfx/lz.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2015 YamaArashi
#ifndef LZ_H
#define LZ_H
unsigned char *LZDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *LZCompress(unsigned char *src, int srcSize, int *compressedSize, const int minDistance);
#endif // LZ_H

626
tools/gbagfx/main.c Normal file
View File

@ -0,0 +1,626 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "global.h"
#include "util.h"
#include "options.h"
#include "gfx.h"
#include "convert_png.h"
#include "jasc_pal.h"
#include "lz.h"
#include "rl.h"
#include "font.h"
#include "huff.h"
struct CommandHandler
{
const char *inputFileExtension;
const char *outputFileExtension;
void(*function)(char *inputPath, char *outputPath, int argc, char **argv);
};
void ConvertGbaToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *options)
{
struct Image image;
if (options->paletteFilePath != NULL)
{
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
{
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;
WritePng(outputPath, &image);
FreeImage(&image);
}
void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *options)
{
struct Image image;
image.bitDepth = options->bitDepth;
image.tilemap.data.affine = NULL; // initialize to NULL to avoid issues in FreeImage
ReadPng(inputPath, &image);
WriteImage(outputPath, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette);
FreeImage(&image);
}
void HandleGbaToPngCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
char *inputFileExtension = GetFileExtensionAfterDot(inputPath);
struct GbaToPngOptions options;
options.paletteFilePath = NULL;
options.bitDepth = inputFileExtension[0] - '0';
options.hasTransparency = false;
options.width = 1;
options.metatileWidth = 1;
options.metatileHeight = 1;
options.tilemapFilePath = NULL;
options.isAffineMap = false;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-palette") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No palette file path following \"-palette\".\n");
i++;
options.paletteFilePath = argv[i];
}
else if (strcmp(option, "-object") == 0)
{
options.hasTransparency = true;
}
else if (strcmp(option, "-width") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No width following \"-width\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.width))
FATAL_ERROR("Failed to parse width.\n");
if (options.width < 1)
FATAL_ERROR("Width must be positive.\n");
}
else if (strcmp(option, "-mwidth") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile width value following \"-mwidth\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileWidth))
FATAL_ERROR("Failed to parse metatile width.\n");
if (options.metatileWidth < 1)
FATAL_ERROR("metatile width must be positive.\n");
}
else if (strcmp(option, "-mheight") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile height value following \"-mheight\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileHeight))
FATAL_ERROR("Failed to parse metatile height.\n");
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);
}
}
if (options.metatileWidth > options.width)
options.width = options.metatileWidth;
ConvertGbaToPng(inputPath, outputPath, &options);
}
void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
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++)
{
char *option = argv[i];
if (strcmp(option, "-num_tiles") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No number of tiles following \"-num_tiles\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.numTiles))
FATAL_ERROR("Failed to parse number of tiles.\n");
if (options.numTiles < 1)
FATAL_ERROR("Number of tiles must be positive.\n");
}
else if (strcmp(option, "-mwidth") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile width value following \"-mwidth\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileWidth))
FATAL_ERROR("Failed to parse metatile width.\n");
if (options.metatileWidth < 1)
FATAL_ERROR("metatile width must be positive.\n");
}
else if (strcmp(option, "-mheight") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No metatile height value following \"-mheight\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &options.metatileHeight))
FATAL_ERROR("Failed to parse metatile height.\n");
if (options.metatileHeight < 1)
FATAL_ERROR("metatile height must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
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 = {};
ReadPngPalette(inputPath, &palette);
WriteGbaPalette(outputPath, &palette);
}
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
ReadGbaPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
}
void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int numColors = 0;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-num_colors") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No number of colors following \"-num_colors\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &numColors))
FATAL_ERROR("Failed to parse number of colors.\n");
if (numColors < 1)
FATAL_ERROR("Number of colors must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
struct Palette palette = {};
ReadJascPalette(inputPath, &palette);
if (numColors != 0)
palette.numColors = numColors;
WriteGbaPalette(outputPath, &palette);
}
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);
FreeImage(&image);
}
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;
ReadPng(inputPath, &image);
WriteLatinFont(outputPath, &image);
FreeImage(&image);
}
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);
FreeImage(&image);
}
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;
ReadPng(inputPath, &image);
WriteHalfwidthJapaneseFont(outputPath, &image);
FreeImage(&image);
}
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);
FreeImage(&image);
}
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;
ReadPng(inputPath, &image);
WriteFullwidthJapaneseFont(outputPath, &image);
FreeImage(&image);
}
void HandleLZCompressCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int overflowSize = 0;
int minDistance = 2; // default, for compatibility with LZ77UnCompVram()
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-overflow") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-overflow\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &overflowSize))
FATAL_ERROR("Failed to parse overflow size.\n");
if (overflowSize < 1)
FATAL_ERROR("Overflow size must be positive.\n");
}
else if (strcmp(option, "-search") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-overflow\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &minDistance))
FATAL_ERROR("Failed to parse LZ min search distance.\n");
if (minDistance < 1)
FATAL_ERROR("LZ min search distance must be positive.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
// The overflow option allows a quirk in some of Ruby/Sapphire's tilesets
// to be reproduced. It works by appending a number of zeros to the data
// before compressing it and then amending the LZ header's size field to
// reflect the expected size. This will cause an overflow when decompressing
// the data.
int fileSize;
unsigned char *buffer = ReadWholeFileZeroPadded(inputPath, &fileSize, overflowSize);
int compressedSize;
unsigned char *compressedData = LZCompress(buffer, fileSize + overflowSize, &compressedSize, minDistance);
compressedData[1] = (unsigned char)fileSize;
compressedData[2] = (unsigned char)(fileSize >> 8);
compressedData[3] = (unsigned char)(fileSize >> 16);
free(buffer);
WriteWholeFile(outputPath, compressedData, compressedSize);
free(compressedData);
}
void HandleLZDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = LZDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
WriteWholeFile(outputPath, uncompressedData, uncompressedSize);
free(uncompressedData);
}
void HandleRLCompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int compressedSize;
unsigned char *compressedData = RLCompress(buffer, fileSize, &compressedSize);
free(buffer);
WriteWholeFile(outputPath, compressedData, compressedSize);
free(compressedData);
}
void HandleRLDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = RLDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
WriteWholeFile(outputPath, uncompressedData, uncompressedSize);
free(uncompressedData);
}
void HandleHuffCompressCommand(char *inputPath, char *outputPath, int argc, char **argv)
{
int fileSize;
int bitDepth = 4;
for (int i = 3; i < argc; i++)
{
char *option = argv[i];
if (strcmp(option, "-depth") == 0)
{
if (i + 1 >= argc)
FATAL_ERROR("No size following \"-depth\".\n");
i++;
if (!ParseNumber(argv[i], NULL, 10, &bitDepth))
FATAL_ERROR("Failed to parse bit depth.\n");
if (bitDepth != 4 && bitDepth != 8)
FATAL_ERROR("GBA only supports bit depth of 4 or 8.\n");
}
else
{
FATAL_ERROR("Unrecognized option \"%s\".\n", option);
}
}
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int compressedSize;
unsigned char *compressedData = HuffCompress(buffer, fileSize, &compressedSize, bitDepth);
free(buffer);
WriteWholeFile(outputPath, compressedData, compressedSize);
free(compressedData);
}
void HandleHuffDecompressCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(inputPath, &fileSize);
int uncompressedSize;
unsigned char *uncompressedData = HuffDecompress(buffer, fileSize, &uncompressedSize);
free(buffer);
WriteWholeFile(outputPath, uncompressedData, uncompressedSize);
free(uncompressedData);
}
int main(int argc, char **argv)
{
char converted = 0;
if (argc < 3)
FATAL_ERROR("Usage: gbagfx INPUT_PATH OUTPUT_PATH [options...]\n");
struct CommandHandler handlers[] =
{
{ "1bpp", "png", HandleGbaToPngCommand },
{ "4bpp", "png", HandleGbaToPngCommand },
{ "8bpp", "png", HandleGbaToPngCommand },
{ "png", "1bpp", HandlePngToGbaCommand },
{ "png", "4bpp", HandlePngToGbaCommand },
{ "png", "8bpp", HandlePngToGbaCommand },
{ "png", "gbapal", HandlePngToGbaPaletteCommand },
{ "png", "pal", HandlePngToJascPaletteCommand },
{ "gbapal", "pal", HandleGbaToJascPaletteCommand },
{ "pal", "gbapal", HandleJascToGbaPaletteCommand },
{ "latfont", "png", HandleLatinFontToPngCommand },
{ "png", "latfont", HandlePngToLatinFontCommand },
{ "hwjpnfont", "png", HandleHalfwidthJapaneseFontToPngCommand },
{ "png", "hwjpnfont", HandlePngToHalfwidthJapaneseFontCommand },
{ "fwjpnfont", "png", HandleFullwidthJapaneseFontToPngCommand },
{ "png", "fwjpnfont", HandlePngToFullwidthJapaneseFontCommand },
{ NULL, "huff", HandleHuffCompressCommand },
{ NULL, "lz", HandleLZCompressCommand },
{ "huff", NULL, HandleHuffDecompressCommand },
{ "lz", NULL, HandleLZDecompressCommand },
{ NULL, "rl", HandleRLCompressCommand },
{ "rl", NULL, HandleRLDecompressCommand },
{ NULL, NULL, NULL }
};
char *inputPath = argv[1];
char *outputPath = argv[2];
char *inputFileExtension = GetFileExtensionAfterDot(inputPath);
char *outputFileExtension = GetFileExtensionAfterDot(outputPath);
if (inputFileExtension == NULL)
FATAL_ERROR("Input file \"%s\" has no extension.\n", inputPath);
if (outputFileExtension == NULL)
{
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++)
{
if ((handlers[i].inputFileExtension == NULL || strcmp(handlers[i].inputFileExtension, inputFileExtension) == 0)
&& (handlers[i].outputFileExtension == NULL || strcmp(handlers[i].outputFileExtension, outputFileExtension) == 0))
{
handlers[i].function(inputPath, outputPath, argc, argv);
converted = 1;
break;
}
}
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;
}

28
tools/gbagfx/options.h Normal file
View File

@ -0,0 +1,28 @@
// Copyright (c) 2018 huderlem
#ifndef OPTIONS_H
#define OPTIONS_H
#include <stdbool.h>
struct GbaToPngOptions {
char *paletteFilePath;
int bitDepth;
bool hasTransparency;
int width;
int metatileWidth;
int metatileHeight;
char *tilemapFilePath;
bool isAffineMap;
};
struct PngToGbaOptions {
int numTiles;
int bitDepth;
int metatileWidth;
int metatileHeight;
char *tilemapFilePath;
bool isAffineMap;
};
#endif // OPTIONS_H

149
tools/gbagfx/rl.c Normal file
View File

@ -0,0 +1,149 @@
// Copyright (c) 2016 YamaArashi
#include <stdlib.h>
#include <stdbool.h>
#include "global.h"
#include "rl.h"
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize)
{
if (srcSize < 4)
goto fail;
int destSize = (src[3] << 16) | (src[2] << 8) | src[1];
unsigned char *dest = malloc(destSize);
if (dest == NULL)
goto fail;
int srcPos = 4;
int destPos = 0;
for (;;)
{
if (srcPos >= srcSize)
goto fail;
unsigned char flags = src[srcPos++];
bool compressed = ((flags & 0x80) != 0);
if (compressed)
{
int length = (flags & 0x7F) + 3;
unsigned char data = src[srcPos++];
if (destPos + length > destSize)
goto fail;
for (int i = 0; i < length; i++)
dest[destPos++] = data;
}
else
{
int length = (flags & 0x7F) + 1;
if (destPos + length > destSize)
goto fail;
for (int i = 0; i < length; i++)
dest[destPos++] = src[srcPos++];
}
if (destPos == destSize)
{
*uncompressedSize = destSize;
return dest;
}
}
fail:
FATAL_ERROR("Fatal error while decompressing RL file.\n");
}
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize)
{
if (srcSize <= 0)
goto fail;
int worstCaseDestSize = 4 + srcSize * 2;
// Round up to the next multiple of four.
worstCaseDestSize = (worstCaseDestSize + 3) & ~3;
unsigned char *dest = malloc(worstCaseDestSize);
if (dest == NULL)
goto fail;
// header
dest[0] = 0x30; // RL compression type
dest[1] = (unsigned char)srcSize;
dest[2] = (unsigned char)(srcSize >> 8);
dest[3] = (unsigned char)(srcSize >> 16);
int srcPos = 0;
int destPos = 4;
for (;;)
{
bool compress = false;
int uncompressedStart = srcPos;
int uncompressedLength = 0;
while (srcPos < srcSize && uncompressedLength < (0x7F + 1))
{
compress = (srcPos + 2 < srcSize && src[srcPos] == src[srcPos + 1] && src[srcPos] == src[srcPos + 2]);
if (compress)
break;
srcPos++;
uncompressedLength++;
}
if (uncompressedLength > 0)
{
dest[destPos++] = uncompressedLength - 1;
for (int i = 0; i < uncompressedLength; i++)
dest[destPos++] = src[uncompressedStart + i];
}
if (compress)
{
unsigned char data = src[srcPos];
int compressedLength = 0;
while (compressedLength < (0x7F + 3)
&& srcPos + compressedLength < srcSize
&& src[srcPos + compressedLength] == data)
{
compressedLength++;
}
dest[destPos++] = 0x80 | (compressedLength - 3);
dest[destPos++] = data;
srcPos += compressedLength;
}
if (srcPos == srcSize)
{
// Pad to multiple of 4 bytes.
int remainder = destPos % 4;
if (remainder != 0)
{
for (int i = 0; i < 4 - remainder; i++)
dest[destPos++] = 0;
}
*compressedSize = destPos;
return dest;
}
}
fail:
FATAL_ERROR("Fatal error while compressing RL file.\n");
}

9
tools/gbagfx/rl.h Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) 2016 YamaArashi
#ifndef RL_H
#define RL_H
unsigned char *RLDecompress(unsigned char *src, int srcSize, int *uncompressedSize);
unsigned char *RLCompress(unsigned char *src, int srcSize, int *compressedSize);
#endif // RL_H

131
tools/gbagfx/util.c Normal file
View File

@ -0,0 +1,131 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <limits.h>
#include "global.h"
#include "util.h"
bool ParseNumber(char *s, char **end, int radix, int *intValue)
{
char *localEnd;
if (end == NULL)
end = &localEnd;
errno = 0;
const long longValue = strtol(s, end, radix);
if (*end == s)
return false; // not a number
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
return false;
if (longValue > INT_MAX)
return false;
if (longValue < INT_MIN)
return false;
*intValue = (int)longValue;
return true;
}
char *GetFileExtension(char *path)
{
char *extension = path;
while (*extension != 0)
extension++;
while (extension > path && *extension != '.')
extension--;
return extension;
}
char *GetFileExtensionAfterDot(char *path)
{
char *extension = GetFileExtension(path);
if (extension == path)
return NULL;
extension++;
if (*extension == 0)
return NULL;
return extension;
}
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = calloc(*size + padAmount, 1);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
void WriteWholeFile(char *path, void *buffer, int bufferSize)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fwrite(buffer, bufferSize, 1, fp) != 1)
FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp);
}

15
tools/gbagfx/util.h Normal file
View File

@ -0,0 +1,15 @@
// Copyright (c) 2015 YamaArashi
#ifndef UTIL_H
#define UTIL_H
#include <stdbool.h>
bool ParseNumber(char *s, char **end, int radix, int *intValue);
char *GetFileExtension(char *path);
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);
#endif // UTIL_H

1
tools/jsonproc/.gitignore vendored Executable file
View File

@ -0,0 +1 @@
jsonproc

26
tools/jsonproc/Makefile Executable file
View File

@ -0,0 +1,26 @@
CXX ?= g++
CXXFLAGS := -Wall -std=c++11 -O2
INCLUDES := -I .
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$(EXE)
@:
jsonproc$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(INCLUDES) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) jsonproc jsonproc.exe

3529
tools/jsonproc/inja.hpp Executable file
View File

@ -0,0 +1,3529 @@
// MIT License
// Copyright (c) 2018 lbersch
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ---
// Copyright (c) 2009-2018 FIRST
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the FIRST nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY FIRST AND CONTRIBUTORS``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef PANTOR_INJA_HPP
#define PANTOR_INJA_HPP
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
// #include "environment.hpp"
#ifndef PANTOR_INJA_ENVIRONMENT_HPP
#define PANTOR_INJA_ENVIRONMENT_HPP
#include <memory>
#include <fstream>
#include <sstream>
#include <string>
#include <nlohmann/json.hpp>
// #include "config.hpp"
#ifndef PANTOR_INJA_CONFIG_HPP
#define PANTOR_INJA_CONFIG_HPP
#include <functional>
#include <string>
// #include "string_view.hpp"
// Copyright 2017-2019 by Martin Moene
//
// string-view lite, a C++17-like string_view for C++98 and later.
// For more information see https://github.com/martinmoene/string-view-lite
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef NONSTD_SV_LITE_H_INCLUDED
#define NONSTD_SV_LITE_H_INCLUDED
#define string_view_lite_MAJOR 1
#define string_view_lite_MINOR 1
#define string_view_lite_PATCH 0
#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x )
#define nssv_STRINGIFY_( x ) #x
// string-view lite configuration:
#define nssv_STRING_VIEW_DEFAULT 0
#define nssv_STRING_VIEW_NONSTD 1
#define nssv_STRING_VIEW_STD 2
#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
#endif
#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
#endif
#ifndef nssv_CONFIG_STD_SV_OPERATOR
# define nssv_CONFIG_STD_SV_OPERATOR 0
#endif
#ifndef nssv_CONFIG_USR_SV_OPERATOR
# define nssv_CONFIG_USR_SV_OPERATOR 1
#endif
#ifdef nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
#endif
#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
#endif
#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
#endif
// Control presence of exception handling (try and auto discover):
#ifndef nssv_CONFIG_NO_EXCEPTIONS
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
# define nssv_CONFIG_NO_EXCEPTIONS 0
# else
# define nssv_CONFIG_NO_EXCEPTIONS 1
# endif
#endif
// C++ language version detection (C++20 is speculative):
// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
#ifndef nssv_CPLUSPLUS
# if defined(_MSVC_LANG ) && !defined(__clang__)
# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
# else
# define nssv_CPLUSPLUS __cplusplus
# endif
#endif
#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L )
#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L )
#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L )
#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L )
#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L )
// use C++17 std::string_view if available and requested:
#if nssv_CPP17_OR_GREATER && defined(__has_include )
# if __has_include( <string_view> )
# define nssv_HAVE_STD_STRING_VIEW 1
# else
# define nssv_HAVE_STD_STRING_VIEW 0
# endif
#else
# define nssv_HAVE_STD_STRING_VIEW 0
#endif
#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
//
// Use C++17 std::string_view:
//
#if nssv_USES_STD_STRING_VIEW
#include <string_view>
// Extensions for std::string:
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
{
return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
}
template< class CharT, class Traits, class Allocator >
std::basic_string_view<CharT, Traits>
to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
{
return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
}
// Literal operators sv and _sv:
#if nssv_CONFIG_STD_SV_OPERATOR
using namespace std::literals::string_view_literals;
#endif
#if nssv_CONFIG_USR_SV_OPERATOR
inline namespace literals {
inline namespace string_view_literals {
constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1)
{
return std::string_view{ str, len };
}
constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2)
{
return std::u16string_view{ str, len };
}
constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3)
{
return std::u32string_view{ str, len };
}
constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4)
{
return std::wstring_view{ str, len };
}
}} // namespace literals::string_view_literals
#endif // nssv_CONFIG_USR_SV_OPERATOR
} // namespace nonstd
#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
using std::string_view;
using std::wstring_view;
using std::u16string_view;
using std::u32string_view;
using std::basic_string_view;
// literal "sv" and "_sv", see above
using std::operator==;
using std::operator!=;
using std::operator<;
using std::operator<=;
using std::operator>;
using std::operator>=;
using std::operator<<;
} // namespace nonstd
#else // nssv_HAVE_STD_STRING_VIEW
//
// Before C++17: use string_view lite:
//
// Compiler versions:
//
// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
#if defined(_MSC_VER ) && !defined(__clang__)
# define nssv_COMPILER_MSVC_VER (_MSC_VER )
# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
#else
# define nssv_COMPILER_MSVC_VER 0
# define nssv_COMPILER_MSVC_VERSION 0
#endif
#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch)
#if defined(__clang__)
# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
#else
# define nssv_COMPILER_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#else
# define nssv_COMPILER_GNUC_VERSION 0
#endif
// half-open range [lo..hi):
#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
// Presence of language and library features:
#ifdef _HAS_CPP0X
# define nssv_HAS_CPP0X _HAS_CPP0X
#else
# define nssv_HAS_CPP0X 0
#endif
// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
#if nssv_COMPILER_MSVC_VER >= 1900
# undef nssv_CPP11_OR_GREATER
# define nssv_CPP11_OR_GREATER 1
#endif
#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
// Presence of C++11 language features:
#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
#define nssv_HAVE_NULLPTR nssv_CPP11_100
#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
#endif
// Presence of C++14 language features:
#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
// Presence of C++17 language features:
#define nssv_HAVE_NODISCARD nssv_CPP17_000
// Presence of C++ library features:
#define nssv_HAVE_STD_HASH nssv_CPP11_120
// C++ feature usage:
#if nssv_HAVE_CONSTEXPR_11
# define nssv_constexpr constexpr
#else
# define nssv_constexpr /*constexpr*/
#endif
#if nssv_HAVE_CONSTEXPR_14
# define nssv_constexpr14 constexpr
#else
# define nssv_constexpr14 /*constexpr*/
#endif
#if nssv_HAVE_EXPLICIT_CONVERSION
# define nssv_explicit explicit
#else
# define nssv_explicit /*explicit*/
#endif
#if nssv_HAVE_INLINE_NAMESPACE
# define nssv_inline_ns inline
#else
# define nssv_inline_ns /*inline*/
#endif
#if nssv_HAVE_NOEXCEPT
# define nssv_noexcept noexcept
#else
# define nssv_noexcept /*noexcept*/
#endif
//#if nssv_HAVE_REF_QUALIFIER
//# define nssv_ref_qual &
//# define nssv_refref_qual &&
//#else
//# define nssv_ref_qual /*&*/
//# define nssv_refref_qual /*&&*/
//#endif
#if nssv_HAVE_NULLPTR
# define nssv_nullptr nullptr
#else
# define nssv_nullptr NULL
#endif
#if nssv_HAVE_NODISCARD
# define nssv_nodiscard [[nodiscard]]
#else
# define nssv_nodiscard /*[[nodiscard]]*/
#endif
// Additional includes:
#include <algorithm>
#include <cassert>
#include <iterator>
#include <limits>
#include <ostream>
#include <string> // std::char_traits<>
#if ! nssv_CONFIG_NO_EXCEPTIONS
# include <stdexcept>
#endif
#if nssv_CPP11_OR_GREATER
# include <type_traits>
#endif
// Clang, GNUC, MSVC warning suppression macros:
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wuser-defined-literals"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wliteral-suffix"
#endif // __clang__
#if nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) )
# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
#else
# define nssv_SUPPRESS_MSGSL_WARNING(expr)
# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
# define nssv_DISABLE_MSVC_WARNINGS(codes)
#endif
#if defined(__clang__)
# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
#elif defined(__GNUC__)
# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
#elif nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_RESTORE_WARNINGS() __pragma(warning(pop ))
#else
# define nssv_RESTORE_WARNINGS()
#endif
// Suppress the following MSVC (GSL) warnings:
// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
// start with an underscore are reserved
// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
// use brace initialization, gsl::narrow_cast or gsl::narow
// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
namespace nonstd { namespace sv_lite {
template
<
class CharT,
class Traits = std::char_traits<CharT>
>
class basic_string_view;
//
// basic_string_view:
//
template
<
class CharT,
class Traits /* = std::char_traits<CharT> */
>
class basic_string_view
{
public:
// Member types:
typedef Traits traits_type;
typedef CharT value_type;
typedef CharT * pointer;
typedef CharT const * const_pointer;
typedef CharT & reference;
typedef CharT const & const_reference;
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::size_t size_type;
typedef std::ptrdiff_t difference_type;
// 24.4.2.1 Construction and assignment:
nssv_constexpr basic_string_view() nssv_noexcept
: data_( nssv_nullptr )
, size_( 0 )
{}
#if nssv_CPP11_OR_GREATER
nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
#else
nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
: data_( other.data_)
, size_( other.size_)
{}
#endif
nssv_constexpr basic_string_view( CharT const * s, size_type count )
: data_( s )
, size_( count )
{}
nssv_constexpr basic_string_view( CharT const * s)
: data_( s )
, size_( Traits::length(s) )
{}
// Assignment:
#if nssv_CPP11_OR_GREATER
nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
#else
nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
{
data_ = other.data_;
size_ = other.size_;
return *this;
}
#endif
// 24.4.2.2 Iterator support:
nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); }
nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); }
nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
// 24.4.2.3 Capacity:
nssv_constexpr size_type size() const nssv_noexcept { return size_; }
nssv_constexpr size_type length() const nssv_noexcept { return size_; }
nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
// since C++20
nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
{
return 0 == size_;
}
// 24.4.2.4 Element access:
nssv_constexpr const_reference operator[]( size_type pos ) const
{
return data_at( pos );
}
nssv_constexpr14 const_reference at( size_type pos ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos < size() );
#else
if ( pos >= size() )
{
throw std::out_of_range("nonst::string_view::at()");
}
#endif
return data_at( pos );
}
nssv_constexpr const_reference front() const { return data_at( 0 ); }
nssv_constexpr const_reference back() const { return data_at( size() - 1 ); }
nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
// 24.4.2.5 Modifiers:
nssv_constexpr14 void remove_prefix( size_type n )
{
assert( n <= size() );
data_ += n;
size_ -= n;
}
nssv_constexpr14 void remove_suffix( size_type n )
{
assert( n <= size() );
size_ -= n;
}
nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
{
using std::swap;
swap( data_, other.data_ );
swap( size_, other.size_ );
}
// 24.4.2.6 String operations:
size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos <= size() );
#else
if ( pos > size() )
{
throw std::out_of_range("nonst::string_view::copy()");
}
#endif
const size_type rlen = (std::min)( n, size() - pos );
(void) Traits::copy( dest, data() + pos, rlen );
return rlen;
}
nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos <= size() );
#else
if ( pos > size() )
{
throw std::out_of_range("nonst::string_view::substr()");
}
#endif
return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
}
// compare(), 6x:
nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
{
if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
return result;
return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
}
nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
{
return substr( pos1, n1 ).compare( other );
}
nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
{
return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
}
nssv_constexpr int compare( CharT const * s ) const // (4)
{
return compare( basic_string_view( s ) );
}
nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
{
return substr( pos1, n1 ).compare( basic_string_view( s ) );
}
nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
{
return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
}
// 24.4.2.7 Searching:
// starts_with(), 3x, since C++20:
nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1)
{
return size() >= v.size() && compare( 0, v.size(), v ) == 0;
}
nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2)
{
return starts_with( basic_string_view( &c, 1 ) );
}
nssv_constexpr bool starts_with( CharT const * s ) const // (3)
{
return starts_with( basic_string_view( s ) );
}
// ends_with(), 3x, since C++20:
nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1)
{
return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
}
nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2)
{
return ends_with( basic_string_view( &c, 1 ) );
}
nssv_constexpr bool ends_with( CharT const * s ) const // (3)
{
return ends_with( basic_string_view( s ) );
}
// find(), 4x:
nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return assert( v.size() == 0 || v.data() != nssv_nullptr )
, pos >= size()
? npos
: to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
}
nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3)
{
return find( basic_string_view( s, n ), pos );
}
nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4)
{
return find( basic_string_view( s ), pos );
}
// rfind(), 4x:
nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
if ( size() < v.size() )
return npos;
if ( v.empty() )
return (std::min)( size(), pos );
const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
return result != last ? size_type( result - cbegin() ) : npos;
}
nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return rfind( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3)
{
return rfind( basic_string_view( s, n ), pos );
}
nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4)
{
return rfind( basic_string_view( s ), pos );
}
// find_first_of(), 4x:
nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos >= size()
? npos
: to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
}
nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3)
{
return find_first_of( basic_string_view( s, n ), pos );
}
nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4)
{
return find_first_of( basic_string_view( s ), pos );
}
// find_last_of(), 4x:
nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
return empty()
? npos
: pos >= size()
? find_last_of( v, size() - 1 )
: to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
}
nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_last_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4)
{
return find_last_of( basic_string_view( s ), pos );
}
// find_first_not_of(), 4x:
nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos >= size()
? npos
: to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
}
nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_not_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_first_not_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4)
{
return find_first_not_of( basic_string_view( s ), pos );
}
// find_last_not_of(), 4x:
nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
return empty()
? npos
: pos >= size()
? find_last_not_of( v, size() - 1 )
: to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
}
nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_not_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_last_not_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4)
{
return find_last_not_of( basic_string_view( s ), pos );
}
// Constants:
#if nssv_CPP17_OR_GREATER
static nssv_constexpr size_type npos = size_type(-1);
#elif nssv_CPP11_OR_GREATER
enum : size_type { npos = size_type(-1) };
#else
enum { npos = size_type(-1) };
#endif
private:
struct not_in_view
{
const basic_string_view v;
nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
nssv_constexpr bool operator()( CharT c ) const
{
return npos == v.find_first_of( c );
}
};
nssv_constexpr size_type to_pos( const_iterator it ) const
{
return it == cend() ? npos : size_type( it - cbegin() );
}
nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
{
return it == crend() ? npos : size_type( crend() - it - 1 );
}
nssv_constexpr const_reference data_at( size_type pos ) const
{
#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
return data_[pos];
#else
return assert( pos < size() ), data_[pos];
#endif
}
private:
const_pointer data_;
size_type size_;
public:
#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
template< class Allocator >
basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
: data_( s.data() )
, size_( s.size() )
{}
#if nssv_HAVE_EXPLICIT_CONVERSION
template< class Allocator >
explicit operator std::basic_string<CharT, Traits, Allocator>() const
{
return to_string( Allocator() );
}
#endif // nssv_HAVE_EXPLICIT_CONVERSION
#if nssv_CPP11_OR_GREATER
template< class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( Allocator const & a = Allocator() ) const
{
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
}
#else
std::basic_string<CharT, Traits>
to_string() const
{
return std::basic_string<CharT, Traits>( begin(), end() );
}
template< class Allocator >
std::basic_string<CharT, Traits, Allocator>
to_string( Allocator const & a ) const
{
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
}
#endif // nssv_CPP11_OR_GREATER
#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
};
//
// Non-member functions:
//
// 24.4.3 Non-member comparison functions:
// lexicographically compare two string views (function template):
template< class CharT, class Traits >
nssv_constexpr bool operator== (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) == 0 ; }
template< class CharT, class Traits >
nssv_constexpr bool operator!= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) != 0 ; }
template< class CharT, class Traits >
nssv_constexpr bool operator< (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0 ; }
template< class CharT, class Traits >
nssv_constexpr bool operator<= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0 ; }
template< class CharT, class Traits >
nssv_constexpr bool operator> (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0 ; }
template< class CharT, class Traits >
nssv_constexpr bool operator>= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0 ; }
// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
// Implementations shall provide sufficient additional overloads marked
// constexpr and noexcept so that an object t with an implicit conversion
// to S can be compared according to Table 67.
#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view<T,U> >::type
#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
# define nssv_MSVC_ORDER(x) , int=x
#else
# define nssv_MSVC_ORDER(x) /*, int=x*/
#endif
// ==
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator==(
basic_string_view <CharT, Traits> lhs,
nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) == 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator==(
nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
// !=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator!= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator!= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) != 0 ; }
// <
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator< (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0 ; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator< (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0 ; }
// <=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator<= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0 ; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator<= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0 ; }
// >
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator> (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0 ; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator> (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0 ; }
// >=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator>= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0 ; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator>= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0 ; }
#undef nssv_MSVC_ORDER
#undef nssv_BASIC_STRING_VIEW_I
#endif // nssv_CPP11_OR_GREATER
// 24.4.4 Inserters and extractors:
namespace detail {
template< class Stream >
void write_padding( Stream & os, std::streamsize n )
{
for ( std::streamsize i = 0; i < n; ++i )
os.rdbuf()->sputc( os.fill() );
}
template< class Stream, class View >
Stream & write_to_stream( Stream & os, View const & sv )
{
typename Stream::sentry sentry( os );
if ( !os )
return os;
const std::streamsize length = static_cast<std::streamsize>( sv.length() );
// Whether, and how, to pad:
const bool pad = ( length < os.width() );
const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
if ( left_pad )
write_padding( os, os.width() - length );
// Write span characters:
os.rdbuf()->sputn( sv.begin(), length );
if ( pad && !left_pad )
write_padding( os, os.width() - length );
// Reset output stream width:
os.width( 0 );
return os;
}
} // namespace detail
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits>& os,
basic_string_view <CharT, Traits> sv )
{
return detail::write_to_stream( os, sv );
}
// Several typedefs for common character types are provided:
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
#if nssv_HAVE_WCHAR16_T
typedef basic_string_view<char16_t> u16string_view;
typedef basic_string_view<char32_t> u32string_view;
#endif
}} // namespace nonstd::sv_lite
//
// 24.4.6 Suffix for basic_string_view literals:
//
#if nssv_HAVE_USER_DEFINED_LITERALS
namespace nonstd {
nssv_inline_ns namespace literals {
nssv_inline_ns namespace string_view_literals {
#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1)
{
return nonstd::sv_lite::string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{
return nonstd::sv_lite::u16string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{
return nonstd::sv_lite::u32string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{
return nonstd::sv_lite::wstring_view{ str, len };
}
#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
#if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1)
{
return nonstd::sv_lite::string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{
return nonstd::sv_lite::u16string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{
return nonstd::sv_lite::u32string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{
return nonstd::sv_lite::wstring_view{ str, len };
}
#endif // nssv_CONFIG_USR_SV_OPERATOR
}}} // namespace nonstd::literals::string_view_literals
#endif
//
// Extensions for std::string:
//
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
namespace sv_lite {
// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
{
return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
}
#else
template< class CharT, class Traits >
std::basic_string<CharT, Traits>
to_string( basic_string_view<CharT, Traits> v )
{
return std::basic_string<CharT, Traits>( v.begin(), v.end() );
}
template< class CharT, class Traits, class Allocator >
std::basic_string<CharT, Traits, Allocator>
to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
{
return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
}
#endif // nssv_CPP11_OR_GREATER
template< class CharT, class Traits, class Allocator >
basic_string_view<CharT, Traits>
to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
{
return basic_string_view<CharT, Traits>( s.data(), s.size() );
}
}} // namespace nonstd::sv_lite
#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
//
// make types and algorithms available in namespace nonstd:
//
namespace nonstd {
using sv_lite::basic_string_view;
using sv_lite::string_view;
using sv_lite::wstring_view;
#if nssv_HAVE_WCHAR16_T
using sv_lite::u16string_view;
#endif
#if nssv_HAVE_WCHAR32_T
using sv_lite::u32string_view;
#endif
// literal "sv"
using sv_lite::operator==;
using sv_lite::operator!=;
using sv_lite::operator<;
using sv_lite::operator<=;
using sv_lite::operator>;
using sv_lite::operator>=;
using sv_lite::operator<<;
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
using sv_lite::to_string;
using sv_lite::to_string_view;
#endif
} // namespace nonstd
// 24.4.5 Hash support (C++11):
// Note: The hash value of a string view object is equal to the hash value of
// the corresponding string object.
#if nssv_HAVE_STD_HASH
#include <functional>
namespace std {
template<>
struct hash< nonstd::string_view >
{
public:
std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
{
return std::hash<std::string>()( std::string( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::wstring_view >
{
public:
std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
{
return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::u16string_view >
{
public:
std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
{
return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::u32string_view >
{
public:
std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
{
return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
}
};
} // namespace std
#endif // nssv_HAVE_STD_HASH
nssv_RESTORE_WARNINGS()
#endif // nssv_HAVE_STD_STRING_VIEW
#endif // NONSTD_SV_LITE_H_INCLUDED
namespace inja {
enum class ElementNotation {
Dot,
Pointer
};
/*!
* \brief Class for lexer configuration.
*/
struct LexerConfig {
std::string statement_open {"{%"};
std::string statement_close {"%}"};
std::string line_statement {"##"};
std::string expression_open {"{{"};
std::string expression_close {"}}"};
std::string comment_open {"{#"};
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) {
open_chars += line_statement[0];
}
if (open_chars.find(statement_open[0]) == std::string::npos) {
open_chars += statement_open[0];
}
if (open_chars.find(expression_open[0]) == std::string::npos) {
open_chars += expression_open[0];
}
if (open_chars.find(comment_open[0]) == std::string::npos) {
open_chars += comment_open[0];
}
}
};
/*!
* \brief Class for parser configuration.
*/
struct ParserConfig {
ElementNotation notation {ElementNotation::Dot};
};
}
#endif // PANTOR_INJA_CONFIG_HPP
// #include "function_storage.hpp"
#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>
// #include "string_view.hpp"
namespace inja {
using json = nlohmann::json;
struct Bytecode {
enum class Op : uint8_t {
Nop,
// print StringRef (always immediate)
PrintText,
// print value
PrintValue,
// push value onto stack (always immediate)
Push,
// builtin functions
// result is pushed to stack
// args specify number of arguments
// all functions can take their "last" argument either immediate
// or popped off stack (e.g. if immediate, it's like the immediate was
// just pushed to the stack)
Not,
And,
Or,
In,
Equal,
Greater,
GreaterEqual,
Less,
LessEqual,
At,
Different,
DivisibleBy,
Even,
First,
Float,
Int,
Last,
Length,
Lower,
Max,
Min,
Odd,
Range,
Result,
Round,
Sort,
Upper,
Exists,
ExistsInObject,
IsBoolean,
IsNumber,
IsInteger,
IsFloat,
IsObject,
IsArray,
IsString,
Default,
// include another template
// value is the template name
Include,
// callback function
// str is the function name (this means it cannot be a lookup)
// args specify number of arguments
// as with builtin functions, "last" argument can be immediate
Callback,
// unconditional jump
// args is the index of the bytecode to jump to.
Jump,
// conditional jump
// value popped off stack is checked for truthyness
// if false, args is the index of the bytecode to jump to.
// if true, no action is taken (falls through)
ConditionalJump,
// start loop
// value popped off stack is what is iterated over
// args is index of bytecode after end loop (jumped to if iterable is
// empty)
// immediate value is key name (for maps)
// str is value name
StartLoop,
// end a loop
// args is index of the first bytecode in the loop body
EndLoop,
};
enum Flag {
// location of value for value-taking ops (mask)
ValueMask = 0x03,
// pop value off stack
ValuePop = 0x00,
// value is immediate rather than on stack
ValueImmediate = 0x01,
// lookup immediate str (dot notation)
ValueLookupDot = 0x02,
// lookup immediate str (json pointer notation)
ValueLookupPointer = 0x03,
};
Op op {Op::Nop};
uint32_t args: 30;
uint32_t flags: 2;
json value;
std::string str;
Bytecode(): args(0), flags(0) {}
explicit Bytecode(Op op, unsigned int args = 0): op(op), args(args), flags(0) {}
explicit Bytecode(Op op, nonstd::string_view str, unsigned int flags): op(op), args(0), flags(flags), str(str) {}
explicit Bytecode(Op op, json&& value, unsigned int flags): op(op), args(0), flags(flags), value(std::move(value)) {}
};
} // namespace inja
#endif // PANTOR_INJA_BYTECODE_HPP
// #include "string_view.hpp"
namespace inja {
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) {
auto& data = get_or_new(name, num_args);
data.op = op;
}
void add_callback(nonstd::string_view name, unsigned int num_args, const CallbackFunction& function) {
auto& data = get_or_new(name, num_args);
data.function = function;
}
Bytecode::Op find_builtin(nonstd::string_view name, unsigned int num_args) const {
if (auto ptr = get(name, num_args)) {
return ptr->op;
}
return Bytecode::Op::Nop;
}
CallbackFunction find_callback(nonstd::string_view name, unsigned int num_args) const {
if (auto ptr = get(name, num_args)) {
return ptr->function;
}
return nullptr;
}
private:
struct FunctionData {
unsigned int num_args {0};
Bytecode::Op op {Bytecode::Op::Nop}; // for builtins
CallbackFunction function; // for callbacks
};
FunctionData& get_or_new(nonstd::string_view name, unsigned int num_args) {
auto &vec = m_map[static_cast<std::string>(name)];
for (auto &i: vec) {
if (i.num_args == num_args) return i;
}
vec.emplace_back();
vec.back().num_args = num_args;
return vec.back();
}
const FunctionData* get(nonstd::string_view name, unsigned int num_args) const {
auto it = m_map.find(static_cast<std::string>(name));
if (it == m_map.end()) return nullptr;
for (auto &&i: it->second) {
if (i.num_args == num_args) return &i;
}
return nullptr;
}
std::map<std::string, std::vector<FunctionData>> m_map;
};
}
#endif // PANTOR_INJA_FUNCTION_STORAGE_HPP
// #include "parser.hpp"
#ifndef PANTOR_INJA_PARSER_HPP
#define PANTOR_INJA_PARSER_HPP
#include <limits>
#include <string>
#include <utility>
#include <vector>
// #include "bytecode.hpp"
// #include "config.hpp"
// #include "function_storage.hpp"
// #include "lexer.hpp"
#ifndef PANTOR_INJA_LEXER_HPP
#define PANTOR_INJA_LEXER_HPP
#include <cctype>
#include <locale>
// #include "config.hpp"
// #include "token.hpp"
#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,
ExpressionOpen, // {{
ExpressionClose, // }}
LineStatementOpen, // ##
LineStatementClose, // \n
StatementOpen, // {%
StatementClose, // %}
CommentOpen, // {#
CommentClose, // #}
Id, // this, this.foo
Number, // 1, 2, -1, 5.2, -5.3
String, // "this"
Comma, // ,
Colon, // :
LeftParen, // (
RightParen, // )
LeftBracket, // [
RightBracket, // ]
LeftBrace, // {
RightBrace, // }
Equal, // ==
GreaterThan, // >
GreaterEqual, // >=
LessThan, // <
LessEqual, // <=
NotEqual, // !=
Unknown,
Eof
} kind {Kind::Unknown};
nonstd::string_view text;
constexpr Token() = default;
constexpr Token(Kind kind, nonstd::string_view text): kind(kind), text(text) {}
std::string describe() const {
switch (kind) {
case Kind::Text:
return "<text>";
case Kind::LineStatementClose:
return "<eol>";
case Kind::Eof:
return "<eof>";
default:
return static_cast<std::string>(text);
}
}
};
}
#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"
namespace inja {
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);
}
inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
size_t idx = view.find(Separator);
if (idx == nonstd::string_view::npos) {
return std::make_pair(view, nonstd::string_view());
}
return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
}
inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
}
} // namespace string
} // namespace inja
#endif // PANTOR_INJA_UTILS_HPP
namespace inja {
/*!
* \brief Class for lexing an inja Template.
*/
class Lexer {
enum class State {
Text,
ExpressionStart,
ExpressionBody,
LineStart,
LineBody,
StatementStart,
StatementBody,
CommentStart,
CommentBody
} m_state;
const LexerConfig& m_config;
nonstd::string_view m_in;
size_t m_tok_start;
size_t m_pos;
public:
explicit Lexer(const LexerConfig& config) : m_config(config) {}
void start(nonstd::string_view in) {
m_in = in;
m_tok_start = 0;
m_pos = 0;
m_state = State::Text;
}
Token scan() {
m_tok_start = m_pos;
again:
if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof);
switch (m_state) {
default:
case State::Text: {
// fast-scan to first open character
size_t open_start = m_in.substr(m_pos).find_first_of(m_config.open_chars);
if (open_start == nonstd::string_view::npos) {
// didn't find open, return remaining text as text token
m_pos = m_in.size();
return make_token(Token::Kind::Text);
}
m_pos += open_start;
// 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;
} else {
m_pos += 1; // wasn't actually an opening sequence
goto again;
}
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;
m_pos += m_config.expression_open.size();
return make_token(Token::Kind::ExpressionOpen);
}
case State::LineStart: {
m_state = State::LineBody;
m_pos += m_config.line_statement.size();
return make_token(Token::Kind::LineStatementOpen);
}
case State::StatementStart: {
m_state = State::StatementBody;
m_pos += m_config.statement_open.size();
return make_token(Token::Kind::StatementOpen);
}
case State::CommentStart: {
m_state = State::CommentBody;
m_pos += m_config.comment_open.size();
return make_token(Token::Kind::CommentOpen);
}
case State::ExpressionBody:
return scan_body(m_config.expression_close, Token::Kind::ExpressionClose);
case State::LineBody:
return scan_body("\n", Token::Kind::LineStatementClose);
case State::StatementBody:
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);
if (end == nonstd::string_view::npos) {
m_pos = m_in.size();
return make_token(Token::Kind::Eof);
}
// return the entire comment in the close token
m_state = State::Text;
m_pos += end + m_config.comment_close.size();
Token tok = make_token(Token::Kind::CommentClose);
if (m_config.trim_blocks)
skip_newline();
return tok;
}
}
}
const LexerConfig& get_config() const { return m_config; }
private:
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);
char ch = m_in[m_tok_start];
if (ch == ' ' || ch == '\t' || ch == '\r') {
m_tok_start += 1;
goto again;
}
// check for close
if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) {
m_state = State::Text;
m_pos = m_tok_start + close.size();
Token tok = make_token(closeKind);
if (trim)
skip_newline();
return tok;
}
// skip \n
if (ch == '\n') {
m_tok_start += 1;
goto again;
}
m_pos = m_tok_start + 1;
if (std::isalpha(ch)) return scan_id();
switch (ch) {
case ',':
return make_token(Token::Kind::Comma);
case ':':
return make_token(Token::Kind::Colon);
case '(':
return make_token(Token::Kind::LeftParen);
case ')':
return make_token(Token::Kind::RightParen);
case '[':
return make_token(Token::Kind::LeftBracket);
case ']':
return make_token(Token::Kind::RightBracket);
case '{':
return make_token(Token::Kind::LeftBrace);
case '}':
return make_token(Token::Kind::RightBrace);
case '>':
if (m_pos < m_in.size() && m_in[m_pos] == '=') {
m_pos += 1;
return make_token(Token::Kind::GreaterEqual);
}
return make_token(Token::Kind::GreaterThan);
case '<':
if (m_pos < m_in.size() && m_in[m_pos] == '=') {
m_pos += 1;
return make_token(Token::Kind::LessEqual);
}
return make_token(Token::Kind::LessThan);
case '=':
if (m_pos < m_in.size() && m_in[m_pos] == '=') {
m_pos += 1;
return make_token(Token::Kind::Equal);
}
return make_token(Token::Kind::Unknown);
case '!':
if (m_pos < m_in.size() && m_in[m_pos] == '=') {
m_pos += 1;
return make_token(Token::Kind::NotEqual);
}
return make_token(Token::Kind::Unknown);
case '\"':
return scan_string();
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return scan_number();
case '_':
return scan_id();
default:
return make_token(Token::Kind::Unknown);
}
}
Token scan_id() {
for (;;) {
if (m_pos >= m_in.size()) {
break;
}
char ch = m_in[m_pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
break;
}
m_pos += 1;
}
return make_token(Token::Kind::Id);
}
Token scan_number() {
for (;;) {
if (m_pos >= m_in.size()) {
break;
}
char ch = m_in[m_pos];
// be very permissive in lexer (we'll catch errors when conversion happens)
if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
break;
}
m_pos += 1;
}
return make_token(Token::Kind::Number);
}
Token scan_string() {
bool escape {false};
for (;;) {
if (m_pos >= m_in.size()) break;
char ch = m_in[m_pos++];
if (ch == '\\') {
escape = true;
} else if (!escape && ch == m_in[m_tok_start]) {
break;
} else {
escape = false;
}
}
return make_token(Token::Kind::String);
}
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;
}
};
}
#endif // PANTOR_INJA_LEXER_HPP
// #include "template.hpp"
#ifndef PANTOR_INJA_TEMPLATE_HPP
#define PANTOR_INJA_TEMPLATE_HPP
#include <map>
#include <string>
#include <vector>
// #include "bytecode.hpp"
namespace inja {
/*!
* \brief The main inja Template.
*/
struct Template {
std::vector<Bytecode> bytecodes;
std::string content;
};
using TemplateStorage = std::map<std::string, Template>;
}
#endif // PANTOR_INJA_TEMPLATE_HPP
// #include "token.hpp"
// #include "utils.hpp"
#include <nlohmann/json.hpp>
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);
functions.add_builtin("first", 1, Bytecode::Op::First);
functions.add_builtin("float", 1, Bytecode::Op::Float);
functions.add_builtin("int", 1, Bytecode::Op::Int);
functions.add_builtin("last", 1, Bytecode::Op::Last);
functions.add_builtin("length", 1, Bytecode::Op::Length);
functions.add_builtin("lower", 1, Bytecode::Op::Lower);
functions.add_builtin("max", 1, Bytecode::Op::Max);
functions.add_builtin("min", 1, Bytecode::Op::Min);
functions.add_builtin("odd", 1, Bytecode::Op::Odd);
functions.add_builtin("range", 1, Bytecode::Op::Range);
functions.add_builtin("round", 2, Bytecode::Op::Round);
functions.add_builtin("sort", 1, Bytecode::Op::Sort);
functions.add_builtin("upper", 1, Bytecode::Op::Upper);
functions.add_builtin("exists", 1, Bytecode::Op::Exists);
functions.add_builtin("existsIn", 2, Bytecode::Op::ExistsInObject);
functions.add_builtin("isBoolean", 1, Bytecode::Op::IsBoolean);
functions.add_builtin("isNumber", 1, Bytecode::Op::IsNumber);
functions.add_builtin("isInteger", 1, Bytecode::Op::IsInteger);
functions.add_builtin("isFloat", 1, Bytecode::Op::IsFloat);
functions.add_builtin("isObject", 1, Bytecode::Op::IsObject);
functions.add_builtin("isArray", 1, Bytecode::Op::IsArray);
functions.add_builtin("isString", 1, Bytecode::Op::IsString);
}
public:
ParserStatic(const ParserStatic&) = delete;
ParserStatic& operator=(const ParserStatic&) = delete;
static const ParserStatic& get_instance() {
static ParserStatic inst;
return inst;
}
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 != 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);
return true;
}
bool parse_expression_and(Template& tmpl) {
if (!parse_expression_not(tmpl)) return false;
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);
return true;
}
bool parse_expression_not(Template& tmpl) {
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);
return true;
} else {
return parse_expression_comparison(tmpl);
}
}
bool parse_expression_comparison(Template& tmpl) {
if (!parse_expression_datum(tmpl)) return false;
Bytecode::Op op;
switch (m_tok.kind) {
case Token::Kind::Id:
if (m_tok.text == static_cast<decltype(m_tok.text)>("in"))
op = Bytecode::Op::In;
else
return true;
break;
case Token::Kind::Equal:
op = Bytecode::Op::Equal;
break;
case Token::Kind::GreaterThan:
op = Bytecode::Op::Greater;
break;
case Token::Kind::LessThan:
op = Bytecode::Op::Less;
break;
case Token::Kind::LessEqual:
op = Bytecode::Op::LessEqual;
break;
case Token::Kind::GreaterEqual:
op = Bytecode::Op::GreaterEqual;
break;
case Token::Kind::NotEqual:
op = Bytecode::Op::Different;
break;
default:
return true;
}
get_next_token();
if (!parse_expression_datum(tmpl)) return false;
append_function(tmpl, op, 2);
return true;
}
bool parse_expression_datum(Template& tmpl) {
nonstd::string_view json_first;
size_t bracket_level = 0;
size_t brace_level = 0;
for (;;) {
switch (m_tok.kind) {
case Token::Kind::LeftParen: {
get_next_token();
if (!parse_expression(tmpl)) return false;
if (m_tok.kind != Token::Kind::RightParen) {
inja_throw("parser_error", "unmatched '('");
}
get_next_token();
return true;
}
case Token::Kind::Id:
get_peek_token();
if (m_peek_tok.kind == Token::Kind::LeftParen) {
// function call, parse arguments
Token func_token = m_tok;
get_next_token(); // id
get_next_token(); // leftParen
unsigned int num_args = 0;
if (m_tok.kind == Token::Kind::RightParen) {
// no args
get_next_token();
} else {
for (;;) {
if (!parse_expression(tmpl)) {
inja_throw("parser_error", "expected expression, got '" + m_tok.describe() + "'");
}
num_args += 1;
if (m_tok.kind == Token::Kind::RightParen) {
get_next_token();
break;
}
if (m_tok.kind != Token::Kind::Comma) {
inja_throw("parser_error", "expected ')' or ',', got '" + m_tok.describe() + "'");
}
get_next_token();
}
}
auto op = m_static.functions.find_builtin(func_token.text, num_args);
if (op != Bytecode::Op::Nop) {
// swap arguments for default(); see comment in RenderTo()
if (op == Bytecode::Op::Default)
std::swap(tmpl.bytecodes.back(), *(tmpl.bytecodes.rbegin() + 1));
append_function(tmpl, op, num_args);
return true;
} else {
append_callback(tmpl, func_token.text, num_args);
return true;
}
} 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;
goto returnJson;
}
break;
} else {
// normal literal (json read)
tmpl.bytecodes.emplace_back(
Bytecode::Op::Push, m_tok.text,
m_config.notation == ElementNotation::Pointer ? Bytecode::Flag::ValueLookupPointer : Bytecode::Flag::ValueLookupDot);
get_next_token();
return true;
}
// json passthrough
case Token::Kind::Number:
case Token::Kind::String:
if (brace_level == 0 && bracket_level == 0) {
json_first = m_tok.text;
goto returnJson;
}
break;
case Token::Kind::Comma:
case Token::Kind::Colon:
if (brace_level == 0 && bracket_level == 0) {
inja_throw("parser_error", "unexpected token '" + m_tok.describe() + "'");
}
break;
case Token::Kind::LeftBracket:
if (brace_level == 0 && bracket_level == 0) {
json_first = m_tok.text;
}
bracket_level += 1;
break;
case Token::Kind::LeftBrace:
if (brace_level == 0 && bracket_level == 0) {
json_first = m_tok.text;
}
brace_level += 1;
break;
case Token::Kind::RightBracket:
if (bracket_level == 0) {
inja_throw("parser_error", "unexpected ']'");
}
--bracket_level;
if (brace_level == 0 && bracket_level == 0) goto returnJson;
break;
case Token::Kind::RightBrace:
if (brace_level == 0) {
inja_throw("parser_error", "unexpected '}'");
}
--brace_level;
if (brace_level == 0 && bracket_level == 0) goto returnJson;
break;
default:
if (brace_level != 0) {
inja_throw("parser_error", "unmatched '{'");
}
if (bracket_level != 0) {
inja_throw("parser_error", "unmatched '['");
}
return false;
}
get_next_token();
}
returnJson:
// bridge across all intermediate tokens
nonstd::string_view json_text(json_first.data(), m_tok.text.data() - json_first.data() + m_tok.text.size());
tmpl.bytecodes.emplace_back(Bytecode::Op::Push, json::parse(json_text), Bytecode::Flag::ValueImmediate);
get_next_token();
return true;
}
bool parse_statement(Template& tmpl, nonstd::string_view path) {
if (m_tok.kind != Token::Kind::Id) return false;
if (m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
get_next_token();
// evaluate expression
if (!parse_expression(tmpl)) return false;
// start a new if block on if stack
m_if_stack.emplace_back(tmpl.bytecodes.size());
// conditional jump; destination will be filled in by else or endif
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
} 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");
}
auto& if_data = m_if_stack.back();
get_next_token();
// previous conditional jump jumps here
if (if_data.prev_cond_jump != std::numeric_limits<unsigned int>::max()) {
tmpl.bytecodes[if_data.prev_cond_jump].args = tmpl.bytecodes.size();
}
// update all previous unconditional jumps to here
for (unsigned int i: if_data.uncond_jumps) {
tmpl.bytecodes[i].args = tmpl.bytecodes.size();
}
// pop if stack
m_if_stack.pop_back();
} 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();
get_next_token();
// end previous block with unconditional jump to endif; destination will be
// filled in by endif
if_data.uncond_jumps.push_back(tmpl.bytecodes.size());
tmpl.bytecodes.emplace_back(Bytecode::Op::Jump);
// previous conditional jump jumps here
tmpl.bytecodes[if_data.prev_cond_jump].args = tmpl.bytecodes.size();
if_data.prev_cond_jump = std::numeric_limits<unsigned int>::max();
// chained else if
if (m_tok.kind == Token::Kind::Id && m_tok.text == static_cast<decltype(m_tok.text)>("if")) {
get_next_token();
// evaluate expression
if (!parse_expression(tmpl)) return false;
// update "previous jump"
if_data.prev_cond_jump = tmpl.bytecodes.size();
// conditional jump; destination will be filled in by else or endif
tmpl.bytecodes.emplace_back(Bytecode::Op::ConditionalJump);
}
} 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
if (m_tok.kind != Token::Kind::Id)
inja_throw("parser_error", "expected id, got '" + m_tok.describe() + "'");
Token value_token = m_tok;
get_next_token();
Token key_token;
if (m_tok.kind == Token::Kind::Comma) {
get_next_token();
if (m_tok.kind != Token::Kind::Id)
inja_throw("parser_error", "expected id, got '" + m_tok.describe() + "'");
key_token = std::move(value_token);
value_token = m_tok;
get_next_token();
}
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();
if (!parse_expression(tmpl)) return false;
m_loop_stack.push_back(tmpl.bytecodes.size());
tmpl.bytecodes.emplace_back(Bytecode::Op::StartLoop);
if (!key_token.text.empty()) {
tmpl.bytecodes.back().value = key_token.text;
}
tmpl.bytecodes.back().str = static_cast<std::string>(value_token.text);
} 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");
}
// update loop with EndLoop index (for empty case)
tmpl.bytecodes[m_loop_stack.back()].args = tmpl.bytecodes.size();
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 == static_cast<decltype(m_tok.text)>("include")) {
get_next_token();
if (m_tok.kind != Token::Kind::String) {
inja_throw("parser_error", "expected string, got '" + m_tok.describe() + "'");
}
// build the relative path
json json_name = json::parse(m_tok.text);
std::string pathname = static_cast<std::string>(path);
pathname += json_name.get_ref<const std::string&>();
if (pathname.compare(0, 2, "./") == 0) {
pathname.erase(0, 2);
}
// sys::path::remove_dots(pathname, true, sys::path::Style::posix);
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);
get_next_token();
} else {
return false;
}
return true;
}
void append_function(Template& tmpl, Bytecode::Op op, unsigned int num_args) {
// we can merge with back-to-back push
if (!tmpl.bytecodes.empty()) {
Bytecode& last = tmpl.bytecodes.back();
if (last.op == Bytecode::Op::Push) {
last.op = op;
last.args = num_args;
return;
}
}
// otherwise just add it to the end
tmpl.bytecodes.emplace_back(op, num_args);
}
void append_callback(Template& tmpl, nonstd::string_view name, unsigned int num_args) {
// we can merge with back-to-back push value (not lookup)
if (!tmpl.bytecodes.empty()) {
Bytecode& last = tmpl.bytecodes.back();
if (last.op == Bytecode::Op::Push &&
(last.flags & Bytecode::Flag::ValueMask) == Bytecode::Flag::ValueImmediate) {
last.op = Bytecode::Op::Callback;
last.args = num_args;
last.str = static_cast<std::string>(name);
return;
}
}
// otherwise just add it to the end
tmpl.bytecodes.emplace_back(Bytecode::Op::Callback, num_args);
tmpl.bytecodes.back().str = static_cast<std::string>(name);
}
void parse_into(Template& tmpl, nonstd::string_view path) {
m_lexer.start(tmpl.content);
for (;;) {
get_next_token();
switch (m_tok.kind) {
case Token::Kind::Eof:
if (!m_if_stack.empty()) inja_throw("parser_error", "unmatched if");
if (!m_loop_stack.empty()) inja_throw("parser_error", "unmatched for");
return;
case Token::Kind::Text:
tmpl.bytecodes.emplace_back(Bytecode::Op::PrintText, m_tok.text, 0u);
break;
case Token::Kind::StatementOpen:
get_next_token();
if (!parse_statement(tmpl, path)) {
inja_throw("parser_error", "expected statement, got '" + m_tok.describe() + "'");
}
if (m_tok.kind != Token::Kind::StatementClose) {
inja_throw("parser_error", "expected statement close, got '" + m_tok.describe() + "'");
}
break;
case Token::Kind::LineStatementOpen:
get_next_token();
parse_statement(tmpl, path);
if (m_tok.kind != Token::Kind::LineStatementClose &&
m_tok.kind != Token::Kind::Eof) {
inja_throw("parser_error", "expected line statement close, got '" + m_tok.describe() + "'");
}
break;
case Token::Kind::ExpressionOpen:
get_next_token();
if (!parse_expression(tmpl)) {
inja_throw("parser_error", "expected expression, got '" + m_tok.describe() + "'");
}
append_function(tmpl, Bytecode::Op::PrintValue, 1);
if (m_tok.kind != Token::Kind::ExpressionClose) {
inja_throw("parser_error", "expected expression close, got '" + m_tok.describe() + "'");
}
break;
case Token::Kind::CommentOpen:
get_next_token();
if (m_tok.kind != Token::Kind::CommentClose) {
inja_throw("parser_error", "expected comment close, got '" + m_tok.describe() + "'");
}
break;
default:
inja_throw("parser_error", "unexpected token '" + m_tok.describe() + "'");
break;
}
}
}
Template parse(nonstd::string_view input, nonstd::string_view path) {
Template result;
result.content = static_cast<std::string>(input);
parse_into(result, path);
return result;
}
Template parse(nonstd::string_view input) {
return parse(input, "./");
}
Template parse_template(nonstd::string_view filename) {
Template result;
result.content = load_file(filename);
nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
// StringRef path = sys::path::parent_path(filename);
Parser(m_config, m_lexer.get_config(), m_included_templates).parse_into(result, path);
return result;
}
std::string load_file(nonstd::string_view filename) {
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;
Lexer m_lexer;
Token m_tok;
Token m_peek_tok;
bool m_have_peek_tok {false};
TemplateStorage& m_included_templates;
const ParserStatic& m_static;
struct IfData {
unsigned int prev_cond_jump;
std::vector<unsigned int> uncond_jumps;
explicit IfData(unsigned int condJump): prev_cond_jump(condJump) {}
};
std::vector<IfData> m_if_stack;
std::vector<unsigned int> m_loop_stack;
void get_next_token() {
if (m_have_peek_tok) {
m_tok = m_peek_tok;
m_have_peek_tok = false;
} else {
m_tok = m_lexer.scan();
}
}
void get_peek_token() {
if (!m_have_peek_tok) {
m_peek_tok = m_lexer.scan();
m_have_peek_tok = true;
}
}
};
} // namespace inja
#endif // PANTOR_INJA_PARSER_HPP
// #include "polyfill.hpp"
#ifndef PANTOR_INJA_POLYFILL_HPP
#define PANTOR_INJA_POLYFILL_HPP
#if __cplusplus < 201402L
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace stdinja {
template<class T> struct _Unique_if {
typedef std::unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]> {
typedef std::unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]> {
typedef void _Known_bound;
};
template<class T, class... Args>
typename _Unique_if<T>::_Single_object
make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n) {
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[n]());
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
}
#else
namespace stdinja = std;
#endif // memory */
#endif // PANTOR_INJA_POLYFILL_HPP
// #include "renderer.hpp"
#ifndef PANTOR_INJA_RENDERER_HPP
#define PANTOR_INJA_RENDERER_HPP
#include <algorithm>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include <nlohmann/json.hpp>
// #include "bytecode.hpp"
// #include "template.hpp"
// #include "utils.hpp"
namespace inja {
inline nonstd::string_view convert_dot_to_json_pointer(nonstd::string_view dot, std::string& out) {
out.clear();
do {
nonstd::string_view part;
std::tie(part, dot) = string_view::split(dot, '.');
out.push_back('/');
out.append(part.begin(), part.end());
} while (!dot.empty());
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();
bool has_imm = ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop);
// get args from stack
unsigned int pop_args = bc.args;
if (has_imm) {
pop_args -= 1;
}
for (auto i = std::prev(m_stack.end(), pop_args); i != m_stack.end(); i++) {
m_tmp_args.push_back(&(*i));
}
// get immediate arg
if (has_imm) {
m_tmp_args.push_back(get_imm(bc));
}
return m_tmp_args;
}
void pop_args(const Bytecode& bc) {
unsigned int popArgs = bc.args;
if ((bc.flags & Bytecode::Flag::ValueMask) != Bytecode::Flag::ValuePop) {
popArgs -= 1;
}
for (unsigned int i = 0; i < popArgs; ++i) {
m_stack.pop_back();
}
}
const json* get_imm(const Bytecode& bc) {
std::string ptr_buffer;
nonstd::string_view ptr;
switch (bc.flags & Bytecode::Flag::ValueMask) {
case Bytecode::Flag::ValuePop:
return nullptr;
case Bytecode::Flag::ValueImmediate:
return &bc.value;
case Bytecode::Flag::ValueLookupDot:
ptr = convert_dot_to_json_pointer(bc.str, ptr_buffer);
break;
case Bytecode::Flag::ValueLookupPointer:
ptr_buffer += '/';
ptr_buffer += bc.str;
ptr = ptr_buffer;
break;
}
try {
return &m_data->at(json::json_pointer(ptr.data()));
} catch (std::exception&) {
// try to evaluate as a no-argument callback
if (auto callback = m_callbacks.find_callback(bc.str, 0)) {
std::vector<const json*> arguments {};
m_tmp_val = callback(arguments);
return &m_tmp_val;
}
inja_throw("render_error", "variable '" + static_cast<std::string>(bc.str) + "' not found");
return nullptr;
}
}
bool truthy(const json& var) const {
if (var.empty()) {
return false;
} else if (var.is_number()) {
return (var != 0);
} else if (var.is_string()) {
return !var.empty();
}
try {
return var.get<bool>();
} catch (json::type_error& e) {
inja_throw("json_error", e.what());
throw;
}
}
void update_loop_data() {
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;
auto& loopData = level.data["loop"];
loopData["index"] = level.index;
loopData["index1"] = level.index + 1;
loopData["is_first"] = (level.index == 0);
loopData["is_last"] = (level.index == level.size - 1);
} else {
level.data[static_cast<std::string>(level.key_name)] = level.map_it->first;
level.data[static_cast<std::string>(level.value_name)] = *level.map_it->second;
}
}
const TemplateStorage& m_included_templates;
const FunctionStorage& m_callbacks;
std::vector<json> m_stack;
struct LoopLevel {
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
json data; // data with loop info added
json values; // values to iterate over
// loop over list
size_t index; // current list index
size_t size; // length of list
// 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
};
std::vector<LoopLevel> m_loop_stack;
const json* m_data;
std::vector<const json*> m_tmp_args;
json m_tmp_val;
public:
Renderer(const TemplateStorage& included_templates, const FunctionStorage& callbacks): m_included_templates(included_templates), m_callbacks(callbacks) {
m_stack.reserve(16);
m_tmp_args.reserve(4);
m_loop_stack.reserve(16);
}
void render_to(std::ostream& os, const Template& tmpl, const json& data) {
m_data = &data;
for (size_t i = 0; i < tmpl.bytecodes.size(); ++i) {
const auto& bc = tmpl.bytecodes[i];
switch (bc.op) {
case Bytecode::Op::Nop: {
break;
}
case Bytecode::Op::PrintText: {
os << bc.str;
break;
}
case Bytecode::Op::PrintValue: {
const json& val = *get_args(bc)[0];
if (val.is_string()) {
os << val.get_ref<const std::string&>();
} else {
os << val.dump();
}
pop_args(bc);
break;
}
case Bytecode::Op::Push: {
m_stack.emplace_back(*get_imm(bc));
break;
}
case Bytecode::Op::Upper: {
auto result = get_args(bc)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Lower: {
auto result = get_args(bc)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Range: {
int number = get_args(bc)[0]->get<int>();
std::vector<int> result(number);
std::iota(std::begin(result), std::end(result), 0);
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Length: {
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;
}
case Bytecode::Op::Sort: {
auto result = get_args(bc)[0]->get<std::vector<json>>();
std::sort(result.begin(), result.end());
pop_args(bc);
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);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Last: {
auto result = get_args(bc)[0]->back();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Round: {
auto args = get_args(bc);
double number = args[0]->get<double>();
int precision = args[1]->get<int>();
pop_args(bc);
m_stack.emplace_back(std::round(number * std::pow(10.0, precision)) / std::pow(10.0, precision));
break;
}
case Bytecode::Op::DivisibleBy: {
auto args = get_args(bc);
int number = args[0]->get<int>();
int divisor = args[1]->get<int>();
pop_args(bc);
m_stack.emplace_back((divisor != 0) && (number % divisor == 0));
break;
}
case Bytecode::Op::Odd: {
int number = get_args(bc)[0]->get<int>();
pop_args(bc);
m_stack.emplace_back(number % 2 != 0);
break;
}
case Bytecode::Op::Even: {
int number = get_args(bc)[0]->get<int>();
pop_args(bc);
m_stack.emplace_back(number % 2 == 0);
break;
}
case Bytecode::Op::Max: {
auto args = get_args(bc);
auto result = *std::max_element(args[0]->begin(), args[0]->end());
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Min: {
auto args = get_args(bc);
auto result = *std::min_element(args[0]->begin(), args[0]->end());
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Not: {
bool result = !truthy(*get_args(bc)[0]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::And: {
auto args = get_args(bc);
bool result = truthy(*args[0]) && truthy(*args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Or: {
auto args = get_args(bc);
bool result = truthy(*args[0]) || truthy(*args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::In: {
auto args = get_args(bc);
bool result = std::find(args[1]->begin(), args[1]->end(), *args[0]) !=
args[1]->end();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Equal: {
auto args = get_args(bc);
bool result = (*args[0] == *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Greater: {
auto args = get_args(bc);
bool result = (*args[0] > *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Less: {
auto args = get_args(bc);
bool result = (*args[0] < *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::GreaterEqual: {
auto args = get_args(bc);
bool result = (*args[0] >= *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::LessEqual: {
auto args = get_args(bc);
bool result = (*args[0] <= *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Different: {
auto args = get_args(bc);
bool result = (*args[0] != *args[1]);
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Float: {
double result =
std::stod(get_args(bc)[0]->get_ref<const std::string&>());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Int: {
int result = std::stoi(get_args(bc)[0]->get_ref<const std::string&>());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Exists: {
auto&& name = get_args(bc)[0]->get_ref<const std::string&>();
bool result = (data.find(name) != data.end());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::ExistsInObject: {
auto args = get_args(bc);
auto&& name = args[1]->get_ref<const std::string&>();
bool result = (args[0]->find(name) != args[0]->end());
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsBoolean: {
bool result = get_args(bc)[0]->is_boolean();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsNumber: {
bool result = get_args(bc)[0]->is_number();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsInteger: {
bool result = get_args(bc)[0]->is_number_integer();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsFloat: {
bool result = get_args(bc)[0]->is_number_float();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsObject: {
bool result = get_args(bc)[0]->is_object();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsArray: {
bool result = get_args(bc)[0]->is_array();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::IsString: {
bool result = get_args(bc)[0]->is_string();
pop_args(bc);
m_stack.emplace_back(result);
break;
}
case Bytecode::Op::Default: {
// default needs to be a bit "magic"; we can't evaluate the first
// argument during the push operation, so we swap the arguments during
// the parse phase so the second argument is pushed on the stack and
// the first argument is in the immediate
try {
const json* imm = get_imm(bc);
// if no exception was raised, replace the stack value with it
m_stack.back() = *imm;
} catch (std::exception&) {
// couldn't read immediate, just leave the stack as is
}
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, *m_data);
break;
case Bytecode::Op::Callback: {
auto callback = m_callbacks.find_callback(bc.str, bc.args);
if (!callback) {
inja_throw("render_error", "function '" + static_cast<std::string>(bc.str) + "' (" + std::to_string(static_cast<unsigned int>(bc.args)) + ") not found");
}
json result = callback(get_args(bc));
pop_args(bc);
m_stack.emplace_back(std::move(result));
break;
}
case Bytecode::Op::Jump: {
i = bc.args - 1; // -1 due to ++i in loop
break;
}
case Bytecode::Op::ConditionalJump: {
if (!truthy(m_stack.back())) {
i = bc.args - 1; // -1 due to ++i in loop
}
m_stack.pop_back();
break;
}
case Bytecode::Op::StartLoop: {
// jump past loop body if empty
if (m_stack.back().empty()) {
m_stack.pop_back();
i = bc.args; // ++i in loop will take it past EndLoop
break;
}
m_loop_stack.emplace_back();
LoopLevel& level = m_loop_stack.back();
level.value_name = bc.str;
level.values = std::move(m_stack.back());
level.data = (*m_data);
m_stack.pop_back();
if (bc.value.is_string()) {
// map iterator
if (!level.values.is_object()) {
m_loop_stack.pop_back();
inja_throw("render_error", "for key, value requires object");
}
level.loop_type = LoopLevel::Type::Map;
level.key_name = bc.value.get_ref<const std::string&>();
// sort by key
for (auto it = level.values.begin(), end = level.values.end(); it != end; ++it) {
level.map_values.emplace_back(it.key(), &it.value());
}
std::sort(level.map_values.begin(), level.map_values.end(), [](const LoopLevel::KeyValue& a, const LoopLevel::KeyValue& b) { return a.first < b.first; });
level.map_it = level.map_values.begin();
} else {
if (!level.values.is_array()) {
m_loop_stack.pop_back();
inja_throw("render_error", "type must be array");
}
// list iterator
level.loop_type = LoopLevel::Type::Array;
level.index = 0;
level.size = level.values.size();
}
// provide parent access in nested loop
auto parent_loop_it = level.data.find("loop");
if (parent_loop_it != level.data.end()) {
json loop_copy = *parent_loop_it;
(*parent_loop_it)["parent"] = std::move(loop_copy);
}
// set "current" data to loop data
m_data = &level.data;
update_loop_data();
break;
}
case Bytecode::Op::EndLoop: {
if (m_loop_stack.empty()) {
inja_throw("render_error", "unexpected state in renderer");
}
LoopLevel& level = m_loop_stack.back();
bool done;
if (level.loop_type == LoopLevel::Type::Array) {
level.index += 1;
done = (level.index == level.values.size());
} else {
level.map_it += 1;
done = (level.map_it == level.map_values.end());
}
if (done) {
m_loop_stack.pop_back();
// set "current" data to outer loop data or main data as appropriate
if (!m_loop_stack.empty()) {
m_data = &m_loop_stack.back().data;
} else {
m_data = &data;
}
break;
}
update_loop_data();
// jump back to start of loop
i = bc.args - 1; // -1 due to ++i in loop
break;
}
default: {
inja_throw("render_error", "unknown op in renderer: " + std::to_string(static_cast<unsigned int>(bc.op)));
}
}
}
}
};
} // namespace inja
#endif // PANTOR_INJA_RENDERER_HPP
// #include "string_view.hpp"
// #include "template.hpp"
// #include "utils.hpp"
namespace inja {
using namespace nlohmann;
/*!
* \brief Class for changing the configuration.
*/
class Environment {
class Impl {
public:
std::string input_path;
std::string output_path;
LexerConfig lexer_config;
ParserConfig parser_config;
FunctionStorage callbacks;
TemplateStorage included_templates;
};
std::unique_ptr<Impl> m_impl;
public:
Environment(): Environment("") { }
explicit Environment(const std::string& global_path): m_impl(stdinja::make_unique<Impl>()) {
m_impl->input_path = global_path;
m_impl->output_path = global_path;
}
explicit Environment(const std::string& input_path, const std::string& output_path): m_impl(stdinja::make_unique<Impl>()) {
m_impl->input_path = input_path;
m_impl->output_path = output_path;
}
/// Sets the opener and closer for template statements
void set_statement(const std::string& open, const std::string& close) {
m_impl->lexer_config.statement_open = open;
m_impl->lexer_config.statement_close = close;
m_impl->lexer_config.update_open_chars();
}
/// Sets the opener for template line statements
void set_line_statement(const std::string& open) {
m_impl->lexer_config.line_statement = open;
m_impl->lexer_config.update_open_chars();
}
/// Sets the opener and closer for template expressions
void set_expression(const std::string& open, const std::string& close) {
m_impl->lexer_config.expression_open = open;
m_impl->lexer_config.expression_close = close;
m_impl->lexer_config.update_open_chars();
}
/// Sets the opener and closer for template comments
void set_comment(const std::string& open, const std::string& close) {
m_impl->lexer_config.comment_open = open;
m_impl->lexer_config.comment_close = close;
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;
}
Template parse(nonstd::string_view input) {
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
return parser.parse(input);
}
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));
}
std::string render(nonstd::string_view input, const json& data) {
return render(parse(input), data);
}
std::string render(const Template& tmpl, const json& data) {
std::stringstream os;
render_to(os, tmpl, data);
return os.str();
}
std::string render_file(const std::string& filename, const json& 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);
}
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();
}
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();
}
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);
}
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);
return os;
}
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);
}
json load_json(const std::string& filename) {
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);
}
/** Includes a template with a given name into the environment.
* Then, a template can be rendered in another template using the
* include "<name>" syntax.
*/
void include_template(const std::string& name, const Template& tmpl) {
m_impl->included_templates[name] = tmpl;
}
};
/*!
@brief render with default settings to a string
*/
inline std::string render(nonstd::string_view input, const json& data) {
return Environment().render(input, data);
}
/*!
@brief render with default settings to the given output stream
*/
inline void render_to(std::ostream& os, nonstd::string_view input, const json& data) {
Environment env;
env.render_to(os, env.parse(input), data);
}
}
#endif // PANTOR_INJA_ENVIRONMENT_HPP
// #include "string_view.hpp"
// #include "template.hpp"
// #include "parser.hpp"
// #include "renderer.hpp"
#endif // PANTOR_INJA_HPP

109
tools/jsonproc/jsonproc.cpp Executable file
View File

@ -0,0 +1,109 @@
// jsonproc.cpp
#include "jsonproc.h"
#include <map>
#include <string>
using std::string; using std::to_string;
#include <inja.hpp>
using namespace inja;
using json = nlohmann::json;
std::map<string, string> customVars;
void set_custom_var(string key, string value)
{
customVars[key] = value;
}
string get_custom_var(string key)
{
return customVars[key];
}
int main(int argc, char *argv[])
{
if (argc != 4)
FATAL_ERROR("USAGE: jsonproc <json-filepath> <template-filepath> <output-filepath>\n");
string jsonfilepath = argv[1];
string templateFilepath = argv[2];
string outputFilepath = argv[3];
Environment env;
// 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";
});
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) {
string key = args.at(0)->get<string>();
string value = args.at(1)->get<string>();
set_custom_var(key, value);
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);
});
env.add_callback("concat", 2, [](Arguments& args) {
string first = args.at(0)->get<string>();
string second = args.at(1)->get<string>();
return first + second;
});
env.add_callback("removePrefix", 2, [](Arguments& args) {
string rawValue = args.at(0)->get<string>();
string prefix = args.at(1)->get<string>();
string::size_type i = rawValue.find(prefix);
if (i != 0)
return rawValue;
return rawValue.erase(0, prefix.length());
});
env.add_callback("removeSuffix", 2, [](Arguments& args) {
string rawValue = args.at(0)->get<string>();
string suffix = args.at(1)->get<string>();
string::size_type i = rawValue.rfind(suffix);
if (i == string::npos)
return rawValue;
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);
}
catch (const std::exception& e)
{
FATAL_ERROR("JSONPROC_ERROR: %s\n", e.what());
}
return 0;
}

32
tools/jsonproc/jsonproc.h Executable file
View File

@ -0,0 +1,32 @@
// jsonproc.h
#ifndef JSONPROC_H
#define JSONPROC_H
#include <cstdlib>
#include <cstdio>
using std::fprintf; using std::exit;
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#endif // JSONPROC_H

20842
tools/jsonproc/nlohmann/json.hpp Executable file

File diff suppressed because it is too large Load Diff

1
tools/mapjson/.gitignore vendored Executable file
View File

@ -0,0 +1 @@
mapjson

24
tools/mapjson/Makefile Normal file
View File

@ -0,0 +1,24 @@
CXX ?= g++
CXXFLAGS := -Wall -std=c++11 -O2
SRCS := json11.cpp mapjson.cpp
HEADERS := mapjson.h
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
.PHONY: all clean
all: mapjson$(EXE)
@:
mapjson$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) mapjson mapjson.exe

786
tools/mapjson/json11.cpp Normal file
View File

@ -0,0 +1,786 @@
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "json11.h"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
namespace json11 {
static const int max_depth = 200;
using std::string;
using std::vector;
using std::map;
using std::make_shared;
using std::initializer_list;
using std::move;
/* Helper for representing null - just a do-nothing struct, plus comparison
* operators so the helpers in JsonValue work. We can't use nullptr_t because
* it may not be orderable.
*/
struct NullStruct {
bool operator==(NullStruct) const { return true; }
bool operator<(NullStruct) const { return false; }
};
/* * * * * * * * * * * * * * * * * * * *
* Serialization
*/
static void dump(NullStruct, string &out) {
out += "null";
}
static void dump(double value, string &out) {
if (std::isfinite(value)) {
char buf[32];
snprintf(buf, sizeof buf, "%.17g", value);
out += buf;
} else {
out += "null";
}
}
static void dump(int value, string &out) {
char buf[32];
snprintf(buf, sizeof buf, "%d", value);
out += buf;
}
static void dump(bool value, string &out) {
out += value ? "true" : "false";
}
static void dump(const string &value, string &out) {
out += '"';
for (size_t i = 0; i < value.length(); i++) {
const char ch = value[i];
if (ch == '\\') {
out += "\\\\";
} else if (ch == '"') {
out += "\\\"";
} else if (ch == '\b') {
out += "\\b";
} else if (ch == '\f') {
out += "\\f";
} else if (ch == '\n') {
out += "\\n";
} else if (ch == '\r') {
out += "\\r";
} else if (ch == '\t') {
out += "\\t";
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
char buf[8];
snprintf(buf, sizeof buf, "\\u%04x", ch);
out += buf;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
out += "\\u2028";
i += 2;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
out += "\\u2029";
i += 2;
} else {
out += ch;
}
}
out += '"';
}
static void dump(const Json::array &values, string &out) {
bool first = true;
out += "[";
for (const auto &value : values) {
if (!first)
out += ", ";
value.dump(out);
first = false;
}
out += "]";
}
static void dump(const Json::object &values, string &out) {
bool first = true;
out += "{";
for (const auto &kv : values) {
if (!first)
out += ", ";
dump(kv.first, out);
out += ": ";
kv.second.dump(out);
first = false;
}
out += "}";
}
void Json::dump(string &out) const {
m_ptr->dump(out);
}
/* * * * * * * * * * * * * * * * * * * *
* Value wrappers
*/
template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:
// Constructors
explicit Value(const T &value) : m_value(value) {}
explicit Value(T &&value) : m_value(move(value)) {}
// Get type tag
Json::Type type() const override {
return tag;
}
// Comparisons
bool equals(const JsonValue * other) const override {
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
}
bool less(const JsonValue * other) const override {
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
}
const T m_value;
void dump(string &out) const override { json11::dump(m_value, out); }
};
class JsonDouble final : public Value<Json::NUMBER, double> {
double number_value() const override { return m_value; }
int int_value() const override { return static_cast<int>(m_value); }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonDouble(double value) : Value(value) {}
};
class JsonInt final : public Value<Json::NUMBER, int> {
double number_value() const override { return m_value; }
int int_value() const override { return m_value; }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonInt(int value) : Value(value) {}
};
class JsonBoolean final : public Value<Json::BOOL, bool> {
bool bool_value() const override { return m_value; }
public:
explicit JsonBoolean(bool value) : Value(value) {}
};
class JsonString final : public Value<Json::STRING, string> {
const string &string_value() const override { return m_value; }
public:
explicit JsonString(const string &value) : Value(value) {}
explicit JsonString(string &&value) : Value(move(value)) {}
};
class JsonArray final : public Value<Json::ARRAY, Json::array> {
const Json::array &array_items() const override { return m_value; }
const Json & operator[](size_t i) const override;
public:
explicit JsonArray(const Json::array &value) : Value(value) {}
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
};
class JsonObject final : public Value<Json::OBJECT, Json::object> {
const Json::object &object_items() const override { return m_value; }
const Json & operator[](const string &key) const override;
public:
explicit JsonObject(const Json::object &value) : Value(value) {}
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
};
class JsonNull final : public Value<Json::NUL, NullStruct> {
public:
JsonNull() : Value({}) {}
};
/* * * * * * * * * * * * * * * * * * * *
* Static globals - static-init-safe
*/
struct Statics {
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
const string empty_string;
const vector<Json> empty_vector;
const map<string, Json> empty_map;
Statics() {}
};
static const Statics & statics() {
static const Statics s {};
return s;
}
static const Json & static_null() {
// This has to be separate, not in Statics, because Json() accesses statics().null.
static const Json json_null;
return json_null;
}
/* * * * * * * * * * * * * * * * * * * *
* Constructors
*/
Json::Json() noexcept : m_ptr(statics().null) {}
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
/* * * * * * * * * * * * * * * * * * * *
* Accessors
*/
Json::Type Json::type() const { return m_ptr->type(); }
double Json::number_value() const { return m_ptr->number_value(); }
int Json::int_value() const { return m_ptr->int_value(); }
bool Json::bool_value() const { return m_ptr->bool_value(); }
const string & Json::string_value() const { return m_ptr->string_value(); }
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
double JsonValue::number_value() const { return 0; }
int JsonValue::int_value() const { return 0; }
bool JsonValue::bool_value() const { return false; }
const string & JsonValue::string_value() const { return statics().empty_string; }
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
const Json & JsonObject::operator[] (const string &key) const {
auto iter = m_value.find(key);
return (iter == m_value.end()) ? static_null() : iter->second;
}
const Json & JsonArray::operator[] (size_t i) const {
if (i >= m_value.size()) return static_null();
else return m_value[i];
}
/* * * * * * * * * * * * * * * * * * * *
* Comparison
*/
bool Json::operator== (const Json &other) const {
if (m_ptr == other.m_ptr)
return true;
if (m_ptr->type() != other.m_ptr->type())
return false;
return m_ptr->equals(other.m_ptr.get());
}
bool Json::operator< (const Json &other) const {
if (m_ptr == other.m_ptr)
return false;
if (m_ptr->type() != other.m_ptr->type())
return m_ptr->type() < other.m_ptr->type();
return m_ptr->less(other.m_ptr.get());
}
/* * * * * * * * * * * * * * * * * * * *
* Parsing
*/
/* esc(c)
*
* Format char c suitable for printing in an error message.
*/
static inline string esc(char c) {
char buf[12];
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
} else {
snprintf(buf, sizeof buf, "(%d)", c);
}
return string(buf);
}
static inline bool in_range(long x, long lower, long upper) {
return (x >= lower && x <= upper);
}
namespace {
/* JsonParser
*
* Object that tracks all state of an in-progress parse.
*/
struct JsonParser final {
/* State
*/
const string &str;
size_t i;
string &err;
bool failed;
const JsonParse strategy;
/* fail(msg, err_ret = Json())
*
* Mark this parse as failed.
*/
Json fail(string &&msg) {
return fail(move(msg), Json());
}
template <typename T>
T fail(string &&msg, const T err_ret) {
if (!failed)
err = std::move(msg);
failed = true;
return err_ret;
}
/* consume_whitespace()
*
* Advance until the current character is non-whitespace.
*/
void consume_whitespace() {
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
i++;
}
/* consume_comment()
*
* Advance comments (c-style inline and multiline).
*/
bool consume_comment() {
bool comment_found = false;
if (str[i] == '/') {
i++;
if (i == str.size())
return fail("unexpected end of input after start of comment", false);
if (str[i] == '/') { // inline comment
i++;
// advance until next line, or end of input
while (i < str.size() && str[i] != '\n') {
i++;
}
comment_found = true;
}
else if (str[i] == '*') { // multiline comment
i++;
if (i > str.size()-2)
return fail("unexpected end of input inside multi-line comment", false);
// advance until closing tokens
while (!(str[i] == '*' && str[i+1] == '/')) {
i++;
if (i > str.size()-2)
return fail(
"unexpected end of input inside multi-line comment", false);
}
i += 2;
comment_found = true;
}
else
return fail("malformed comment", false);
}
return comment_found;
}
/* consume_garbage()
*
* Advance until the current character is non-whitespace and non-comment.
*/
void consume_garbage() {
consume_whitespace();
if(strategy == JsonParse::COMMENTS) {
bool comment_found = false;
do {
comment_found = consume_comment();
if (failed) return;
consume_whitespace();
}
while(comment_found);
}
}
/* get_next_token()
*
* Return the next non-whitespace character. If the end of the input is reached,
* flag an error and return 0.
*/
char get_next_token() {
consume_garbage();
if (failed) return static_cast<char>(0);
if (i == str.size())
return fail("unexpected end of input", static_cast<char>(0));
return str[i++];
}
/* encode_utf8(pt, out)
*
* Encode pt as UTF-8 and add it to out.
*/
void encode_utf8(long pt, string & out) {
if (pt < 0)
return;
if (pt < 0x80) {
out += static_cast<char>(pt);
} else if (pt < 0x800) {
out += static_cast<char>((pt >> 6) | 0xC0);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
out += static_cast<char>((pt >> 12) | 0xE0);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else {
out += static_cast<char>((pt >> 18) | 0xF0);
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
}
}
/* parse_string()
*
* Parse a string, starting at the current position.
*/
string parse_string() {
string out;
long last_escaped_codepoint = -1;
while (true) {
if (i == str.size())
return fail("unexpected end of input in string", "");
char ch = str[i++];
if (ch == '"') {
encode_utf8(last_escaped_codepoint, out);
return out;
}
if (in_range(ch, 0, 0x1f))
return fail("unescaped " + esc(ch) + " in string", "");
// The usual case: non-escaped characters
if (ch != '\\') {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
out += ch;
continue;
}
// Handle escapes
if (i == str.size())
return fail("unexpected end of input in string", "");
ch = str[i++];
if (ch == 'u') {
// Extract 4-byte escape sequence
string esc = str.substr(i, 4);
// Explicitly check length of the substring. The following loop
// relies on std::string returning the terminating NUL when
// accessing str[length]. Checking here reduces brittleness.
if (esc.length() < 4) {
return fail("bad \\u escape: " + esc, "");
}
for (size_t j = 0; j < 4; j++) {
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
&& !in_range(esc[j], '0', '9'))
return fail("bad \\u escape: " + esc, "");
}
long codepoint = strtol(esc.data(), nullptr, 16);
// JSON specifies that characters outside the BMP shall be encoded as a pair
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
// whether we're in the middle of such a beast: the previous codepoint was an
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
// Reassemble the two surrogate pairs into one astral-plane character, per
// the UTF-16 algorithm.
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
| (codepoint - 0xDC00)) + 0x10000, out);
last_escaped_codepoint = -1;
} else {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = codepoint;
}
i += 4;
continue;
}
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
if (ch == 'b') {
out += '\b';
} else if (ch == 'f') {
out += '\f';
} else if (ch == 'n') {
out += '\n';
} else if (ch == 'r') {
out += '\r';
} else if (ch == 't') {
out += '\t';
} else if (ch == '"' || ch == '\\' || ch == '/') {
out += ch;
} else {
return fail("invalid escape character " + esc(ch), "");
}
}
}
/* parse_number()
*
* Parse a double.
*/
Json parse_number() {
size_t start_pos = i;
if (str[i] == '-')
i++;
// Integer part
if (str[i] == '0') {
i++;
if (in_range(str[i], '0', '9'))
return fail("leading 0s not permitted in numbers");
} else if (in_range(str[i], '1', '9')) {
i++;
while (in_range(str[i], '0', '9'))
i++;
} else {
return fail("invalid " + esc(str[i]) + " in number");
}
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
return std::atoi(str.c_str() + start_pos);
}
// Decimal part
if (str[i] == '.') {
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in fractional part");
while (in_range(str[i], '0', '9'))
i++;
}
// Exponent part
if (str[i] == 'e' || str[i] == 'E') {
i++;
if (str[i] == '+' || str[i] == '-')
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in exponent");
while (in_range(str[i], '0', '9'))
i++;
}
return std::strtod(str.c_str() + start_pos, nullptr);
}
/* expect(str, res)
*
* Expect that 'str' starts at the character that was just read. If it does, advance
* the input and return res. If not, flag an error.
*/
Json expect(const string &expected, Json res) {
assert(i != 0);
i--;
if (str.compare(i, expected.length(), expected) == 0) {
i += expected.length();
return res;
} else {
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
}
}
/* parse_json()
*
* Parse a JSON object.
*/
Json parse_json(int depth) {
if (depth > max_depth) {
return fail("exceeded maximum nesting depth");
}
char ch = get_next_token();
if (failed)
return Json();
if (ch == '-' || (ch >= '0' && ch <= '9')) {
i--;
return parse_number();
}
if (ch == 't')
return expect("true", true);
if (ch == 'f')
return expect("false", false);
if (ch == 'n')
return expect("null", Json());
if (ch == '"')
return parse_string();
if (ch == '{') {
map<string, Json> data;
ch = get_next_token();
if (ch == '}')
return data;
while (1) {
if (ch != '"')
return fail("expected '\"' in object, got " + esc(ch));
string key = parse_string();
if (failed)
return Json();
ch = get_next_token();
if (ch != ':')
return fail("expected ':' in object, got " + esc(ch));
data[std::move(key)] = parse_json(depth + 1);
if (failed)
return Json();
ch = get_next_token();
if (ch == '}')
break;
if (ch != ',')
return fail("expected ',' in object, got " + esc(ch));
ch = get_next_token();
}
return data;
}
if (ch == '[') {
vector<Json> data;
ch = get_next_token();
if (ch == ']')
return data;
while (1) {
i--;
data.push_back(parse_json(depth + 1));
if (failed)
return Json();
ch = get_next_token();
if (ch == ']')
break;
if (ch != ',')
return fail("expected ',' in list, got " + esc(ch));
ch = get_next_token();
(void)ch;
}
return data;
}
return fail("expected value, got " + esc(ch));
}
};
}//namespace {
Json Json::parse(const string &in, string &err, JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
Json result = parser.parse_json(0);
// Check for any trailing garbage
parser.consume_garbage();
if (parser.failed)
return Json();
if (parser.i != in.size())
return parser.fail("unexpected trailing " + esc(in[parser.i]));
return result;
}
// Documented in json11.hpp
vector<Json> Json::parse_multi(const string &in,
std::string::size_type &parser_stop_pos,
string &err,
JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
parser_stop_pos = 0;
vector<Json> json_vec;
while (parser.i != in.size() && !parser.failed) {
json_vec.push_back(parser.parse_json(0));
if (parser.failed)
break;
// Check for another object
parser.consume_garbage();
if (parser.failed)
break;
parser_stop_pos = parser.i;
}
return json_vec;
}
/* * * * * * * * * * * * * * * * * * * *
* Shape-checking
*/
bool Json::has_shape(const shape & types, string & err) const {
if (!is_object()) {
err = "expected JSON object, got " + dump();
return false;
}
for (auto & item : types) {
if ((*this)[item.first].type() != item.second) {
err = "bad type for " + item.first + " in " + dump();
return false;
}
}
return true;
}
} // namespace json11

230
tools/mapjson/json11.h Normal file
View File

@ -0,0 +1,230 @@
/* json11
*
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
*
* The core object provided by the library is json11::Json. A Json object represents any JSON
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
* object (std::map).
*
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
* Json::parse (static) to parse a std::string as a Json object.
*
* Internally, the various types of Json object are represented by the JsonValue class
* hierarchy.
*
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
* so some JSON implementations distinguish between integers and floating-point numbers, while
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
* to JSON that will be *silently* changed by a round-trip through those implementations.
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
* provides integer helpers.
*
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
* will be exact for +/- 275 years.)
*/
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <initializer_list>
#ifdef _MSC_VER
#if _MSC_VER <= 1800 // VS 2013
#ifndef noexcept
#define noexcept throw()
#endif
#ifndef snprintf
#define snprintf _snprintf_s
#endif
#endif
#endif
namespace json11 {
enum JsonParse {
STANDARD, COMMENTS
};
class JsonValue;
class Json final {
public:
// Types
enum Type {
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
};
// Array and object typedefs
typedef std::vector<Json> array;
typedef std::map<std::string, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const std::string &value); // STRING
Json(std::string &&value); // STRING
Json(const char * value); // STRING
Json(const array &values); // ARRAY
Json(array &&values); // ARRAY
Json(const object &values); // OBJECT
Json(object &&values); // OBJECT
// Implicit constructor: anything with a to_json() function.
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
template <class M, typename std::enable_if<
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
int>::type = 0>
Json(const M & m) : Json(object(m.begin(), m.end())) {}
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
template <class V, typename std::enable_if<
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.
Json(void *) = delete;
// Accessors
Type type() const;
bool is_null() const { return type() == NUL; }
bool is_number() const { return type() == NUMBER; }
bool is_bool() const { return type() == BOOL; }
bool is_string() const { return type() == STRING; }
bool is_array() const { return type() == ARRAY; }
bool is_object() const { return type() == OBJECT; }
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
// distinguish between integer and non-integer numbers - number_value() and int_value()
// can both be applied to a NUMBER-typed object.
double number_value() const;
int int_value() const;
// Return the enclosed value if this is a boolean, false otherwise.
bool bool_value() const;
// Return the enclosed string if this is a string, "" otherwise.
const std::string &string_value() const;
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
const array &array_items() const;
// Return the enclosed std::map if this is an object, or an empty map otherwise.
const object &object_items() const;
// Return a reference to arr[i] if this is an array, Json() otherwise.
const Json & operator[](size_t i) const;
// Return a reference to obj[key] if this is an object, Json() otherwise.
const Json & operator[](const std::string &key) const;
// Serialize.
void dump(std::string &out) const;
std::string dump() const {
std::string out;
dump(out);
return out;
}
// Parse. If parse fails, return Json() and assign an error message to err.
static Json parse(const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static Json parse(const char * in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
if (in) {
return parse(std::string(in), err, strategy);
} else {
err = "null input";
return nullptr;
}
}
// Parse multiple objects, concatenated or separated by whitespace
static std::vector<Json> parse_multi(
const std::string & in,
std::string::size_type & parser_stop_pos,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static inline std::vector<Json> parse_multi(
const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
std::string::size_type parser_stop_pos;
return parse_multi(in, parser_stop_pos, err, strategy);
}
bool operator== (const Json &rhs) const;
bool operator< (const Json &rhs) const;
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
bool operator> (const Json &rhs) const { return (rhs < *this); }
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
/* has_shape(types, err)
*
* Return true if this is a JSON object and, for each item in types, has a field of
* the given type. If not, return false and set err to a descriptive message.
*/
typedef std::initializer_list<std::pair<std::string, Type>> shape;
bool has_shape(const shape & types, std::string & err) const;
private:
std::shared_ptr<JsonValue> m_ptr;
};
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
class JsonValue {
protected:
friend class Json;
friend class JsonInt;
friend class JsonDouble;
virtual Json::Type type() const = 0;
virtual bool equals(const JsonValue * other) const = 0;
virtual bool less(const JsonValue * other) const = 0;
virtual void dump(std::string &out) const = 0;
virtual double number_value() const;
virtual int int_value() const;
virtual bool bool_value() const;
virtual const std::string &string_value() const;
virtual const Json::array &array_items() const;
virtual const Json &operator[](size_t i) const;
virtual const Json::object &object_items() const;
virtual const Json &operator[](const std::string &key) const;
virtual ~JsonValue() {}
};
} // namespace json11

566
tools/mapjson/mapjson.cpp Normal file
View File

@ -0,0 +1,566 @@
// mapjson.cpp
#include <iostream>
using std::cout; using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <algorithm>
using std::sort; using std::find;
#include <map>
using std::map;
#include <fstream>
using std::ofstream; using std::ifstream;
#include <sstream>
using std::ostringstream;
#include <limits>
using std::numeric_limits;
#include "json11.h"
using json11::Json;
#include "mapjson.h"
string read_text_file(string filepath) {
ifstream in_file(filepath);
if (!in_file.is_open())
FATAL_ERROR("Cannot open file %s for reading.\n", filepath.c_str());
string text;
in_file.seekg(0, std::ios::end);
text.resize(in_file.tellg());
in_file.seekg(0, std::ios::beg);
in_file.read(&text[0], text.size());
in_file.close();
return text;
}
void write_text_file(string filepath, string text) {
ofstream out_file(filepath, std::ofstream::binary);
if (!out_file.is_open())
FATAL_ERROR("Cannot open file %s for writing.\n", filepath.c_str());
out_file << text;
out_file.close();
}
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;
for (auto &field : layouts_data["layouts"].array_items()) {
if (map_layout_id == field["id"].string_value())
matched.push_back(field);
}
if (matched.size() != 1)
FATAL_ERROR("Failed to find matching layout for %s.\n", map_layout_id.c_str());
Json layout = matched[0];
ostringstream text;
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/"
<< map_data["name"].string_value()
<< "/map.json\n@\n\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())
text << "\t.4byte " << map_data["shared_events_map"].string_value() << "_MapEvents\n";
else
text << "\t.4byte " << map_data["name"].string_value() << "_MapEvents\n";
if (map_data.object_items().find("shared_scripts_map") != map_data.object_items().end())
text << "\t.4byte " << map_data["shared_scripts_map"].string_value() << "_MapScripts\n";
else
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)
text << "\t.4byte " << map_data["name"].string_value() << "_MapConnections\n";
else
text << "\t.4byte 0x0\n";
text << "\t.2byte " << map_data["music"].string_value() << "\n"
<< "\t.2byte " << layout["id"].string_value() << "\n"
<< "\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.2byte 0\n";
if (version == "ruby")
text << "\t.byte " << map_data["show_map_name"].bool_value() << "\n";
else if (version == "emerald")
text << "\tmap_header_flags "
<< "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";
text << "\t.byte " << map_data["battle_scene"].string_value() << "\n\n";
return text.str();
}
string generate_map_connections_text(Json map_data) {
if (map_data["connections"] == Json())
return string("\n");
ostringstream text;
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/"
<< map_data["name"].string_value()
<< "/map.json\n@\n\n";
text << map_data["name"].string_value() << "_MapConnectionsList:\n";
for (auto &connection : map_data["connections"].array_items()) {
text << "\tconnection "
<< connection["direction"].string_value() << ", "
<< connection["offset"].int_value() << ", "
<< connection["map"].string_value() << "\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";
return text.str();
}
string generate_map_events_text(Json map_data) {
if (map_data.object_items().find("shared_events_map") != map_data.object_items().end())
return string("\n");
ostringstream text;
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/"
<< map_data["name"].string_value()
<< "/map.json\n@\n\n";
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() + "_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];
text << "\tobject_event " << i + 1 << ", "
<< obj_event["graphics_id"].string_value() << ", 0, "
<< 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"].string_value() << ", "
<< obj_event["trainer_sight_or_berry_tree_id"].string_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() << ", "
<< coord_event["var"].string_value() << ", "
<< coord_event["var_value"].string_value() << ", "
<< 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"] == "sign") {
text << "\tbg_event "
<< bg_event["x"].int_value() << ", "
<< bg_event["y"].int_value() << ", "
<< bg_event["elevation"].int_value() << ", "
<< bg_event["player_facing_dir"].string_value() << ", "
<< bg_event["script"].string_value() << "\n";
}
else 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() << "\n";
}
else if (bg_event["type"] == "secret_base") {
text << "\tbg_secret_base_event "
<< bg_event["x"].int_value() << ", "
<< bg_event["y"].int_value() << ", "
<< bg_event["elevation"].int_value() << ", "
<< bg_event["secret_base_id"].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, string version) {
string mapdata_err, layouts_err;
string mapdata_json_text = read_text_file(map_filepath);
string layouts_json_text = read_text_file(layouts_filepath);
Json map_data = Json::parse(mapdata_json_text, mapdata_err);
if (map_data == Json())
FATAL_ERROR("%s\n", mapdata_err.c_str());
Json layouts_data = Json::parse(layouts_json_text, layouts_err);
if (layouts_data == Json())
FATAL_ERROR("%s\n", layouts_err.c_str());
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);
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 generate_groups_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";
for (auto &key : groups_data["group_order"].array_items()) {
string group = key.string_value();
text << group << "::\n";
auto maps = groups_data[group].array_items();
for (Json &map_name : maps)
text << "\t.4byte " << map_name.string_value() << "\n";
text << "\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";
return text.str();
}
string generate_connections_text(Json groups_data) {
vector<Json> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name);
vector<Json> connections_include_order = groups_data["connections_include_order"].array_items();
if (connections_include_order.size() > 0)
sort(map_names.begin(), map_names.end(), [connections_include_order](const Json &a, const Json &b) {
auto iter_a = find(connections_include_order.begin(), connections_include_order.end(), a);
if (iter_a == connections_include_order.end())
iter_a = connections_include_order.begin() + numeric_limits<int>::max();
auto iter_b = find(connections_include_order.begin(), connections_include_order.end(), b);
if (iter_b == connections_include_order.end())
iter_b = connections_include_order.begin() + numeric_limits<int>::max();
return iter_a < iter_b;
});
ostringstream text;
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/" << map_name.string_value() << "/connections.inc\"\n";
return text.str();
}
string generate_headers_text(Json groups_data) {
vector<string> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name.string_value());
ostringstream text;
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";
return text.str();
}
string generate_events_text(Json groups_data) {
vector<string> map_names;
for (auto &group : groups_data["group_order"].array_items())
for (auto map_name : groups_data[group.string_value()].array_items())
map_names.push_back(map_name.string_value());
ostringstream text;
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 << "/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();
ostringstream text;
text << "#ifndef GUARD_CONSTANTS_MAP_GROUPS_H\n"
<< "#define GUARD_CONSTANTS_MAP_GROUPS_H\n\n";
text << "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n//\n\n";
int group_num = 0;
for (auto &group : groups_data["group_order"].array_items()) {
text << "// " << group.string_value() << "\n";
vector<Json> map_ids;
size_t max_length = 0;
for (auto &map_name : groups_data[group.string_value()].array_items()) {
string header_filepath = file_dir + map_name.string_value() + dir_separator + "map.json";
string err_str;
Json map_data = Json::parse(read_text_file(header_filepath), err_str);
map_ids.push_back(map_data["id"]);
if (map_data["id"].string_value().length() > max_length)
max_length = map_data["id"].string_value().length();
}
int map_id_num = 0;
for (Json map_id : map_ids) {
text << "#define " << map_id.string_value() << string((max_length - map_id.string_value().length() + 1), ' ')
<< "(" << map_id_num++ << " | (" << group_num << " << 8))\n";
}
text << "\n";
group_num++;
}
text << "#define MAP_GROUPS_COUNT " << group_num << "\n\n";
text << "#endif // GUARD_CONSTANTS_MAP_GROUPS_H\n";
return text.str();
}
void process_groups(string groups_filepath) {
string err;
Json groups_data = Json::parse(read_text_file(groups_filepath), err);
if (groups_data == Json())
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 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);
}
string generate_layout_headers_text(Json layouts_data) {
ostringstream text;
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n@\n\n";
for (auto &layout : layouts_data["layouts"].array_items()) {
string border_label = layout["name"].string_value() + "_Border";
string blockdata_label = layout["name"].string_value() + "_Blockdata";
text << border_label << "::\n"
<< "\t.incbin \"" << layout["border_filepath"].string_value() << "\"\n\n"
<< blockdata_label << "::\n"
<< "\t.incbin \"" << layout["blockdata_filepath"].string_value() << "\"\n\n"
<< "\t.align 2\n"
<< layout["name"].string_value() << "::\n"
<< "\t.4byte " << layout["width"].int_value() << "\n"
<< "\t.4byte " << layout["height"].int_value() << "\n"
<< "\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\n";
}
return text.str();
}
string generate_layouts_table_text(Json layouts_data) {
ostringstream text;
text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n@\n\n";
text << "\t.align 2\n"
<< layouts_data["layouts_table_label"].string_value() << "::\n";
for (auto &layout : layouts_data["layouts"].array_items())
text << "\t.4byte " << layout["name"].string_value() << "\n";
return text.str();
}
string generate_layouts_constants_text(Json layouts_data) {
ostringstream text;
text << "#ifndef GUARD_CONSTANTS_LAYOUTS_H\n"
<< "#define GUARD_CONSTANTS_LAYOUTS_H\n\n";
text << "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n//\n\n";
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";
return text.str();
}
void process_layouts(string layouts_filepath) {
string err;
Json layouts_data = Json::parse(read_text_file(layouts_filepath), err);
if (layouts_data == Json())
FATAL_ERROR("%s\n", err.c_str());
string layout_headers_text = generate_layout_headers_text(layouts_data);
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);
}
int main(int argc, char *argv[]) {
if (argc < 3)
FATAL_ERROR("USAGE: mapjson <mode> <game-version> [options]\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);
if (mode != "layouts" && mode != "map" && mode != "groups")
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");
string filepath(argv[3]);
string layouts_filepath(argv[4]);
process_map(filepath, layouts_filepath, version);
}
else if (mode == "groups") {
if (argc != 4)
FATAL_ERROR("USAGE: mapjson groups <game-version> <groups_file>\n");
string filepath(argv[3]);
process_groups(filepath);
}
else if (mode == "layouts") {
if (argc != 4)
FATAL_ERROR("USAGE: mapjson layouts <game-version> <layouts_file>\n");
string filepath(argv[3]);
process_layouts(filepath);
}
return 0;
}

31
tools/mapjson/mapjson.h Normal file
View File

@ -0,0 +1,31 @@
// mapjson.h
#ifndef MAPJSON_H
#define MAPJSON_H
#include <cstdio>
using std::fprintf; using std::exit;
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#endif // MAPJSON_H

1
tools/mid2agb/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
mid2agb

19
tools/mid2agb/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
tools/mid2agb/Makefile Normal file
View File

@ -0,0 +1,24 @@
CXX ?= g++
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
SRCS := agb.cpp error.cpp main.cpp midi.cpp tables.cpp
HEADERS := agb.h error.h main.h midi.h tables.h
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
.PHONY: all clean
all: mid2agb$(EXE)
@:
mid2agb$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) mid2agb mid2agb.exe

547
tools/mid2agb/agb.cpp Normal file
View File

@ -0,0 +1,547 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <vector>
#include "agb.h"
#include "main.h"
#include "midi.h"
#include "tables.h"
int g_agbTrack;
static std::string s_lastOpName;
static int s_blockNum;
static bool s_keepLastOpName;
static int s_lastNote;
static int s_lastVelocity;
static bool s_noteChanged;
static bool s_velocityChanged;
static bool s_inPattern;
static int s_extendedCommand;
static int s_memaccOp;
static int s_memaccParam1;
static int s_memaccParam2;
void PrintAgbHeader()
{
std::fprintf(g_outputFile, "\t.include \"MPlayDef.s\"\n\n");
std::fprintf(g_outputFile, "\t.equ\t%s_grp, voicegroup%03u\n", g_asmLabel.c_str(), g_voiceGroup);
std::fprintf(g_outputFile, "\t.equ\t%s_pri, %u\n", g_asmLabel.c_str(), g_priority);
if (g_reverb >= 0)
std::fprintf(g_outputFile, "\t.equ\t%s_rev, reverb_set+%u\n", g_asmLabel.c_str(), g_reverb);
else
std::fprintf(g_outputFile, "\t.equ\t%s_rev, 0\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.equ\t%s_mvl, %u\n", g_asmLabel.c_str(), g_masterVolume);
std::fprintf(g_outputFile, "\t.equ\t%s_key, %u\n", g_asmLabel.c_str(), 0);
std::fprintf(g_outputFile, "\t.equ\t%s_tbs, %u\n", g_asmLabel.c_str(), g_clocksPerBeat);
std::fprintf(g_outputFile, "\t.equ\t%s_exg, %u\n", g_asmLabel.c_str(), g_exactGateTime);
std::fprintf(g_outputFile, "\t.equ\t%s_cmp, %u\n", g_asmLabel.c_str(), g_compressionEnabled);
std::fprintf(g_outputFile, "\n\t.section .rodata\n");
std::fprintf(g_outputFile, "\t.global\t%s\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.align\t2\n");
}
void ResetTrackVars()
{
s_lastVelocity = -1;
s_lastNote = -1;
s_velocityChanged = false;
s_noteChanged = false;
s_keepLastOpName = false;
s_lastOpName = "";
s_inPattern = false;
}
void PrintWait(int wait)
{
if (wait > 0)
{
std::fprintf(g_outputFile, "\t.byte\tW%02d\n", wait);
s_velocityChanged = true;
s_noteChanged = true;
s_keepLastOpName = true;
}
}
void PrintOp(int wait, std::string name, const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t.byte\t\t");
if (format != nullptr)
{
if (!g_compressionEnabled || s_lastOpName != name)
{
std::fprintf(g_outputFile, "%s, ", name.c_str());
s_lastOpName = name;
}
else
{
std::fprintf(g_outputFile, " ");
}
std::vfprintf(g_outputFile, format, args);
}
else
{
std::fputs(name.c_str(), g_outputFile);
s_lastOpName = name;
}
std::fprintf(g_outputFile, "\n");
va_end(args);
PrintWait(wait);
}
void PrintByte(const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t.byte\t");
std::vfprintf(g_outputFile, format, args);
std::fprintf(g_outputFile, "\n");
s_velocityChanged = true;
s_noteChanged = true;
s_keepLastOpName = true;
va_end(args);
}
void PrintWord(const char *format, ...)
{
std::va_list args;
va_start(args, format);
std::fprintf(g_outputFile, "\t .word\t");
std::vfprintf(g_outputFile, format, args);
std::fprintf(g_outputFile, "\n");
va_end(args);
}
void PrintNote(const Event& event)
{
int note = event.note;
int velocity = g_noteVelocityLUT[event.param1];
int duration = -1;
if (event.param2 != -1)
duration = g_noteDurationLUT[event.param2];
int gateTimeParam = 0;
if (g_exactGateTime && duration != -1)
gateTimeParam = event.param2 - duration;
char gtpBuf[16];
if (gateTimeParam > 0)
std::snprintf(gtpBuf, sizeof(gtpBuf), ", gtp%u", gateTimeParam);
else
gtpBuf[0] = 0;
char opName[16];
if (duration == -1)
std::strcpy(opName, "TIE ");
else
std::snprintf(opName, sizeof(opName), "N%02u ", duration);
bool noteChanged = true;
bool velocityChanged = true;
if (g_compressionEnabled)
{
noteChanged = (note != s_lastNote);
velocityChanged = (velocity != s_lastVelocity);
}
if (s_keepLastOpName)
s_keepLastOpName = false;
else
s_lastOpName = "";
if (noteChanged || velocityChanged || (gateTimeParam > 0))
{
s_lastNote = note;
char noteBuf[16];
if (note >= 24)
std::snprintf(noteBuf, sizeof(noteBuf), g_noteTable[note % 12], note / 12 - 2);
else
std::snprintf(noteBuf, sizeof(noteBuf), g_minusNoteTable[note % 12], note / -12 + 2);
char velocityBuf[16];
if (velocityChanged || (gateTimeParam > 0))
{
s_lastVelocity = velocity;
std::snprintf(velocityBuf, sizeof(velocityBuf), ", v%03u", velocity);
}
else
{
velocityBuf[0] = 0;
}
PrintOp(event.time, opName, "%s%s%s", noteBuf, velocityBuf, gtpBuf);
}
else
{
PrintOp(event.time, opName, 0);
}
s_noteChanged = noteChanged;
s_velocityChanged = velocityChanged;
}
void PrintEndOfTieOp(const Event& event)
{
int note = event.note;
bool noteChanged = (note != s_lastNote);
if (!noteChanged || !s_noteChanged)
s_lastOpName = "";
if (!noteChanged && g_compressionEnabled)
{
PrintOp(event.time, "EOT ", nullptr);
}
else
{
s_lastNote = note;
if (note >= 24)
PrintOp(event.time, "EOT ", g_noteTable[note % 12], note / 12 - 2);
else
PrintOp(event.time, "EOT ", g_minusNoteTable[note % 12], note / -12 + 2);
}
s_noteChanged = noteChanged;
}
void PrintSeqLoopLabel(const Event& event)
{
s_blockNum = event.param1 + 1;
std::fprintf(g_outputFile, "%s_%u_B%u:\n", g_asmLabel.c_str(), g_agbTrack, s_blockNum);
PrintWait(event.time);
ResetTrackVars();
}
void PrintMemAcc(const Event& event)
{
switch (s_memaccOp)
{
case 0x00:
PrintByte("MEMACC, mem_set, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x01:
PrintByte("MEMACC, mem_add, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x02:
PrintByte("MEMACC, mem_sub, 0x%02X, %u", s_memaccParam1, event.param2);
break;
case 0x03:
PrintByte("MEMACC, mem_mem_set, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
case 0x04:
PrintByte("MEMACC, mem_mem_add, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
case 0x05:
PrintByte("MEMACC, mem_mem_sub, 0x%02X, 0x%02X", s_memaccParam1, event.param2);
break;
// TODO: everything else
case 0x06:
break;
case 0x07:
break;
case 0x08:
break;
case 0x09:
break;
case 0x0A:
break;
case 0x0B:
break;
case 0x0C:
break;
case 0x0D:
break;
case 0x0E:
break;
case 0x0F:
break;
case 0x10:
break;
case 0x11:
break;
case 0x46:
break;
case 0x47:
break;
case 0x48:
break;
case 0x49:
break;
case 0x4A:
break;
case 0x4B:
break;
case 0x4C:
break;
case 0x4D:
break;
case 0x4E:
break;
case 0x4F:
break;
case 0x50:
break;
case 0x51:
break;
default:
break;
}
PrintWait(event.time);
}
void PrintExtendedOp(const Event& event)
{
// TODO: support for other extended commands
switch (s_extendedCommand)
{
case 0x08:
PrintOp(event.time, "XCMD ", "xIECV , %u", event.param2);
break;
case 0x09:
PrintOp(event.time, "XCMD ", "xIECL , %u", event.param2);
break;
default:
PrintWait(event.time);
break;
}
}
void PrintControllerOp(const Event& event)
{
switch (event.param1)
{
case 0x01:
PrintOp(event.time, "MOD ", "%u", event.param2);
break;
case 0x07:
PrintOp(event.time, "VOL ", "%u*%s_mvl/mxv", event.param2, g_asmLabel.c_str());
break;
case 0x0A:
PrintOp(event.time, "PAN ", "c_v%+d", event.param2 - 64);
break;
case 0x0C:
case 0x10:
PrintMemAcc(event);
break;
case 0x0D:
s_memaccOp = event.param2;
PrintWait(event.time);
break;
case 0x0E:
s_memaccParam1 = event.param2;
PrintWait(event.time);
break;
case 0x0F:
s_memaccParam2 = event.param2;
PrintWait(event.time);
break;
case 0x11:
std::fprintf(g_outputFile, "%s_%u_L%u:\n", g_asmLabel.c_str(), g_agbTrack, event.param2);
PrintWait(event.time);
ResetTrackVars();
break;
case 0x14:
PrintOp(event.time, "BENDR ", "%u", event.param2);
break;
case 0x15:
PrintOp(event.time, "LFOS ", "%u", event.param2);
break;
case 0x16:
PrintOp(event.time, "MODT ", "%u", event.param2);
break;
case 0x18:
PrintOp(event.time, "TUNE ", "c_v%+d", event.param2 - 64);
break;
case 0x1A:
PrintOp(event.time, "LFODL ", "%u", event.param2);
break;
case 0x1D:
case 0x1F:
PrintExtendedOp(event);
break;
case 0x1E:
s_extendedCommand = event.param2;
// TODO: loop op
break;
case 0x21:
case 0x27:
PrintByte("PRIO , %u", event.param2);
PrintWait(event.time);
break;
default:
PrintWait(event.time);
break;
}
}
void PrintAgbTrack(std::vector<Event>& events)
{
std::fprintf(g_outputFile, "\n@**************** Track %u (Midi-Chn.%u) ****************@\n\n", g_agbTrack, g_midiChan + 1);
std::fprintf(g_outputFile, "%s_%u:\n", g_asmLabel.c_str(), g_agbTrack);
int wholeNoteCount = 0;
int loopEndBlockNum = 0;
ResetTrackVars();
bool foundVolBeforeNote = false;
for (const Event& event : events)
{
if (event.type == EventType::Note)
break;
if (event.type == EventType::Controller && event.param1 == 0x07)
{
foundVolBeforeNote = true;
break;
}
}
if (!foundVolBeforeNote)
PrintByte("\tVOL , 127*%s_mvl/mxv", g_asmLabel.c_str());
PrintWait(g_initialWait);
PrintByte("KEYSH , %s_key%+d", g_asmLabel.c_str(), 0);
for (unsigned i = 0; events[i].type != EventType::EndOfTrack; i++)
{
const Event& event = events[i];
if (IsPatternBoundary(event.type))
{
if (s_inPattern)
PrintByte("PEND");
s_inPattern = false;
}
if (event.type == EventType::WholeNoteMark || event.type == EventType::Pattern)
std::fprintf(g_outputFile, "@ %03d ----------------------------------------\n", wholeNoteCount++);
switch (event.type)
{
case EventType::Note:
PrintNote(event);
break;
case EventType::EndOfTie:
PrintEndOfTieOp(event);
break;
case EventType::Label:
PrintSeqLoopLabel(event);
break;
case EventType::LoopEnd:
PrintByte("GOTO");
PrintWord("%s_%u_B%u", g_asmLabel.c_str(), g_agbTrack, loopEndBlockNum);
PrintSeqLoopLabel(event);
break;
case EventType::LoopEndBegin:
PrintByte("GOTO");
PrintWord("%s_%u_B%u", g_asmLabel.c_str(), g_agbTrack, loopEndBlockNum);
PrintSeqLoopLabel(event);
loopEndBlockNum = s_blockNum;
break;
case EventType::LoopBegin:
PrintSeqLoopLabel(event);
loopEndBlockNum = s_blockNum;
break;
case EventType::WholeNoteMark:
if (event.param2 & 0x80000000)
{
std::fprintf(g_outputFile, "%s_%u_%03lu:\n", g_asmLabel.c_str(), g_agbTrack, (unsigned long)(event.param2 & 0x7FFFFFFF));
ResetTrackVars();
s_inPattern = true;
}
PrintWait(event.time);
break;
case EventType::Pattern:
PrintByte("PATT");
PrintWord("%s_%u_%03lu", g_asmLabel.c_str(), g_agbTrack, event.param2);
while (!IsPatternBoundary(events[i + 1].type))
i++;
ResetTrackVars();
break;
case EventType::Tempo:
PrintByte("TEMPO , %u*%s_tbs/2", 60000000 / event.param2, g_asmLabel.c_str());
PrintWait(event.time);
break;
case EventType::InstrumentChange:
PrintOp(event.time, "VOICE ", "%u", event.param1);
break;
case EventType::PitchBend:
PrintOp(event.time, "BEND ", "c_v%+d", event.param2 - 64);
break;
case EventType::Controller:
PrintControllerOp(event);
break;
default:
PrintWait(event.time);
break;
}
}
PrintByte("FINE");
}
void PrintAgbFooter()
{
int trackCount = g_agbTrack - 1;
std::fprintf(g_outputFile, "\n@******************************************************@\n");
std::fprintf(g_outputFile, "\t.align\t2\n");
std::fprintf(g_outputFile, "\n%s:\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.byte\t%u\t@ NumTrks\n", trackCount);
std::fprintf(g_outputFile, "\t.byte\t%u\t@ NumBlks\n", 0);
std::fprintf(g_outputFile, "\t.byte\t%s_pri\t@ Priority\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\t.byte\t%s_rev\t@ Reverb.\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\n");
std::fprintf(g_outputFile, "\t.word\t%s_grp\n", g_asmLabel.c_str());
std::fprintf(g_outputFile, "\n");
// track pointers
for (int i = 1; i <= trackCount; i++)
std::fprintf(g_outputFile, "\t.word\t%s_%u\n", g_asmLabel.c_str(), i);
std::fprintf(g_outputFile, "\n\t.end\n");
}

33
tools/mid2agb/agb.h Normal file
View File

@ -0,0 +1,33 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef AGB_H
#define AGB_H
#include <vector>
#include "midi.h"
void PrintAgbHeader();
void PrintAgbTrack(std::vector<Event>& events);
void PrintAgbFooter();
extern int g_agbTrack;
#endif // AGB_H

36
tools/mid2agb/error.cpp Normal file
View File

@ -0,0 +1,36 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
// Reports an error diagnostic and terminates the program.
[[noreturn]] void RaiseError(const char* format, ...)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::va_list args;
va_start(args, format);
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "error: %s\n", buffer);
va_end(args);
std::exit(1);
}

26
tools/mid2agb/error.h Normal file
View File

@ -0,0 +1,26 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ERROR_H
#define ERROR_H
[[noreturn]] void RaiseError(const char* format, ...);
#endif // ERROR_H

233
tools/mid2agb/main.cpp Normal file
View File

@ -0,0 +1,233 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cassert>
#include <string>
#include <set>
#include "main.h"
#include "error.h"
#include "midi.h"
#include "agb.h"
FILE* g_inputFile = nullptr;
FILE* g_outputFile = nullptr;
std::string g_asmLabel;
int g_masterVolume = 127;
int g_voiceGroup = 0;
int g_priority = 0;
int g_reverb = -1;
int g_clocksPerBeat = 1;
bool g_exactGateTime = false;
bool g_compressionEnabled = true;
[[noreturn]] static void PrintUsage()
{
std::printf(
"Usage: MID2AGB name [options]\n"
"\n"
" input_file filename(.mid) of MIDI file\n"
" output_file filename(.s) for AGB file (default:input_file)\n"
"\n"
"options -L??? label for assembler (default:output_file)\n"
" -V??? master volume (default:127)\n"
" -G??? voice group number (default:0)\n"
" -P??? priority (default:0)\n"
" -R??? reverb (default:off)\n"
" -X 48 clocks/beat (default:24 clocks/beat)\n"
" -E exact gate-time\n"
" -N no compression\n"
);
std::exit(1);
}
static std::string StripExtension(std::string s)
{
std::size_t pos = s.find_last_of('.');
if (pos > 0 && pos != std::string::npos)
{
s = s.substr(0, pos);
}
return s;
}
static std::string GetExtension(std::string s)
{
std::size_t pos = s.find_last_of('.');
if (pos > 0 && pos != std::string::npos)
{
return s.substr(pos + 1);
}
return "";
}
static std::string BaseName(std::string s)
{
std::size_t posAfterSlash = s.find_last_of("/\\");
if (posAfterSlash == std::string::npos)
posAfterSlash = 0;
else
posAfterSlash++;
std::size_t dotPos = s.find_first_of('.', posAfterSlash);
if (dotPos > posAfterSlash && dotPos != std::string::npos)
s = s.substr(posAfterSlash, dotPos - posAfterSlash);
return s;
}
static const char *GetArgument(int argc, char **argv, int& index)
{
assert(index >= 0 && index < argc);
const char *option = argv[index];
assert(option != nullptr);
assert(option[0] == '-');
// If there is text following the letter, return that.
if (std::strlen(option) >= 3)
return option + 2;
// Otherwise, try to get the next arg.
if (index + 1 < argc)
{
index++;
return argv[index];
}
else
{
return nullptr;
}
}
int main(int argc, char** argv)
{
std::string inputFilename;
std::string outputFilename;
for (int i = 1; i < argc; i++)
{
const char *option = argv[i];
if (option[0] == '-' && option[1] != '\0')
{
const char *arg;
switch (std::toupper(option[1]))
{
case 'E':
g_exactGateTime = true;
break;
case 'G':
arg = GetArgument(argc, argv, i);
if (arg == nullptr)
PrintUsage();
g_voiceGroup = std::stoi(arg);
break;
case 'L':
arg = GetArgument(argc, argv, i);
if (arg == nullptr)
PrintUsage();
g_asmLabel = arg;
break;
case 'N':
g_compressionEnabled = false;
break;
case 'P':
arg = GetArgument(argc, argv, i);
if (arg == nullptr)
PrintUsage();
g_priority = std::stoi(arg);
break;
case 'R':
arg = GetArgument(argc, argv, i);
if (arg == nullptr)
PrintUsage();
g_reverb = std::stoi(arg);
break;
case 'V':
arg = GetArgument(argc, argv, i);
if (arg == nullptr)
PrintUsage();
g_masterVolume = std::stoi(arg);
break;
case 'X':
g_clocksPerBeat = 2;
break;
default:
PrintUsage();
}
}
else
{
if (inputFilename.empty())
inputFilename = argv[i];
else if (outputFilename.empty())
outputFilename = argv[i];
else
PrintUsage();
}
}
if (inputFilename.empty())
PrintUsage();
if (GetExtension(inputFilename) != "mid")
RaiseError("input filename extension is not \"mid\"");
if (outputFilename.empty())
outputFilename = StripExtension(inputFilename) + ".s";
if (GetExtension(outputFilename) != "s")
RaiseError("output filename extension is not \"s\"");
if (g_asmLabel.empty())
g_asmLabel = BaseName(outputFilename);
g_inputFile = std::fopen(inputFilename.c_str(), "rb");
if (g_inputFile == nullptr)
RaiseError("failed to open \"%s\" for reading", inputFilename.c_str());
g_outputFile = std::fopen(outputFilename.c_str(), "w");
if (g_outputFile == nullptr)
RaiseError("failed to open \"%s\" for writing", outputFilename.c_str());
ReadMidiFileHeader();
PrintAgbHeader();
ReadMidiTracks();
PrintAgbFooter();
std::fclose(g_inputFile);
std::fclose(g_outputFile);
return 0;
}

39
tools/mid2agb/main.h Normal file
View File

@ -0,0 +1,39 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef MAIN_H
#define MAIN_H
#include <cstdio>
#include <string>
extern FILE* g_inputFile;
extern FILE* g_outputFile;
extern std::string g_asmLabel;
extern int g_masterVolume;
extern int g_voiceGroup;
extern int g_priority;
extern int g_reverb;
extern int g_clocksPerBeat;
extern bool g_exactGateTime;
extern bool g_compressionEnabled;
#endif // MAIN_H

964
tools/mid2agb/midi.cpp Normal file
View File

@ -0,0 +1,964 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cassert>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
#include "midi.h"
#include "main.h"
#include "error.h"
#include "agb.h"
#include "tables.h"
enum class MidiEventCategory
{
Control,
SysEx,
Meta,
Invalid,
};
MidiFormat g_midiFormat;
std::int_fast32_t g_midiTrackCount;
std::int16_t g_midiTimeDiv;
int g_midiChan;
std::int32_t g_initialWait;
static long s_trackDataStart;
static std::vector<Event> s_seqEvents;
static std::vector<Event> s_trackEvents;
static std::int32_t s_absoluteTime;
static int s_blockCount = 0;
static int s_minNote;
static int s_maxNote;
static int s_runningStatus;
void Seek(long offset)
{
if (std::fseek(g_inputFile, offset, SEEK_SET) != 0)
RaiseError("failed to seek to %l", offset);
}
void Skip(long offset)
{
if (std::fseek(g_inputFile, offset, SEEK_CUR) != 0)
RaiseError("failed to skip %l bytes", offset);
}
std::string ReadSignature()
{
char signature[4];
if (std::fread(signature, 4, 1, g_inputFile) != 1)
RaiseError("failed to read signature");
return std::string(signature, 4);
}
std::uint32_t ReadInt8()
{
int c = std::fgetc(g_inputFile);
if (c < 0)
RaiseError("unexpected EOF");
return c;
}
std::uint32_t ReadInt16()
{
std::uint32_t val = 0;
val |= ReadInt8() << 8;
val |= ReadInt8();
return val;
}
std::uint32_t ReadInt24()
{
std::uint32_t val = 0;
val |= ReadInt8() << 16;
val |= ReadInt8() << 8;
val |= ReadInt8();
return val;
}
std::uint32_t ReadInt32()
{
std::uint32_t val = 0;
val |= ReadInt8() << 24;
val |= ReadInt8() << 16;
val |= ReadInt8() << 8;
val |= ReadInt8();
return val;
}
std::uint32_t ReadVLQ()
{
std::uint32_t val = 0;
std::uint32_t c;
do
{
c = ReadInt8();
val <<= 7;
val |= (c & 0x7F);
} while (c & 0x80);
return val;
}
void ReadMidiFileHeader()
{
Seek(0);
if (ReadSignature() != "MThd")
RaiseError("MIDI file header signature didn't match \"MThd\"");
std::uint32_t headerLength = ReadInt32();
if (headerLength != 6)
RaiseError("MIDI file header length isn't 6");
std::uint16_t midiFormat = ReadInt16();
if (midiFormat >= 2)
RaiseError("unsupported MIDI format (%u)", midiFormat);
g_midiFormat = (MidiFormat)midiFormat;
g_midiTrackCount = ReadInt16();
g_midiTimeDiv = ReadInt16();
if (g_midiTimeDiv < 0)
RaiseError("unsupported MIDI time division (%d)", g_midiTimeDiv);
}
long ReadMidiTrackHeader(long offset)
{
Seek(offset);
if (ReadSignature() != "MTrk")
RaiseError("MIDI track header signature didn't match \"MTrk\"");
long size = ReadInt32();
s_trackDataStart = std::ftell(g_inputFile);
return size + 8;
}
void StartTrack()
{
Seek(s_trackDataStart);
s_absoluteTime = 0;
s_runningStatus = 0;
}
void SkipEventData()
{
Skip(ReadVLQ());
}
void DetermineEventCategory(MidiEventCategory& category, int& typeChan, int& size)
{
typeChan = ReadInt8();
if (typeChan < 0x80)
{
// If data byte was found, use the running status.
ungetc(typeChan, g_inputFile);
typeChan = s_runningStatus;
}
if (typeChan == 0xFF)
{
category = MidiEventCategory::Meta;
size = 0;
s_runningStatus = 0;
}
else if (typeChan >= 0xF0)
{
category = MidiEventCategory::SysEx;
size = 0;
s_runningStatus = 0;
}
else if (typeChan >= 0x80)
{
category = MidiEventCategory::Control;
switch (typeChan >> 4)
{
case 0xC:
case 0xD:
size = 1;
break;
default:
size = 2;
break;
}
s_runningStatus = typeChan;
}
else
{
category = MidiEventCategory::Invalid;
}
}
void MakeBlockEvent(Event& event, EventType type)
{
event.type = type;
event.param1 = s_blockCount++;
event.param2 = 0;
}
std::string ReadEventText()
{
char buffer[2];
std::uint32_t length = ReadVLQ();
if (length <= 2)
{
if (fread(buffer, length, 1, g_inputFile) != 1)
RaiseError("failed to read event text");
}
else
{
Skip(length);
length = 0;
}
return std::string(buffer, length);
}
bool ReadSeqEvent(Event& event)
{
s_absoluteTime += ReadVLQ();
event.time = s_absoluteTime;
MidiEventCategory category;
int typeChan;
int size;
DetermineEventCategory(category, typeChan, size);
if (category == MidiEventCategory::Control)
{
Skip(size);
return false;
}
if (category == MidiEventCategory::SysEx)
{
SkipEventData();
return false;
}
if (category == MidiEventCategory::Invalid)
RaiseError("invalid event");
// meta event
int metaEventType = ReadInt8();
if (metaEventType >= 1 && metaEventType <= 7)
{
// text event
std::string text = ReadEventText();
if (text == "[")
MakeBlockEvent(event, EventType::LoopBegin);
else if (text == "][")
MakeBlockEvent(event, EventType::LoopEndBegin);
else if (text == "]")
MakeBlockEvent(event, EventType::LoopEnd);
else if (text == ":")
MakeBlockEvent(event, EventType::Label);
else
return false;
}
else
{
switch (metaEventType)
{
case 0x2F: // end of track
SkipEventData();
event.type = EventType::EndOfTrack;
event.param1 = 0;
event.param2 = 0;
break;
case 0x51: // tempo
if (ReadVLQ() != 3)
RaiseError("invalid tempo size");
event.type = EventType::Tempo;
event.param1 = 0;
event.param2 = ReadInt24();
break;
case 0x58: // time signature
{
if (ReadVLQ() != 4)
RaiseError("invalid time signature size");
int numerator = ReadInt8();
int denominatorExponent = ReadInt8();
if (denominatorExponent >= 16)
RaiseError("invalid time signature denominator");
Skip(2); // ignore other values
int clockTicks = 96 * numerator * g_clocksPerBeat;
int denominator = 1 << denominatorExponent;
int timeSig = clockTicks / denominator;
if (timeSig <= 0 || timeSig >= 0x10000)
RaiseError("invalid time signature");
event.type = EventType::TimeSignature;
event.param1 = 0;
event.param2 = timeSig;
break;
}
default:
SkipEventData();
return false;
}
}
return true;
}
void ReadSeqEvents()
{
StartTrack();
for (;;)
{
Event event = {};
if (ReadSeqEvent(event))
{
s_seqEvents.push_back(event);
if (event.type == EventType::EndOfTrack)
return;
}
}
}
bool CheckNoteEnd(Event& event)
{
event.param2 += ReadVLQ();
MidiEventCategory category;
int typeChan;
int size;
DetermineEventCategory(category, typeChan, size);
if (category == MidiEventCategory::Control)
{
int chan = typeChan & 0xF;
if (chan != g_midiChan)
{
Skip(size);
return false;
}
switch (typeChan & 0xF0)
{
case 0x80: // note off
{
int note = ReadInt8();
ReadInt8(); // ignore velocity
if (note == event.note)
return true;
break;
}
case 0x90: // note on
{
int note = ReadInt8();
int velocity = ReadInt8();
if (velocity == 0 && note == event.note)
return true;
break;
}
default:
Skip(size);
break;
}
return false;
}
if (category == MidiEventCategory::SysEx)
{
SkipEventData();
return false;
}
if (category == MidiEventCategory::Meta)
{
int metaEventType = ReadInt8();
SkipEventData();
if (metaEventType == 0x2F)
RaiseError("note doesn't end");
return false;
}
RaiseError("invalid event");
}
void FindNoteEnd(Event& event)
{
// Save the current file position and running status
// which get modified by CheckNoteEnd.
long startPos = ftell(g_inputFile);
int savedRunningStatus = s_runningStatus;
event.param2 = 0;
while (!CheckNoteEnd(event))
;
Seek(startPos);
s_runningStatus = savedRunningStatus;
}
bool ReadTrackEvent(Event& event)
{
s_absoluteTime += ReadVLQ();
event.time = s_absoluteTime;
MidiEventCategory category;
int typeChan;
int size;
DetermineEventCategory(category, typeChan, size);
if (category == MidiEventCategory::Control)
{
int chan = typeChan & 0xF;
if (chan != g_midiChan)
{
Skip(size);
return false;
}
switch (typeChan & 0xF0)
{
case 0x90: // note on
{
int note = ReadInt8();
int velocity = ReadInt8();
if (velocity != 0)
{
event.type = EventType::Note;
event.note = note;
event.param1 = velocity;
FindNoteEnd(event);
if (event.param2 > 0)
{
if (note < s_minNote)
s_minNote = note;
if (note > s_maxNote)
s_maxNote = note;
}
}
break;
}
case 0xB0: // controller event
event.type = EventType::Controller;
event.param1 = ReadInt8(); // controller index
event.param2 = ReadInt8(); // value
break;
case 0xC0: // instrument change
event.type = EventType::InstrumentChange;
event.param1 = ReadInt8(); // instrument
event.param2 = 0;
break;
case 0xE0: // pitch bend
event.type = EventType::PitchBend;
event.param1 = ReadInt8();
event.param2 = ReadInt8();
break;
default:
Skip(size);
return false;
}
return true;
}
if (category == MidiEventCategory::SysEx)
{
SkipEventData();
return false;
}
if (category == MidiEventCategory::Meta)
{
int metaEventType = ReadInt8();
SkipEventData();
if (metaEventType == 0x2F)
{
event.type = EventType::EndOfTrack;
event.param1 = 0;
event.param2 = 0;
return true;
}
return false;
}
RaiseError("invalid event");
}
void ReadTrackEvents()
{
StartTrack();
s_trackEvents.clear();
s_minNote = 0xFF;
s_maxNote = 0;
for (;;)
{
Event event = {};
if (ReadTrackEvent(event))
{
s_trackEvents.push_back(event);
if (event.type == EventType::EndOfTrack)
return;
}
}
}
bool EventCompare(const Event& event1, const Event& event2)
{
if (event1.time < event2.time)
return true;
if (event1.time > event2.time)
return false;
unsigned event1Type = (unsigned)event1.type;
unsigned event2Type = (unsigned)event2.type;
if (event1.type == EventType::Note)
event1Type += event1.note;
if (event2.type == EventType::Note)
event2Type += event2.note;
if (event1Type < event2Type)
return true;
if (event1Type > event2Type)
return false;
if (event1.type == EventType::EndOfTie)
{
if (event1.note < event2.note)
return true;
if (event1.note > event2.note)
return false;
}
return false;
}
std::unique_ptr<std::vector<Event>> MergeEvents()
{
std::unique_ptr<std::vector<Event>> events(new std::vector<Event>());
unsigned trackEventPos = 0;
unsigned seqEventPos = 0;
while (s_trackEvents[trackEventPos].type != EventType::EndOfTrack
&& s_seqEvents[seqEventPos].type != EventType::EndOfTrack)
{
if (EventCompare(s_trackEvents[trackEventPos], s_seqEvents[seqEventPos]))
events->push_back(s_trackEvents[trackEventPos++]);
else
events->push_back(s_seqEvents[seqEventPos++]);
}
while (s_trackEvents[trackEventPos].type != EventType::EndOfTrack)
events->push_back(s_trackEvents[trackEventPos++]);
while (s_seqEvents[seqEventPos].type != EventType::EndOfTrack)
events->push_back(s_seqEvents[seqEventPos++]);
// Push the EndOfTrack event with the larger time.
if (EventCompare(s_trackEvents[trackEventPos], s_seqEvents[seqEventPos]))
events->push_back(s_seqEvents[seqEventPos]);
else
events->push_back(s_trackEvents[trackEventPos]);
return events;
}
void ConvertTimes(std::vector<Event>& events)
{
for (Event& event : events)
{
event.time = (24 * g_clocksPerBeat * event.time) / g_midiTimeDiv;
if (event.type == EventType::Note)
{
event.param1 = g_noteVelocityLUT[event.param1];
std::uint32_t duration = (24 * g_clocksPerBeat * event.param2) / g_midiTimeDiv;
if (duration == 0)
duration = 1;
if (!g_exactGateTime && duration < 96)
duration = g_noteDurationLUT[duration];
event.param2 = duration;
}
}
}
std::unique_ptr<std::vector<Event>> InsertTimingEvents(std::vector<Event>& inEvents)
{
std::unique_ptr<std::vector<Event>> outEvents(new std::vector<Event>());
Event timingEvent = {};
timingEvent.time = 0;
timingEvent.type = EventType::TimeSignature;
timingEvent.param2 = 96 * g_clocksPerBeat;
for (const Event& event : inEvents)
{
while (EventCompare(timingEvent, event))
{
outEvents->push_back(timingEvent);
timingEvent.time += timingEvent.param2;
}
if (event.type == EventType::TimeSignature)
{
if (g_agbTrack == 1 && event.param2 != timingEvent.param2)
{
Event originalTimingEvent = event;
originalTimingEvent.type = EventType::OriginalTimeSignature;
outEvents->push_back(originalTimingEvent);
}
timingEvent.param2 = event.param2;
timingEvent.time = event.time + timingEvent.param2;
}
outEvents->push_back(event);
}
return outEvents;
}
std::unique_ptr<std::vector<Event>> SplitTime(std::vector<Event>& inEvents)
{
std::unique_ptr<std::vector<Event>> outEvents(new std::vector<Event>());
std::int32_t time = 0;
for (const Event& event : inEvents)
{
std::int32_t diff = event.time - time;
if (diff > 96)
{
int wholeNoteCount = (diff - 1) / 96;
diff -= 96 * wholeNoteCount;
for (int i = 0; i < wholeNoteCount; i++)
{
time += 96;
Event timeSplitEvent = {};
timeSplitEvent.time = time;
timeSplitEvent.type = EventType::TimeSplit;
outEvents->push_back(timeSplitEvent);
}
}
std::int32_t lutValue = g_noteDurationLUT[diff];
if (lutValue != diff)
{
Event timeSplitEvent = {};
timeSplitEvent.time = time + lutValue;
timeSplitEvent.type = EventType::TimeSplit;
outEvents->push_back(timeSplitEvent);
}
time = event.time;
outEvents->push_back(event);
}
return outEvents;
}
std::unique_ptr<std::vector<Event>> CreateTies(std::vector<Event>& inEvents)
{
std::unique_ptr<std::vector<Event>> outEvents(new std::vector<Event>());
for (const Event& event : inEvents)
{
if (event.type == EventType::Note && event.param2 > 96)
{
Event tieEvent = event;
tieEvent.param2 = -1;
outEvents->push_back(tieEvent);
Event eotEvent = {};
eotEvent.time = event.time + event.param2;
eotEvent.type = EventType::EndOfTie;
eotEvent.note = event.note;
outEvents->push_back(eotEvent);
}
else
{
outEvents->push_back(event);
}
}
return outEvents;
}
void CalculateWaits(std::vector<Event>& events)
{
g_initialWait = events[0].time;
int wholeNoteCount = 0;
for (unsigned i = 0; i < events.size() && events[i].type != EventType::EndOfTrack; i++)
{
events[i].time = events[i + 1].time - events[i].time;
if (events[i].type == EventType::TimeSignature)
{
events[i].type = EventType::WholeNoteMark;
events[i].param2 = wholeNoteCount++;
}
}
}
int CalculateCompressionScore(std::vector<Event>& events, int index)
{
int score = 0;
std::uint8_t lastParam1 = events[index].param1;
std::uint8_t lastVelocity = 0x80u;
EventType lastType = events[index].type;
std::int32_t lastDuration = 0x80000000;
std::uint8_t lastNote = 0x40u;
if (events[index].time > 0)
score++;
for (int i = index + 1; !IsPatternBoundary(events[i].type); i++)
{
if (events[i].type == EventType::Note)
{
int val = 0;
if (events[i].note != lastNote)
{
val++;
lastNote = events[i].note;
}
if (events[i].param1 != lastVelocity)
{
val++;
lastVelocity = events[i].param1;
}
std::int32_t duration = events[i].param2;
if (g_noteDurationLUT[duration] != lastDuration)
{
val++;
lastDuration = g_noteDurationLUT[duration];
}
if (duration != lastDuration)
val++;
if (val == 0)
val = 1;
score += val;
}
else
{
lastDuration = 0x80000000;
if (events[i].type == lastType)
{
if ((lastType != EventType::Controller && (int)lastType != 0x25 && lastType != EventType::EndOfTie) || events[i].param1 == lastParam1)
{
score++;
}
else
{
score += 2;
}
}
else
{
score += 2;
}
}
lastParam1 = events[i].param1;
lastType = events[i].type;
if (events[i].time)
score++;
}
return score;
}
bool IsCompressionMatch(std::vector<Event>& events, int index1, int index2)
{
if (events[index1].type != events[index2].type ||
events[index1].note != events[index2].note ||
events[index1].param1 != events[index2].param1 ||
events[index1].time != events[index2].time)
return false;
index1++;
index2++;
do
{
if (events[index1] != events[index2])
return false;
index1++;
index2++;
} while (!IsPatternBoundary(events[index1].type));
return IsPatternBoundary(events[index2].type);
}
void CompressWholeNote(std::vector<Event>& events, int index)
{
for (int j = index + 1; events[j].type != EventType::EndOfTrack; j++)
{
while (events[j].type != EventType::WholeNoteMark)
{
j++;
if (events[j].type == EventType::EndOfTrack)
return;
}
if (IsCompressionMatch(events, index, j))
{
events[j].type = EventType::Pattern;
events[j].param2 = events[index].param2 & 0x7FFFFFFF;
events[index].param2 |= 0x80000000;
}
}
}
void Compress(std::vector<Event>& events)
{
for (int i = 0; events[i].type != EventType::EndOfTrack; i++)
{
while (events[i].type != EventType::WholeNoteMark)
{
i++;
if (events[i].type == EventType::EndOfTrack)
return;
}
if (CalculateCompressionScore(events, i) >= 6)
{
CompressWholeNote(events, i);
}
}
}
void ReadMidiTracks()
{
long trackHeaderStart = 14;
ReadMidiTrackHeader(trackHeaderStart);
ReadSeqEvents();
g_agbTrack = 1;
for (int midiTrack = 0; midiTrack < g_midiTrackCount; midiTrack++)
{
trackHeaderStart += ReadMidiTrackHeader(trackHeaderStart);
for (g_midiChan = 0; g_midiChan < 16; g_midiChan++)
{
ReadTrackEvents();
if (s_minNote != 0xFF)
{
#ifdef DEBUG
printf("Track%d = Midi-Ch.%d\n", g_agbTrack, g_midiChan + 1);
#endif
std::unique_ptr<std::vector<Event>> events(MergeEvents());
// We don't need TEMPO in anything but track 1.
if (g_agbTrack == 1)
{
auto it = std::remove_if(s_seqEvents.begin(), s_seqEvents.end(), [](const Event& event) { return event.type == EventType::Tempo; });
s_seqEvents.erase(it, s_seqEvents.end());
}
ConvertTimes(*events);
events = InsertTimingEvents(*events);
events = CreateTies(*events);
std::stable_sort(events->begin(), events->end(), EventCompare);
events = SplitTime(*events);
CalculateWaits(*events);
if (g_compressionEnabled)
Compress(*events);
PrintAgbTrack(*events);
g_agbTrack++;
}
}
}
}

87
tools/mid2agb/midi.h Normal file
View File

@ -0,0 +1,87 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef MIDI_H
#define MIDI_H
#include <cstdint>
enum class MidiFormat
{
SingleTrack,
MultiTrack
};
enum class EventType
{
EndOfTie = 0x01,
Label = 0x11,
LoopEnd = 0x12,
LoopEndBegin = 0x13,
LoopBegin = 0x14,
OriginalTimeSignature = 0x15,
WholeNoteMark = 0x16,
Pattern = 0x17,
TimeSignature = 0x18,
Tempo = 0x19,
InstrumentChange = 0x21,
Controller = 0x22,
PitchBend = 0x23,
KeyShift = 0x31,
Note = 0x40,
TimeSplit = 0xFE,
EndOfTrack = 0xFF,
};
struct Event
{
std::int32_t time;
EventType type;
std::uint8_t note;
std::uint8_t param1;
std::int32_t param2;
bool operator==(const Event& other)
{
return (time == other.time
&& type == other.type
&& note == other.note
&& param1 == other.param1
&& param2 == other.param2);
}
bool operator!=(const Event& other)
{
return !(*this == other);
}
};
void ReadMidiFileHeader();
void ReadMidiTracks();
extern int g_midiChan;
extern std::int32_t g_initialWait;
inline bool IsPatternBoundary(EventType type)
{
return type == EventType::EndOfTrack || (int)type <= 0x17;
}
#endif // MIDI_H

286
tools/mid2agb/tables.cpp Normal file
View File

@ -0,0 +1,286 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "tables.h"
const int g_noteDurationLUT[] =
{
0, // 0
1, // 1
2, // 2
3, // 3
4, // 4
5, // 5
6, // 6
7, // 7
8, // 8
9, // 9
10, // 10
11, // 11
12, // 12
13, // 13
14, // 14
15, // 15
16, // 16
17, // 17
18, // 18
19, // 19
20, // 20
21, // 21
22, // 22
23, // 23
24, // 24
24, // 25
24, // 26
24, // 27
28, // 28
28, // 29
30, // 30
30, // 31
32, // 32
32, // 33
32, // 34
32, // 35
36, // 36
36, // 37
36, // 38
36, // 39
40, // 40
40, // 41
42, // 42
42, // 43
44, // 44
44, // 45
44, // 46
44, // 47
48, // 48
48, // 49
48, // 50
48, // 51
52, // 52
52, // 53
54, // 54
54, // 55
56, // 56
56, // 57
56, // 58
56, // 59
60, // 60
60, // 61
60, // 62
60, // 63
64, // 64
64, // 65
66, // 66
66, // 67
68, // 68
68, // 69
68, // 70
68, // 71
72, // 72
72, // 73
72, // 74
72, // 75
76, // 76
76, // 77
78, // 78
78, // 79
80, // 80
80, // 81
80, // 82
80, // 83
84, // 84
84, // 85
84, // 86
84, // 87
88, // 88
88, // 89
90, // 90
90, // 91
92, // 92
92, // 93
92, // 94
92, // 95
96, // 96
};
const int g_noteVelocityLUT[] =
{
0, // 0
4, // 1
4, // 2
4, // 3
4, // 4
8, // 5
8, // 6
8, // 7
8, // 8
12, // 9
12, // 10
12, // 11
12, // 12
16, // 13
16, // 14
16, // 15
16, // 16
20, // 17
20, // 18
20, // 19
20, // 20
24, // 21
24, // 22
24, // 23
24, // 24
28, // 25
28, // 26
28, // 27
28, // 28
32, // 29
32, // 30
32, // 31
32, // 32
36, // 33
36, // 34
36, // 35
36, // 36
40, // 37
40, // 38
40, // 39
40, // 40
44, // 41
44, // 42
44, // 43
44, // 44
48, // 45
48, // 46
48, // 47
48, // 48
52, // 49
52, // 50
52, // 51
52, // 52
56, // 53
56, // 54
56, // 55
56, // 56
60, // 57
60, // 58
60, // 59
60, // 60
64, // 61
64, // 62
64, // 63
64, // 64
68, // 65
68, // 66
68, // 67
68, // 68
72, // 69
72, // 70
72, // 71
72, // 72
76, // 73
76, // 74
76, // 75
76, // 76
80, // 77
80, // 78
80, // 79
80, // 80
84, // 81
84, // 82
84, // 83
84, // 84
88, // 85
88, // 86
88, // 87
88, // 88
92, // 89
92, // 90
92, // 91
92, // 92
96, // 93
96, // 94
96, // 95
96, // 96
100, // 97
100, // 98
100, // 99
100, // 100
104, // 101
104, // 102
104, // 103
104, // 104
108, // 105
108, // 106
108, // 107
108, // 108
112, // 109
112, // 110
112, // 111
112, // 112
116, // 113
116, // 114
116, // 115
116, // 116
120, // 117
120, // 118
120, // 119
120, // 120
124, // 121
124, // 122
124, // 123
124, // 124
127, // 125
127, // 126
127, // 127
};
const char* g_noteTable[] =
{
"Cn%01u ",
"Cs%01u ",
"Dn%01u ",
"Ds%01u ",
"En%01u ",
"Fn%01u ",
"Fs%01u ",
"Gn%01u ",
"Gs%01u ",
"An%01u ",
"As%01u ",
"Bn%01u ",
};
const char* g_minusNoteTable[] =
{
"CnM%01u",
"CsM%01u",
"DnM%01u",
"DsM%01u",
"EnM%01u",
"FnM%01u",
"FsM%01u",
"GnM%01u",
"GsM%01u",
"AnM%01u",
"AsM%01u",
"BnM%01u",
};

29
tools/mid2agb/tables.h Normal file
View File

@ -0,0 +1,29 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef TABLES_H
#define TABLES_H
extern const int g_noteDurationLUT[];
extern const int g_noteVelocityLUT[];
extern const char* g_noteTable[];
extern const char* g_minusNoteTable[];
#endif // TABLES_H

1
tools/preproc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
preproc

19
tools/preproc/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

26
tools/preproc/Makefile Normal file
View File

@ -0,0 +1,26 @@
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
HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
utf8.h
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
.PHONY: all clean
all: preproc$(EXE)
@:
preproc$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) preproc preproc.exe

599
tools/preproc/asm_file.cpp Normal file
View File

@ -0,0 +1,599 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <stdexcept>
#include "preproc.h"
#include "asm_file.h"
#include "char_util.h"
#include "utf8.h"
#include "string_parser.h"
#include "../../gflib/characters.h"
AsmFile::AsmFile(std::string filename) : 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());
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_pos = 0;
m_lineNum = 1;
m_lineStart = 0;
RemoveComments();
}
AsmFile::AsmFile(AsmFile&& other) : m_filename(std::move(other.m_filename))
{
m_buffer = other.m_buffer;
m_pos = other.m_pos;
m_size = other.m_size;
m_lineNum = other.m_lineNum;
m_lineStart = other.m_lineStart;
other.m_buffer = nullptr;
}
AsmFile::~AsmFile()
{
delete[] m_buffer;
}
// Removes comments to simplify further processing.
// It stops upon encountering a null character,
// which may or may not be the end of file marker.
// If it's not, the error will be caught later.
void AsmFile::RemoveComments()
{
long pos = 0;
char stringChar = 0;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (stringChar != 0)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == stringChar)
{
pos += 2;
}
else
{
if (m_buffer[pos] == stringChar)
stringChar = 0;
pos++;
}
}
else if (m_buffer[pos] == '@' && (pos == 0 || m_buffer[pos - 1] != '\\'))
{
while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
m_buffer[pos++] = ' ';
}
else if (m_buffer[pos] == '/' && m_buffer[pos + 1] == '*')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (m_buffer[pos] == '*' && m_buffer[pos + 1] == '/')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
break;
}
else
{
if (m_buffer[pos] != '\n')
m_buffer[pos] = ' ';
pos++;
}
}
}
else
{
if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
stringChar = m_buffer[pos];
pos++;
}
}
}
// Checks if we're at a particular directive and if so, consumes it.
// Returns whether the directive was found.
bool AsmFile::CheckForDirective(std::string name)
{
long i;
long length = static_cast<long>(name.length());
for (i = 0; i < length && m_pos + i < m_size; i++)
if (name[i] != m_buffer[m_pos + i])
return false;
if (i < length)
return false;
m_pos += length;
return true;
}
// Checks if we're at a known directive and if so, consumes it.
// Returns which directive was found.
Directive AsmFile::GetDirective()
{
SkipWhitespace();
if (CheckForDirective(".include"))
return Directive::Include;
else if (CheckForDirective(".string"))
return Directive::String;
else if (CheckForDirective(".braille"))
return Directive::Braille;
else
return Directive::Unknown;
}
// Checks if we're at label that ends with '::'.
// Returns the name if so and an empty string if not.
std::string AsmFile::GetGlobalLabel()
{
long start = m_pos;
long pos = m_pos;
if (IsIdentifierStartingChar(m_buffer[pos]))
{
pos++;
while (IsIdentifierChar(m_buffer[pos]))
pos++;
}
if (m_buffer[pos] == ':' && m_buffer[pos + 1] == ':')
{
m_pos = pos + 2;
ExpectEmptyRestOfLine();
return std::string(&m_buffer[start], pos - start);
}
return std::string();
}
// Skips tabs and spaces.
void AsmFile::SkipWhitespace()
{
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
m_pos++;
}
// Reads include path.
std::string AsmFile::ReadPath()
{
SkipWhitespace();
if (m_buffer[m_pos] != '"')
RaiseError("expected file path");
m_pos++;
int length = 0;
long startPos = m_pos;
while (m_buffer[m_pos] != '"')
{
unsigned char c = m_buffer[m_pos++];
if (c == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in include string");
else
RaiseError("unexpected null character in include string");
}
if (!IsAsciiPrintable(c))
RaiseError("unexpected character '\\x%02X' in include string", c);
// Don't bother allowing any escape sequences.
if (c == '\\')
{
c = m_buffer[m_pos];
RaiseError("unexpected escape '\\%c' in include string", c);
}
length++;
if (length > kMaxPath)
RaiseError("path is too long");
}
m_pos++; // Go past the right quote.
ExpectEmptyRestOfLine();
return std::string(&m_buffer[startPos], length);
}
// Reads a charmap string.
int AsmFile::ReadString(unsigned char* s)
{
SkipWhitespace();
int length;
StringParser stringParser(m_buffer, m_size);
try
{
m_pos += stringParser.ParseString(m_pos, s, length);
}
catch (std::runtime_error& e)
{
RaiseError(e.what());
}
SkipWhitespace();
if (ConsumeComma())
{
SkipWhitespace();
int padLength = ReadPadLength();
while (length < padLength)
{
s[length++] = CHAR_SPACE;
}
}
ExpectEmptyRestOfLine();
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', 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();
int length = 0;
if (m_buffer[m_pos] != '"')
RaiseError("expected braille string literal");
m_pos++;
bool inNumber = false;
while (m_buffer[m_pos] != '"')
{
if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == 'n')
{
VerifyStringLength(length);
s[length++] = CHAR_NEWLINE;
m_pos += 2;
}
else
{
char c = m_buffer[m_pos];
if (encoding.count(c) == 0)
{
if (IsAsciiPrintable(c))
RaiseError("character '%c' not valid in braille string", m_buffer[m_pos]);
else
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++;
}
}
m_pos++; // Go past the right quote.
ExpectEmptyRestOfLine();
return length;
}
// If we're at a comma, consumes it.
// Returns whether a comma was found.
bool AsmFile::ConsumeComma()
{
if (m_buffer[m_pos] == ',')
{
m_pos++;
return true;
}
return false;
}
// Converts digit character to numerical value.
static int ConvertDigit(char c, int radix)
{
int digit;
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
digit = 10 + c - 'a';
else
return -1;
return (digit < radix) ? digit : -1;
}
// Reads an integer. If the integer is greater than maxValue, it returns -1.
int AsmFile::ReadPadLength()
{
if (!IsAsciiDigit(m_buffer[m_pos]))
RaiseError("expected integer");
int radix = 10;
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
{
radix = 16;
m_pos += 2;
}
unsigned n = 0;
int digit;
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
{
n = n * radix + digit;
if (n > kMaxStringLength)
RaiseError("pad length greater than maximum length (%d)", kMaxStringLength);
m_pos++;
}
return n;
}
// Outputs the current line and moves to the next one.
void AsmFile::OutputLine()
{
while (m_buffer[m_pos] != '\n' && m_buffer[m_pos] != 0)
m_pos++;
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
{
RaiseWarning("file doesn't end with newline");
puts(&m_buffer[m_lineStart]);
}
else
{
RaiseError("unexpected null character");
}
}
else
{
m_buffer[m_pos] = 0;
puts(&m_buffer[m_lineStart]);
m_buffer[m_pos] = '\n';
m_pos++;
m_lineStart = m_pos;
m_lineNum++;
}
}
// Asserts that the rest of the line is empty and moves to the next one.
void AsmFile::ExpectEmptyRestOfLine()
{
SkipWhitespace();
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
RaiseWarning("file doesn't end with newline");
else
RaiseError("unexpected null character");
}
else if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineStart = m_pos;
m_lineNum++;
}
else if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
{
m_pos += 2;
m_lineStart = m_pos;
m_lineNum++;
}
else
{
RaiseError("junk at end of line");
}
}
// Checks if we're at the end of the file.
bool AsmFile::IsAtEnd()
{
return (m_pos >= m_size);
}
// Output the current location to set gas's logical file and line numbers.
void AsmFile::OutputLocation()
{
std::printf("# %ld \"%s\"\n", m_lineNum, m_filename.c_str());
}
// Reports a diagnostic message.
void AsmFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
}
#define DO_REPORT(type) \
do \
{ \
std::va_list args; \
va_start(args, format); \
ReportDiagnostic(type, format, args); \
va_end(args); \
} while (0)
// Reports an error diagnostic and terminates the program.
void AsmFile::RaiseError(const char* format, ...)
{
DO_REPORT("error");
std::exit(1);
}
// Reports a warning diagnostic.
void AsmFile::RaiseWarning(const char* format, ...)
{
DO_REPORT("warning");
}

73
tools/preproc/asm_file.h Normal file
View File

@ -0,0 +1,73 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ASM_FILE_H
#define ASM_FILE_H
#include <cstdarg>
#include <cstdint>
#include <string>
#include "preproc.h"
enum class Directive
{
Include,
String,
Braille,
Unknown
};
class AsmFile
{
public:
AsmFile(std::string filename);
AsmFile(AsmFile&& other);
AsmFile(const AsmFile&) = delete;
~AsmFile();
Directive GetDirective();
std::string GetGlobalLabel();
std::string ReadPath();
int ReadString(unsigned char* s);
int ReadBraille(unsigned char* s);
bool IsAtEnd();
void OutputLine();
void OutputLocation();
private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
long m_lineStart;
std::string m_filename;
bool ConsumeComma();
int ReadPadLength();
void RemoveComments();
bool CheckForDirective(std::string name);
void SkipWhitespace();
void ExpectEmptyRestOfLine();
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);
};
#endif // ASM_FILE_H

459
tools/preproc/c_file.cpp Normal file
View File

@ -0,0 +1,459 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#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"
CFile::CFile(const char * filenameCStr, bool isStdin)
{
FILE *fp;
if (isStdin) {
fp = stdin;
m_filename = std::string{"<stdin>/"}.append(filenameCStr);
} else {
fp = std::fopen(filenameCStr, "rb");
m_filename = std::string(filenameCStr);
}
std::string& filename = m_filename;
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
m_size = 0;
m_buffer = (char *)malloc(CHUNK_SIZE + 1);
if (m_buffer == NULL) {
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str());
}
std::size_t numAllocatedBytes = CHUNK_SIZE + 1;
std::size_t bufferOffset = 0;
std::size_t count;
while ((count = std::fread(m_buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) {
if (!std::ferror(fp)) {
m_size += count;
if (std::feof(fp)) {
break;
}
numAllocatedBytes += CHUNK_SIZE;
bufferOffset += CHUNK_SIZE;
m_buffer = (char *)realloc(m_buffer, numAllocatedBytes);
if (m_buffer == NULL) {
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str());
}
} else {
FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename.c_str(), std::strerror(errno));
}
}
m_buffer[m_size] = 0;
std::fclose(fp);
m_pos = 0;
m_lineNum = 1;
m_isStdin = isStdin;
}
CFile::CFile(CFile&& other) : m_filename(std::move(other.m_filename))
{
m_buffer = other.m_buffer;
m_pos = other.m_pos;
m_size = other.m_size;
m_lineNum = other.m_lineNum;
m_isStdin = other.m_isStdin;
other.m_buffer = NULL;
}
CFile::~CFile()
{
free(m_buffer);
}
void CFile::Preproc()
{
char stringChar = 0;
while (m_pos < m_size)
{
if (stringChar)
{
if (m_buffer[m_pos] == stringChar)
{
std::putchar(stringChar);
m_pos++;
stringChar = 0;
}
else if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == stringChar)
{
std::putchar('\\');
std::putchar(stringChar);
m_pos += 2;
}
else
{
if (m_buffer[m_pos] == '\n')
m_lineNum++;
std::putchar(m_buffer[m_pos]);
m_pos++;
}
}
else
{
TryConvertString();
TryConvertIncbin();
if (m_pos >= m_size)
break;
char c = m_buffer[m_pos++];
std::putchar(c);
if (c == '\n')
m_lineNum++;
else if (c == '"')
stringChar = '"';
else if (c == '\'')
stringChar = '\'';
}
}
}
bool CFile::ConsumeHorizontalWhitespace()
{
if (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
{
m_pos++;
return true;
}
return false;
}
bool CFile::ConsumeNewline()
{
if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
{
m_pos += 2;
m_lineNum++;
std::putchar('\n');
return true;
}
if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineNum++;
std::putchar('\n');
return true;
}
return false;
}
void CFile::SkipWhitespace()
{
while (ConsumeHorizontalWhitespace() || ConsumeNewline())
;
}
void CFile::TryConvertString()
{
long oldPos = m_pos;
long oldLineNum = m_lineNum;
bool noTerminator = false;
if (m_buffer[m_pos] != '_' || (m_pos > 0 && IsIdentifierChar(m_buffer[m_pos - 1])))
return;
m_pos++;
if (m_buffer[m_pos] == '_')
{
noTerminator = true;
m_pos++;
}
SkipWhitespace();
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
return;
}
m_pos++;
SkipWhitespace();
std::printf("{ ");
while (1)
{
SkipWhitespace();
if (m_buffer[m_pos] == '"')
{
unsigned char s[kMaxStringLength];
int length;
StringParser stringParser(m_buffer, m_size);
try
{
m_pos += stringParser.ParseString(m_pos, s, length);
}
catch (std::runtime_error& e)
{
RaiseError(e.what());
}
for (int i = 0; i < length; i++)
printf("0x%02X, ", s[i]);
}
else if (m_buffer[m_pos] == ')')
{
m_pos++;
break;
}
else
{
if (m_pos >= m_size)
RaiseError("unexpected EOF");
if (IsAsciiPrintable(m_buffer[m_pos]))
RaiseError("unexpected character '%c'", m_buffer[m_pos]);
else
RaiseError("unexpected character '\\x%02X'", m_buffer[m_pos]);
}
}
if (noTerminator)
std::printf(" }");
else
std::printf("0xFF }");
}
bool CFile::CheckIdentifier(const std::string& ident)
{
unsigned int i;
for (i = 0; i < ident.length() && m_pos + i < (unsigned)m_size; i++)
if (ident[i] != m_buffer[m_pos + i])
return false;
return (i == ident.length());
}
std::unique_ptr<unsigned char[]> CFile::ReadWholeFile(const std::string& path, int& size)
{
FILE* fp = std::fopen(path.c_str(), "rb");
if (fp == nullptr)
RaiseError("Failed to open \"%s\" for reading.\n", path.c_str());
std::fseek(fp, 0, SEEK_END);
size = std::ftell(fp);
std::unique_ptr<unsigned char[]> buffer = std::unique_ptr<unsigned char[]>(new unsigned char[size]);
std::rewind(fp);
if (std::fread(buffer.get(), size, 1, fp) != 1)
RaiseError("Failed to read \"%s\".\n", path.c_str());
std::fclose(fp);
return buffer;
}
int ExtractData(const std::unique_ptr<unsigned char[]>& buffer, int offset, int size)
{
switch (size)
{
case 1:
return buffer[offset];
case 2:
return (buffer[offset + 1] << 8)
| buffer[offset];
case 4:
return (buffer[offset + 3] << 24)
| (buffer[offset + 2] << 16)
| (buffer[offset + 1] << 8)
| buffer[offset];
default:
FATAL_ERROR("Invalid size passed to ExtractData.\n");
}
}
void CFile::TryConvertIncbin()
{
std::string idents[6] = { "INCBIN_S8", "INCBIN_U8", "INCBIN_S16", "INCBIN_U16", "INCBIN_S32", "INCBIN_U32" };
int incbinType = -1;
for (int i = 0; i < 6; i++)
{
if (CheckIdentifier(idents[i]))
{
incbinType = i;
break;
}
}
if (incbinType == -1)
return;
int size = 1 << (incbinType / 2);
bool isSigned = ((incbinType % 2) == 0);
long oldPos = m_pos;
long oldLineNum = m_lineNum;
m_pos += idents[incbinType].length();
SkipWhitespace();
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
return;
}
m_pos++;
std::printf("{");
while (true)
{
SkipWhitespace();
if (m_buffer[m_pos] != '"')
RaiseError("expected double quote");
m_pos++;
int startPos = m_pos;
while (m_buffer[m_pos] != '"')
{
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in path string");
else
RaiseError("unexpected null character in path string");
}
if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')
RaiseError("unexpected end of line character in path string");
if (m_buffer[m_pos] == '\\')
RaiseError("unexpected escape in path string");
m_pos++;
}
std::string path(&m_buffer[startPos], m_pos - startPos);
m_pos++;
int fileSize;
std::unique_ptr<unsigned char[]> buffer = ReadWholeFile(path, fileSize);
if ((fileSize % size) != 0)
RaiseError("Size %d doesn't evenly divide file size %d.\n", size, fileSize);
int count = fileSize / size;
int offset = 0;
for (int i = 0; i < count; i++)
{
int data = ExtractData(buffer, offset, size);
offset += size;
if (isSigned)
std::printf("%d,", data);
else
std::printf("%uu,", data);
}
SkipWhitespace();
if (m_buffer[m_pos] != ',')
break;
m_pos++;
}
if (m_buffer[m_pos] != ')')
RaiseError("expected ')'");
m_pos++;
std::printf("}");
}
// Reports a diagnostic message.
void CFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
}
#define DO_REPORT(type) \
do \
{ \
std::va_list args; \
va_start(args, format); \
ReportDiagnostic(type, format, args); \
va_end(args); \
} while (0)
// Reports an error diagnostic and terminates the program.
void CFile::RaiseError(const char* format, ...)
{
DO_REPORT("error");
std::exit(1);
}
// Reports a warning diagnostic.
void CFile::RaiseWarning(const char* format, ...)
{
DO_REPORT("warning");
}

61
tools/preproc/c_file.h Normal file
View File

@ -0,0 +1,61 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef C_FILE_H
#define C_FILE_H
#include <cstdarg>
#include <cstdint>
#include <string>
#include <memory>
#include "preproc.h"
class CFile
{
public:
CFile(const char * filenameCStr, bool isStdin);
CFile(CFile&& other);
CFile(const CFile&) = delete;
~CFile();
void Preproc();
private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
std::string m_filename;
bool m_isStdin;
bool ConsumeHorizontalWhitespace();
bool ConsumeNewline();
void SkipWhitespace();
void TryConvertString();
std::unique_ptr<unsigned char[]> ReadWholeFile(const std::string& path, int& size);
bool CheckIdentifier(const std::string& ident);
void TryConvertIncbin();
void ReportDiagnostic(const char* type, const char* format, std::va_list args);
void RaiseError(const char* format, ...);
void RaiseWarning(const char* format, ...);
};
#define CHUNK_SIZE 4096
#endif // C_FILE_H

71
tools/preproc/char_util.h Normal file
View File

@ -0,0 +1,71 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CHAR_UTIL_H
#define CHAR_UTIL_H
#include <cstdint>
#include <cassert>
inline bool IsAscii(unsigned char c)
{
return (c < 128);
}
inline bool IsAsciiAlpha(unsigned char c)
{
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
inline bool IsAsciiDigit(unsigned char c)
{
return (c >= '0' && c <= '9');
}
inline bool IsAsciiHexDigit(unsigned char c)
{
return ((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F'));
}
inline bool IsAsciiAlphanum(unsigned char c)
{
return (IsAsciiAlpha(c) || IsAsciiDigit(c));
}
inline bool IsAsciiPrintable(unsigned char c)
{
return (c >= ' ' && c <= '~');
}
// Returns whether the character can start a C identifier or the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierStartingChar(unsigned char c)
{
return IsAsciiAlpha(c) || c == '_';
}
// Returns whether the character can be used in a C identifier or the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierChar(unsigned char c)
{
return IsAsciiAlphanum(c) || c == '_';
}
#endif // CHAR_UTIL_H

408
tools/preproc/charmap.cpp Normal file
View File

@ -0,0 +1,408 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdint>
#include <cstdarg>
#include "preproc.h"
#include "charmap.h"
#include "char_util.h"
#include "utf8.h"
enum LhsType
{
Char,
Escape,
Constant,
None
};
struct Lhs
{
LhsType type;
std::string name;
std::int32_t code;
};
class CharmapReader
{
public:
CharmapReader(std::string filename);
CharmapReader(const CharmapReader&) = delete;
~CharmapReader();
Lhs ReadLhs();
void ExpectEqualsSign();
std::string ReadSequence();
void ExpectEmptyRestOfLine();
void RaiseError(const char* format, ...);
private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
std::string m_filename;
void RemoveComments();
std::string ReadConstant();
void SkipWhitespace();
};
CharmapReader::CharmapReader(std::string filename) : 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());
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_pos = 0;
m_lineNum = 1;
RemoveComments();
}
CharmapReader::~CharmapReader()
{
delete[] m_buffer;
}
Lhs CharmapReader::ReadLhs()
{
Lhs lhs;
for (;;)
{
SkipWhitespace();
if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineNum++;
}
else
{
break;
}
}
if (m_buffer[m_pos] == '\'')
{
m_pos++;
bool isEscape = (m_buffer[m_pos] == '\\');
if (isEscape)
{
m_pos++;
}
unsigned char c = m_buffer[m_pos];
if (c == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in UTF-8 character literal");
else
RaiseError("unexpected null character in UTF-8 character literal");
}
if (IsAscii(c) && !IsAsciiPrintable(c))
RaiseError("unexpected character U+%X in UTF-8 character literal", c);
UnicodeChar unicodeChar = DecodeUtf8(&m_buffer[m_pos]);
std::int32_t code = unicodeChar.code;
if (code == -1)
RaiseError("invalid encoding in UTF-8 character literal");
m_pos += unicodeChar.encodingLength;
if (m_buffer[m_pos] != '\'')
RaiseError("unterminated character literal");
m_pos++;
lhs.code = code;
if (isEscape)
{
if (code >= 128)
RaiseError("escapes using non-ASCII characters are invalid");
switch (code)
{
case '\'':
lhs.type = LhsType::Char;
break;
case '\\':
lhs.type = LhsType::Char;
case '"':
RaiseError("cannot escape double quote");
break;
default:
lhs.type = LhsType::Escape;
}
}
else
{
if (code == '\'')
RaiseError("empty character literal");
lhs.type = LhsType::Char;
}
}
else if (IsIdentifierStartingChar(m_buffer[m_pos]))
{
lhs.type = LhsType::Constant;
lhs.name = ReadConstant();
}
else if (m_buffer[m_pos] == '\r')
{
RaiseError("only Unix-style LF newlines are supported");
}
else if (m_buffer[m_pos] == 0)
{
if (m_pos < m_size)
RaiseError("unexpected null character");
lhs.type = LhsType::None;
}
else
{
RaiseError("junk at start of line");
}
return lhs;
}
void CharmapReader::ExpectEqualsSign()
{
SkipWhitespace();
if (m_buffer[m_pos] != '=')
RaiseError("expected equals sign");
m_pos++;
}
static unsigned int ConvertHexDigit(char c)
{
unsigned int digit = 0;
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
digit = 10 + c - 'a';
return digit;
}
std::string CharmapReader::ReadSequence()
{
SkipWhitespace();
long startPos = m_pos;
unsigned int length = 0;
while (IsAsciiHexDigit(m_buffer[m_pos]) && IsAsciiHexDigit(m_buffer[m_pos + 1]))
{
m_pos += 2;
length++;
if (length > kMaxCharmapSequenceLength)
RaiseError("byte sequence too long (max is %lu bytes)", kMaxCharmapSequenceLength);
SkipWhitespace();
}
if (IsAsciiHexDigit(m_buffer[m_pos]))
RaiseError("each byte must have 2 hex digits");
if (length == 0)
RaiseError("expected byte sequence");
std::string sequence;
sequence.reserve(length);
m_pos = startPos;
for (unsigned int i = 0; i < length; i++)
{
unsigned int digit1 = ConvertHexDigit(m_buffer[m_pos]);
unsigned int digit2 = ConvertHexDigit(m_buffer[m_pos + 1]);
unsigned char byte = digit1 * 16 + digit2;
sequence += byte;
m_pos += 2;
SkipWhitespace();
}
return sequence;
}
void CharmapReader::ExpectEmptyRestOfLine()
{
SkipWhitespace();
if (m_buffer[m_pos] == 0)
{
if (m_pos < m_size)
RaiseError("unexpected null character");
}
else if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineNum++;
}
else if (m_buffer[m_pos] == '\r')
{
RaiseError("only Unix-style LF newlines are supported");
}
else
{
RaiseError("junk at end of line");
}
}
void CharmapReader::RaiseError(const char* format, ...)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::va_list args;
va_start(args, format);
std::vsnprintf(buffer, bufferSize, format, args);
va_end(args);
std::fprintf(stderr, "%s:%ld: error: %s\n", m_filename.c_str(), m_lineNum, buffer);
std::exit(1);
}
void CharmapReader::RemoveComments()
{
long pos = 0;
bool inString = false;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (inString)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == '\'')
{
pos += 2;
}
else
{
if (m_buffer[pos] == '\'')
inString = false;
pos++;
}
}
else if (m_buffer[pos] == '@')
{
while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
m_buffer[pos++] = ' ';
}
else
{
if (m_buffer[pos] == '\'')
inString = true;
pos++;
}
}
}
std::string CharmapReader::ReadConstant()
{
long startPos = m_pos;
while (IsIdentifierChar(m_buffer[m_pos]))
m_pos++;
return std::string(&m_buffer[startPos], m_pos - startPos);
}
void CharmapReader::SkipWhitespace()
{
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
m_pos++;
}
Charmap::Charmap(std::string filename)
{
CharmapReader reader(filename);
for (;;)
{
Lhs lhs = reader.ReadLhs();
if (lhs.type == LhsType::None)
return;
reader.ExpectEqualsSign();
std::string sequence = reader.ReadSequence();
switch (lhs.type)
{
case LhsType::Char:
if (m_chars.find(lhs.code) != m_chars.end())
reader.RaiseError("redefining char");
m_chars[lhs.code] = sequence;
break;
case LhsType::Escape:
if (m_escapes[lhs.code].length() != 0)
reader.RaiseError("redefining escape");
m_escapes[lhs.code] = sequence;
break;
case LhsType::Constant:
if (m_constants.find(lhs.name) != m_constants.end())
reader.RaiseError("redefining constant");
m_constants[lhs.name] = sequence;
break;
}
reader.ExpectEmptyRestOfLine();
}
}

64
tools/preproc/charmap.h Normal file
View File

@ -0,0 +1,64 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CHARMAP_H
#define CHARMAP_H
#include <cstdint>
#include <string>
#include <map>
#include <vector>
class Charmap
{
public:
Charmap(std::string filename);
std::string Char(std::int32_t code)
{
auto it = m_chars.find(code);
if (it == m_chars.end())
return std::string();
return it->second;
}
std::string Escape(unsigned char code)
{
return m_escapes[code];
}
std::string Constant(std::string identifier)
{
auto it = m_constants.find(identifier);
if (it == m_constants.end())
return std::string();
return it->second;
}
private:
std::map<std::int32_t, std::string> m_chars;
std::string m_escapes[128];
std::map<std::string, std::string> m_constants;
};
#endif // CHARMAP_H

164
tools/preproc/preproc.cpp Normal file
View File

@ -0,0 +1,164 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <string>
#include <stack>
#include "preproc.h"
#include "asm_file.h"
#include "c_file.h"
#include "charmap.h"
Charmap* g_charmap;
void PrintAsmBytes(unsigned char *s, int length)
{
if (length > 0)
{
std::printf("\t.byte ");
for (int i = 0; i < length; i++)
{
std::printf("0x%02X", s[i]);
if (i < length - 1)
std::printf(", ");
}
std::putchar('\n');
}
}
void PreprocAsmFile(std::string filename)
{
std::stack<AsmFile> stack;
stack.push(AsmFile(filename));
for (;;)
{
while (stack.top().IsAtEnd())
{
stack.pop();
if (stack.empty())
return;
else
stack.top().OutputLocation();
}
Directive directive = stack.top().GetDirective();
switch (directive)
{
case Directive::Include:
stack.push(AsmFile(stack.top().ReadPath()));
stack.top().OutputLocation();
break;
case Directive::String:
{
unsigned char s[kMaxStringLength];
int length = stack.top().ReadString(s);
PrintAsmBytes(s, length);
break;
}
case Directive::Braille:
{
unsigned char s[kMaxStringLength];
int length = stack.top().ReadBraille(s);
PrintAsmBytes(s, length);
break;
}
case Directive::Unknown:
{
std::string globalLabel = stack.top().GetGlobalLabel();
if (globalLabel.length() != 0)
{
const char *s = globalLabel.c_str();
std::printf("%s: ; .global %s\n", s, s);
}
else
{
stack.top().OutputLine();
}
break;
}
}
}
}
void PreprocCFile(const char * filename, bool isStdin)
{
CFile cFile(filename, isStdin);
cFile.Preproc();
}
char* GetFileExtension(char* filename)
{
char* extension = filename;
while (*extension != 0)
extension++;
while (extension > filename && *extension != '.')
extension--;
if (extension == filename)
return nullptr;
extension++;
if (*extension == 0)
return nullptr;
return extension;
}
int main(int argc, char **argv)
{
if (argc < 3 || argc > 4)
{
std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE [-i]\nwhere -i denotes if input is from stdin\n", argv[0]);
return 1;
}
g_charmap = new Charmap(argv[2]);
char* extension = GetFileExtension(argv[1]);
if (!extension)
FATAL_ERROR("\"%s\" has no file extension.\n", argv[1]);
if ((extension[0] == 's') && extension[1] == 0)
PreprocAsmFile(argv[1]);
else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0) {
if (argc == 4) {
if (argv[3][0] == '-' && argv[3][1] == 'i' && argv[3][2] == '\0') {
PreprocCFile(argv[1], true);
} else {
FATAL_ERROR("unknown argument flag \"%s\".\n", argv[3]);
}
} else {
PreprocCFile(argv[1], false);
}
} else
FATAL_ERROR("\"%s\" has an unknown file extension of \"%s\".\n", argv[1], extension);
return 0;
}

54
tools/preproc/preproc.h Normal file
View File

@ -0,0 +1,54 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef PREPROC_H
#define PREPROC_H
#include <cstdio>
#include <cstdlib>
#include "charmap.h"
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, __VA_ARGS__); \
std::exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, ##__VA_ARGS__); \
std::exit(1); \
} while (0)
#endif // _MSC_VER
const int kMaxPath = 256;
const int kMaxStringLength = 1024;
const unsigned long kMaxCharmapSequenceLength = 16;
extern Charmap* g_charmap;
#endif // PREPROC_H

View File

@ -0,0 +1,355 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <stdexcept>
#include "preproc.h"
#include "string_parser.h"
#include "char_util.h"
#include "utf8.h"
// Reads a charmap char or escape sequence.
std::string StringParser::ReadCharOrEscape()
{
std::string sequence;
bool isEscape = (m_buffer[m_pos] == '\\');
if (isEscape)
{
m_pos++;
if (m_buffer[m_pos] == '"')
{
sequence = g_charmap->Char('"');
if (sequence.length() == 0)
RaiseError("no mapping exists for double quote");
return sequence;
}
else if (m_buffer[m_pos] == '\\')
{
sequence = g_charmap->Char('\\');
if (sequence.length() == 0)
RaiseError("no mapping exists for backslash");
return sequence;
}
}
unsigned char c = m_buffer[m_pos];
if (c == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in UTF-8 string");
else
RaiseError("unexpected null character in UTF-8 string");
}
if (IsAscii(c) && !IsAsciiPrintable(c))
RaiseError("unexpected character U+%X in UTF-8 string", c);
UnicodeChar unicodeChar = DecodeUtf8(&m_buffer[m_pos]);
m_pos += unicodeChar.encodingLength;
std::int32_t code = unicodeChar.code;
if (code == -1)
RaiseError("invalid encoding in UTF-8 string");
if (isEscape && code >= 128)
RaiseError("escapes using non-ASCII characters are invalid");
sequence = isEscape ? g_charmap->Escape(code) : g_charmap->Char(code);
if (sequence.length() == 0)
{
if (isEscape)
RaiseError("unknown escape '\\%c'", code);
else
RaiseError("unknown character U+%X", code);
}
return sequence;
}
// Reads a charmap constant, i.e. "{FOO}".
std::string StringParser::ReadBracketedConstants()
{
std::string totalSequence;
m_pos++; // Assume we're on the left curly bracket.
while (m_buffer[m_pos] != '}')
{
SkipWhitespace();
if (IsIdentifierStartingChar(m_buffer[m_pos]))
{
long startPos = m_pos;
m_pos++;
while (IsIdentifierChar(m_buffer[m_pos]))
m_pos++;
std::string sequence = g_charmap->Constant(std::string(&m_buffer[startPos], m_pos - startPos));
if (sequence.length() == 0)
{
m_buffer[m_pos] = 0;
RaiseError("unknown constant '%s'", &m_buffer[startPos]);
}
totalSequence += sequence;
}
else if (IsAsciiDigit(m_buffer[m_pos]))
{
Integer integer = ReadInteger();
switch (integer.size)
{
case 1:
totalSequence += (unsigned char)integer.value;
break;
case 2:
totalSequence += (unsigned char)integer.value;
totalSequence += (unsigned char)(integer.value >> 8);
break;
case 4:
totalSequence += (unsigned char)integer.value;
totalSequence += (unsigned char)(integer.value >> 8);
totalSequence += (unsigned char)(integer.value >> 16);
totalSequence += (unsigned char)(integer.value >> 24);
break;
}
}
else if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF after left curly bracket");
else
RaiseError("unexpected null character within curly brackets");
}
else
{
if (IsAsciiPrintable(m_buffer[m_pos]))
RaiseError("unexpected character '%c' within curly brackets", m_buffer[m_pos]);
else
RaiseError("unexpected character '\\x%02X' within curly brackets", m_buffer[m_pos]);
}
}
m_pos++; // Go past the right curly bracket.
return totalSequence;
}
// Reads a charmap string.
int StringParser::ParseString(long srcPos, unsigned char* dest, int& destLength)
{
m_pos = srcPos;
if (m_buffer[m_pos] != '"')
RaiseError("expected UTF-8 string literal");
long start = m_pos;
m_pos++;
destLength = 0;
while (m_buffer[m_pos] != '"')
{
std::string sequence = (m_buffer[m_pos] == '{') ? ReadBracketedConstants() : ReadCharOrEscape();
for (const char& c : sequence)
{
if (destLength == kMaxStringLength)
RaiseError("mapped string longer than %d bytes", kMaxStringLength);
dest[destLength++] = c;
}
}
m_pos++; // Go past the right quote.
return m_pos - start;
}
void StringParser::RaiseError(const char* format, ...)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::va_list args;
va_start(args, format);
std::vsnprintf(buffer, bufferSize, format, args);
va_end(args);
throw std::runtime_error(buffer);
}
// Converts digit character to numerical value.
static int ConvertDigit(char c, int radix)
{
int digit;
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
digit = 10 + c - 'a';
else
return -1;
return (digit < radix) ? digit : -1;
}
void StringParser::SkipRestOfInteger(int radix)
{
while (ConvertDigit(m_buffer[m_pos], radix) != -1)
m_pos++;
}
StringParser::Integer StringParser::ReadDecimal()
{
const int radix = 10;
std::uint64_t n = 0;
int digit;
std::uint64_t max = UINT32_MAX;
long startPos = m_pos;
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
{
n = n * radix + digit;
if (n >= max)
{
SkipRestOfInteger(radix);
std::string intLiteral(m_buffer + startPos, m_pos - startPos);
RaiseError("integer literal \"%s\" is too large", intLiteral.c_str());
}
m_pos++;
}
int size;
if (m_buffer[m_pos] == 'H')
{
if (n >= 0x10000)
{
RaiseError("%lu is too large to be a halfword", (unsigned long)n);
}
size = 2;
m_pos++;
}
else if (m_buffer[m_pos] == 'W')
{
size = 4;
m_pos++;
}
else
{
if (n >= 0x10000)
size = 4;
else if (n >= 0x100)
size = 2;
else
size = 1;
}
return{ static_cast<std::uint32_t>(n), size };
}
StringParser::Integer StringParser::ReadHex()
{
const int radix = 16;
std::uint64_t n = 0;
int digit;
std::uint64_t max = UINT32_MAX;
long startPos = m_pos;
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
{
n = n * radix + digit;
if (n >= max)
{
SkipRestOfInteger(radix);
std::string intLiteral(m_buffer + startPos, m_pos - startPos);
RaiseError("integer literal \"%s\" is too large", intLiteral.c_str());
}
m_pos++;
}
int length = m_pos - startPos;
int size = 0;
switch (length)
{
case 2:
size = 1;
break;
case 4:
size = 2;
break;
case 8:
size = 4;
break;
default:
{
std::string intLiteral(m_buffer + startPos, m_pos - startPos);
RaiseError("hex integer literal \"0x%s\" doesn't have length of 2, 4, or 8 digits", intLiteral.c_str());
}
}
return{ static_cast<std::uint32_t>(n), size };
}
StringParser::Integer StringParser::ReadInteger()
{
if (!IsAsciiDigit(m_buffer[m_pos]))
RaiseError("expected integer");
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
{
m_pos += 2;
return ReadHex();
}
return ReadDecimal();
}
// Skips tabs and spaces.
void StringParser::SkipWhitespace()
{
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
m_pos++;
}

View File

@ -0,0 +1,55 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef STRING_PARSER_H
#define STRING_PARSER_H
#include <cstdint>
#include <string>
#include "preproc.h"
class StringParser
{
public:
StringParser(char* buffer, long size) : m_buffer(buffer), m_size(size), m_pos(0) {}
int ParseString(long srcPos, unsigned char* dest, int &destLength);
private:
struct Integer
{
std::uint32_t value;
int size;
};
char* m_buffer;
long m_size;
long m_pos;
Integer ReadInteger();
Integer ReadDecimal();
Integer ReadHex();
std::string ReadCharOrEscape();
std::string ReadBracketedConstants();
void SkipWhitespace();
void SkipRestOfInteger(int radix);
void RaiseError(const char* format, ...);
};
#endif // STRING_PARSER_H

92
tools/preproc/utf8.cpp Normal file
View File

@ -0,0 +1,92 @@
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
//
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdint>
#include "utf8.h"
static const unsigned char s_byteTypeTable[] =
{
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
};
const unsigned char s0 = 0 * 12;
const unsigned char s1 = 1 * 12;
const unsigned char s2 = 2 * 12;
const unsigned char s3 = 3 * 12;
const unsigned char s4 = 4 * 12;
const unsigned char s5 = 5 * 12;
const unsigned char s6 = 6 * 12;
const unsigned char s7 = 7 * 12;
const unsigned char s8 = 8 * 12;
static const unsigned char s_transitionTable[] =
{
s0,s1,s2,s3,s5,s8,s7,s1,s1,s1,s4,s6, // s0
s1,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1, // s1
s1,s0,s1,s1,s1,s1,s1,s0,s1,s0,s1,s1, // s2
s1,s2,s1,s1,s1,s1,s1,s2,s1,s2,s1,s1, // s3
s1,s1,s1,s1,s1,s1,s1,s2,s1,s1,s1,s1, // s4
s1,s2,s1,s1,s1,s1,s1,s1,s1,s2,s1,s1, // s5
s1,s1,s1,s1,s1,s1,s1,s3,s1,s3,s1,s1, // s6
s1,s3,s1,s1,s1,s1,s1,s3,s1,s3,s1,s1, // s7
s1,s3,s1,s1,s1,s1,s1,s1,s1,s1,s1,s1, // s8
};
// Decodes UTF-8 encoded Unicode code point at "s".
UnicodeChar DecodeUtf8(const char* s)
{
UnicodeChar unicodeChar;
int state = s0;
auto start = s;
do
{
unsigned char byte = *s++;
int type = s_byteTypeTable[byte];
if (state == s0)
unicodeChar.code = (0xFF >> type) & byte;
else
unicodeChar.code = (unicodeChar.code << 6) | (byte & 0x3F);
state = s_transitionTable[state + type];
if (state == s1)
{
unicodeChar.code = -1;
return unicodeChar;
}
} while (state != s0);
unicodeChar.encodingLength = s - start;
return unicodeChar;
}

34
tools/preproc/utf8.h Normal file
View File

@ -0,0 +1,34 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef UTF8_H
#define UTF8_H
#include <cstdint>
struct UnicodeChar
{
std::int32_t code;
int encodingLength;
};
UnicodeChar DecodeUtf8(const char* s);
#endif // UTF8_H

1
tools/ramscrgen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
ramscrgen

19
tools/ramscrgen/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
tools/ramscrgen/Makefile Normal file
View File

@ -0,0 +1,24 @@
CXX ?= g++
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
SRCS := main.cpp sym_file.cpp elf.cpp
HEADERS := ramscrgen.h sym_file.h elf.h char_util.h
.PHONY: all clean
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: ramscrgen$(EXE)
@:
ramscrgen$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) ramscrgen ramscrgen.exe

View File

@ -0,0 +1,71 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef CHAR_UTIL_H
#define CHAR_UTIL_H
#include <cstdint>
#include <cassert>
inline bool IsAscii(unsigned char c)
{
return (c < 128);
}
inline bool IsAsciiAlpha(unsigned char c)
{
return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
}
inline bool IsAsciiDigit(unsigned char c)
{
return (c >= '0' && c <= '9');
}
inline bool IsAsciiHexDigit(unsigned char c)
{
return ((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F'));
}
inline bool IsAsciiAlphanum(unsigned char c)
{
return (IsAsciiAlpha(c) || IsAsciiDigit(c));
}
inline bool IsAsciiPrintable(unsigned char c)
{
return (c >= ' ' && c <= '~');
}
// Returns whether the character can start the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierStartingChar(unsigned char c)
{
return IsAsciiAlpha(c) || c == '_';
}
// Returns whether the character can be used in the identifier of a "{FOO}" constant in strings.
inline bool IsIdentifierChar(unsigned char c)
{
return IsAsciiAlphanum(c) || c == '_';
}
#endif // CHAR_UTIL_H

272
tools/ramscrgen/elf.cpp Normal file
View File

@ -0,0 +1,272 @@
#include <cstdio>
#include <cstring>
#include <cstdint>
#include <map>
#include <vector>
#include <string>
#include "ramscrgen.h"
#include "elf.h"
#define SHN_COMMON 0xFFF2
static std::string s_elfPath;
static std::string s_archiveFilePath;
static std::string s_archiveObjectPath;
static FILE *s_file;
static std::uint32_t s_sectionHeaderOffset;
static int s_sectionHeaderEntrySize;
static int s_sectionCount;
static int s_shstrtabIndex;
static std::uint32_t s_symtabOffset;
static std::uint32_t s_strtabOffset;
static std::uint32_t s_symbolCount;
static std::uint32_t s_elfFileOffset;
struct Symbol
{
std::uint32_t nameOffset;
std::uint32_t size;
};
static void Seek(long offset)
{
if (std::fseek(s_file, s_elfFileOffset + offset, SEEK_SET) != 0)
FATAL_ERROR("error: failed to seek to %ld in \"%s\"", offset, s_elfPath.c_str());
}
static void Skip(long offset)
{
if (std::fseek(s_file, offset, SEEK_CUR) != 0)
FATAL_ERROR("error: failed to skip %ld bytes in \"%s\"", offset, s_elfPath.c_str());
}
static std::uint32_t ReadInt8()
{
int c = std::fgetc(s_file);
if (c < 0)
FATAL_ERROR("error: unexpected EOF when reading ELF file \"%s\"\n", s_elfPath.c_str());
return c;
}
static std::uint32_t ReadInt16()
{
std::uint32_t val = 0;
val |= ReadInt8();
val |= ReadInt8() << 8;
return val;
}
static std::uint32_t ReadInt32()
{
std::uint32_t val = 0;
val |= ReadInt8();
val |= ReadInt8() << 8;
val |= ReadInt8() << 16;
val |= ReadInt8() << 24;
return val;
}
static std::string ReadString()
{
std::string s;
char c;
while ((c = ReadInt8()) != 0)
s += c;
return s;
}
static void VerifyElfIdent()
{
char expectedMagic[4] = { 0x7F, 'E', 'L', 'F' };
char magic[4];
if (std::fread(magic, 4, 1, s_file) != 1)
FATAL_ERROR("error: failed to read ELF magic from \"%s\"\n", s_elfPath.c_str());
if (std::memcmp(magic, expectedMagic, 4) != 0)
FATAL_ERROR("error: ELF magic did not match in \"%s\"\n", s_elfPath.c_str());
if (std::fgetc(s_file) != 1)
FATAL_ERROR("error: \"%s\" not 32-bit ELF\n", s_elfPath.c_str());
if (std::fgetc(s_file) != 1)
FATAL_ERROR("error: \"%s\" not little-endian ELF\n", s_elfPath.c_str());
}
static void VerifyAr()
{
char expectedMagic[8] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
char magic[8];
if (std::fread(magic, 8, 1, s_file) != 1)
FATAL_ERROR("error: failed to read AR magic from \"%s\"\n", s_archiveFilePath.c_str());
if (std::memcmp(magic, expectedMagic, 8) != 0)
FATAL_ERROR("error: AR magic did not match in \"%s\"\n", s_archiveFilePath.c_str());
}
static void ReadElfHeader()
{
Seek(0x20);
s_sectionHeaderOffset = ReadInt32();
Seek(0x2E);
s_sectionHeaderEntrySize = ReadInt16();
s_sectionCount = ReadInt16();
s_shstrtabIndex = ReadInt16();
}
static void FindArObj()
{
char file_ident[17] = {0};
char filesize_s[11] = {0};
char expectedEndMagic[2] = { 0x60, 0x0a };
char end_magic[2];
std::size_t filesize;
Seek(8);
while (!std::feof(s_file)) {
if (std::fread(file_ident, 16, 1, s_file) != 1)
FATAL_ERROR("error: failed to read file ident in \"%s\"\n", s_archiveFilePath.c_str());
Skip(32);
if (std::fread(filesize_s, 10, 1, s_file) != 1)
FATAL_ERROR("error: failed to read filesize in \"%s\"\n", s_archiveFilePath.c_str());
if (std::fread(end_magic, 2, 1, s_file) != 1)
FATAL_ERROR("error: failed to read end sentinel in \"%s\"\n", s_archiveFilePath.c_str());
if (std::memcmp(end_magic, expectedEndMagic, 2) != 0)
FATAL_ERROR("error: corrupted archive header in \"%s\" at \"%s\"\n", s_archiveFilePath.c_str(), file_ident);
char * ptr = std::strchr(file_ident, '/');
if (ptr != nullptr)
*ptr = 0;
filesize = std::strtoul(filesize_s, nullptr, 10);
if (std::strncmp(s_archiveObjectPath.c_str(), file_ident, 16) == 0) {
s_elfFileOffset = std::ftell(s_file);
return;
}
Skip(filesize);
}
FATAL_ERROR("error: could not find object \"%s\" in archive \"%s\"\n", s_archiveObjectPath.c_str(), s_archiveFilePath.c_str());
}
static std::string GetSectionName(std::uint32_t shstrtabOffset, int index)
{
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * index);
std::uint32_t nameOffset = ReadInt32();
Seek(shstrtabOffset + nameOffset);
return ReadString();
}
static void FindTableOffsets()
{
s_symtabOffset = 0;
s_strtabOffset = 0;
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * s_shstrtabIndex + 0x10);
std::uint32_t shstrtabOffset = ReadInt32();
for (int i = 0; i < s_sectionCount; i++)
{
std::string name = GetSectionName(shstrtabOffset, i);
if (name == ".symtab")
{
if (s_symtabOffset)
FATAL_ERROR("error: mutiple .symtab sections found in \"%s\"\n", s_elfPath.c_str());
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * i + 0x10);
s_symtabOffset = ReadInt32();
std::uint32_t size = ReadInt32();
s_symbolCount = size / 16;
}
else if (name == ".strtab")
{
if (s_strtabOffset)
FATAL_ERROR("error: mutiple .strtab sections found in \"%s\"\n", s_elfPath.c_str());
Seek(s_sectionHeaderOffset + s_sectionHeaderEntrySize * i + 0x10);
s_strtabOffset = ReadInt32();
}
}
if (!s_symtabOffset)
FATAL_ERROR("error: couldn't find .symtab section in \"%s\"\n", s_elfPath.c_str());
if (!s_strtabOffset)
FATAL_ERROR("error: couldn't find .strtab section in \"%s\"\n", s_elfPath.c_str());
}
static std::map<std::string, std::uint32_t> GetCommonSymbols_Shared()
{
VerifyElfIdent();
ReadElfHeader();
FindTableOffsets();
std::map<std::string, std::uint32_t> commonSymbols;
std::vector<Symbol> commonSymbolVec;
Seek(s_symtabOffset);
for (std::uint32_t i = 0; i < s_symbolCount; i++)
{
Symbol sym;
sym.nameOffset = ReadInt32();
Skip(4);
sym.size = ReadInt32();
Skip(2);
std::uint16_t sectionIndex = ReadInt16();
if (sectionIndex == SHN_COMMON)
commonSymbolVec.push_back(sym);
}
for (const Symbol& sym : commonSymbolVec)
{
Seek(s_strtabOffset + sym.nameOffset);
std::string name = ReadString();
commonSymbols[name] = sym.size;
}
return commonSymbols;
}
std::map<std::string, std::uint32_t> GetCommonSymbolsFromLib(std::string sourcePath, std::string libpath)
{
std::size_t colonPos = libpath.find(':');
if (colonPos == std::string::npos)
FATAL_ERROR("error: missing colon separator in libfile \"%s\"\n", s_elfPath.c_str());
s_archiveObjectPath = libpath.substr(colonPos + 1);
s_archiveFilePath = sourcePath + "/" + libpath.substr(1, colonPos - 1);
s_elfPath = sourcePath + "/" + libpath.substr(1);
s_file = std::fopen(s_archiveFilePath.c_str(), "rb");
if (s_file == NULL)
FATAL_ERROR("error: failed to open \"%s\" for reading\n", s_archiveFilePath.c_str());
VerifyAr();
FindArObj();
return GetCommonSymbols_Shared();
}
std::map<std::string, std::uint32_t> GetCommonSymbols(std::string sourcePath, std::string path)
{
s_elfFileOffset = 0;
if (path[0] == '*')
return GetCommonSymbolsFromLib(sourcePath, path);
s_elfPath = sourcePath + "/" + path;
s_file = std::fopen(s_elfPath.c_str(), "rb");
if (s_file == NULL)
FATAL_ERROR("error: failed to open \"%s\" for reading\n", path.c_str());
return GetCommonSymbols_Shared();
}

30
tools/ramscrgen/elf.h Normal file
View File

@ -0,0 +1,30 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ELF_H
#define ELF_H
#include <cstdint>
#include <map>
#include <string>
std::map<std::string, std::uint32_t> GetCommonSymbols(std::string sourcePath, std::string path);
#endif // ELF_H

187
tools/ramscrgen/main.cpp Normal file
View File

@ -0,0 +1,187 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstring>
#include <string>
#include "ramscrgen.h"
#include "sym_file.h"
#include "elf.h"
void HandleCommonInclude(std::string filename, std::string sourcePath, std::string symOrderPath, std::string lang)
{
auto commonSymbols = GetCommonSymbols(sourcePath, filename);
std::size_t dotIndex;
if (filename[0] == '*') {
dotIndex = filename.find_last_of(':');
filename = filename.substr(dotIndex + 1);
}
dotIndex = filename.find_last_of('.');
if (dotIndex == std::string::npos)
FATAL_ERROR("error: \"%s\" doesn't have a file extension\n", filename.c_str());
std::string symOrderFilename = filename.substr(0, dotIndex + 1) + "txt";
SymFile symFile(symOrderPath + "/" + symOrderFilename);
while (!symFile.IsAtEnd())
{
symFile.HandleLangConditional(lang);
std::string label = symFile.GetLabel(false);
if (label.length() == 0)
{
unsigned long length;
if (symFile.ReadInteger(length))
{
if (length & 3)
symFile.RaiseWarning("gap length %d is not multiple of 4", length);
printf(". += 0x%lX;\n", length);
}
}
else
{
if (commonSymbols.count(label) == 0)
symFile.RaiseError("no common symbol named \"%s\"", label.c_str());
unsigned long size = commonSymbols[label];
int alignment = 4;
if (size > 4)
alignment = 8;
if (size > 8)
alignment = 16;
printf(". = ALIGN(%d);\n", alignment);
printf("%s = .;\n", label.c_str());
printf(". += 0x%lX;\n", size);
}
symFile.ExpectEmptyRestOfLine();
}
}
void ConvertSymFile(std::string filename, std::string sectionName, std::string lang, bool common, std::string sourcePath, std::string commonSymPath, std::string libSourcePath)
{
SymFile symFile(filename);
while (!symFile.IsAtEnd())
{
symFile.HandleLangConditional(lang);
Directive directive = symFile.GetDirective();
switch (directive)
{
case Directive::Include:
{
std::string incFilename = symFile.ReadPath();
symFile.ExpectEmptyRestOfLine();
printf(". = ALIGN(4);\n");
if (common)
HandleCommonInclude(incFilename, incFilename[0] == '*' ? libSourcePath : sourcePath, commonSymPath, lang);
else
printf("%s(%s);\n", incFilename.c_str(), sectionName.c_str());
break;
}
case Directive::Space:
{
unsigned long length;
if (!symFile.ReadInteger(length))
symFile.RaiseError("expected integer after .space directive");
symFile.ExpectEmptyRestOfLine();
printf(". += 0x%lX;\n", length);
break;
}
case Directive::Align:
{
unsigned long amount;
if (!symFile.ReadInteger(amount))
symFile.RaiseError("expected integer after .align directive");
if (amount > 4)
symFile.RaiseError("max alignment amount is 4");
amount = 1UL << amount;
symFile.ExpectEmptyRestOfLine();
printf(". = ALIGN(%lu);\n", amount);
break;
}
case Directive::Unknown:
{
std::string label = symFile.GetLabel();
if (label.length() != 0)
{
printf("%s = .;\n", label.c_str());
}
symFile.ExpectEmptyRestOfLine();
break;
}
}
}
}
int main(int argc, char **argv)
{
if (argc < 4)
{
fprintf(stderr, "Usage: %s SECTION_NAME SYM_FILE LANG [-c SRC_PATH,COMMON_SYM_PATH]", argv[0]);
return 1;
}
bool common = false;
std::string sectionName = std::string(argv[1]);
std::string symFileName = std::string(argv[2]);
std::string lang = std::string(argv[3]);
std::string sourcePath;
std::string commonSymPath;
std::string libSourcePath;
if (argc > 4)
{
if (std::strcmp(argv[4], "-c") != 0)
FATAL_ERROR("error: unrecognized argument \"%s\"\n", argv[4]);
if (argc < 6)
FATAL_ERROR("error: missing SRC_PATH,COMMON_SYM_PATH after \"-c\"\n");
common = true;
std::string paths = std::string(argv[5]);
std::size_t commaPos = paths.find(',');
if (commaPos == std::string::npos)
FATAL_ERROR("error: missing comma in argument after \"-c\"\n");
sourcePath = paths.substr(0, commaPos);
commonSymPath = paths.substr(commaPos + 1);
commaPos = commonSymPath.find(',');
if (commaPos == std::string::npos) {
libSourcePath = "tools/agbcc/lib";
} else {
libSourcePath = commonSymPath.substr(commaPos + 1);
commonSymPath = commonSymPath.substr(0, commaPos);
}
}
ConvertSymFile(symFileName, sectionName, lang, common, sourcePath, commonSymPath, libSourcePath);
return 0;
}

View File

@ -0,0 +1,49 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAMSCRGEN_H
#define RAMSCRGEN_H
#include <cstdio>
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, __VA_ARGS__); \
std::exit(1); \
} while (0)
#else
#define FATAL_ERROR(format, ...) \
do \
{ \
std::fprintf(stderr, format, ##__VA_ARGS__); \
std::exit(1); \
} while (0)
#endif // _MSC_VER
const int kMaxPath = 256;
#endif // RAMSCRGEN_H

View File

@ -0,0 +1,492 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdarg>
#include <climits>
#include "ramscrgen.h"
#include "sym_file.h"
#include "char_util.h"
SymFile::SymFile(std::string filename) : 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());
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_pos = 0;
m_lineNum = 1;
m_lineStart = 0;
m_inLangConditional = false;
RemoveComments();
}
SymFile::SymFile(SymFile&& other) : m_filename(std::move(other.m_filename))
{
m_buffer = other.m_buffer;
m_pos = other.m_pos;
m_size = other.m_size;
m_lineNum = other.m_lineNum;
m_lineStart = other.m_lineStart;
other.m_buffer = nullptr;
}
SymFile::~SymFile()
{
delete[] m_buffer;
}
// Removes comments to simplify further processing.
// It stops upon encountering a null character,
// which may or may not be the end of file marker.
// If it's not, the error will be caught later.
void SymFile::RemoveComments()
{
long pos = 0;
char stringChar = 0;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (stringChar != 0)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == stringChar)
{
pos += 2;
}
else
{
if (m_buffer[pos] == stringChar)
stringChar = 0;
pos++;
}
}
else if (m_buffer[pos] == '@' && (pos == 0 || m_buffer[pos - 1] != '\\'))
{
while (m_buffer[pos] != '\n' && m_buffer[pos] != 0)
m_buffer[pos++] = ' ';
}
else if (m_buffer[pos] == '/' && m_buffer[pos + 1] == '*')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
char commentStringChar = 0;
for (;;)
{
if (m_buffer[pos] == 0)
return;
if (commentStringChar != 0)
{
if (m_buffer[pos] == '\\' && m_buffer[pos + 1] == commentStringChar)
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
}
else
{
if (m_buffer[pos] == commentStringChar)
commentStringChar = 0;
if (m_buffer[pos] != '\n')
m_buffer[pos] = ' ';
pos++;
}
}
else
{
if (m_buffer[pos] == '*' && m_buffer[pos + 1] == '/')
{
m_buffer[pos++] = ' ';
m_buffer[pos++] = ' ';
break;
}
else
{
if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
commentStringChar = m_buffer[pos];
if (m_buffer[pos] != '\n')
m_buffer[pos] = ' ';
pos++;
}
}
}
}
else
{
if (m_buffer[pos] == '"' || m_buffer[pos] == '\'')
stringChar = m_buffer[pos];
pos++;
}
}
}
// Checks if we're at a particular directive and if so, consumes it.
// Returns whether the directive was found.
bool SymFile::CheckForDirective(std::string name)
{
long i;
long length = static_cast<long>(name.length());
for (i = 0; i < length && m_pos + i < m_size; i++)
if (name[i] != m_buffer[m_pos + i])
return false;
if (i < length)
return false;
m_pos += length;
return true;
}
// Checks if we're at a known directive and if so, consumes it.
// Returns which directive was found.
Directive SymFile::GetDirective()
{
SkipWhitespace();
if (CheckForDirective(".include"))
return Directive::Include;
else if (CheckForDirective(".space"))
return Directive::Space;
else if (CheckForDirective(".align"))
return Directive::Align;
else
return Directive::Unknown;
}
// Checks if we're at label.
// Returns the name if so and an empty string if not.
std::string SymFile::GetLabel(bool requireColon)
{
long start = m_pos;
long pos = m_pos;
if (IsIdentifierStartingChar(m_buffer[pos]))
{
pos++;
while (IsIdentifierChar(m_buffer[pos]))
pos++;
}
if (requireColon)
{
if (m_buffer[pos] == ':')
{
if (pos != start)
m_pos = pos + 1;
}
else
{
pos = start;
}
}
else
{
m_pos = pos;
}
return std::string(&m_buffer[start], pos - start);
}
// Skips tabs and spaces.
void SymFile::SkipWhitespace()
{
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
m_pos++;
}
// Reads include path.
std::string SymFile::ReadPath()
{
SkipWhitespace();
if (m_buffer[m_pos] != '"')
RaiseError("expected file path");
m_pos++;
int length = 0;
long startPos = m_pos;
while (m_buffer[m_pos] != '"')
{
unsigned char c = m_buffer[m_pos++];
if (c == 0)
{
if (m_pos >= m_size)
RaiseError("unexpected EOF in include string");
else
RaiseError("unexpected null character in include string");
}
if (!IsAsciiPrintable(c))
RaiseError("unexpected character '\\x%02X' in include string", c);
// Don't bother allowing any escape sequences.
if (c == '\\')
{
c = m_buffer[m_pos];
RaiseError("unexpected escape '\\%c' in include string", c);
}
length++;
if (length > kMaxPath)
RaiseError("path is too long");
}
m_pos++; // Go past the right quote.
return std::string(&m_buffer[startPos], length);
}
// If we're at a comma, consumes it.
// Returns whether a comma was found.
bool SymFile::ConsumeComma()
{
if (m_buffer[m_pos] == ',')
{
m_pos++;
return true;
}
return false;
}
// Converts digit character to numerical value.
static int ConvertDigit(char c, int radix)
{
int digit;
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'A' && c <= 'F')
digit = 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
digit = 10 + c - 'a';
else
return -1;
return (digit < radix) ? digit : -1;
}
// Reads an integer.
bool SymFile::ReadInteger(unsigned long& n)
{
SkipWhitespace();
if (!IsAsciiDigit(m_buffer[m_pos]))
return false;
int startPos = m_pos;
int radix = 10;
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
{
radix = 16;
m_pos += 2;
}
unsigned long cutoff = ULONG_MAX / radix;
unsigned long cutoffRemainder = ULONG_MAX % radix;
int digit;
n = 0;
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
{
if (n < cutoff || (n == cutoff && (unsigned long)digit <= cutoffRemainder))
{
n = n * radix + digit;
}
else
{
m_pos++;
while (ConvertDigit(m_buffer[m_pos], radix) != -1)
m_pos++;
RaiseError("integer is too large (%s)", std::string(&m_buffer[startPos], m_pos - startPos).c_str());
}
m_pos++;
}
return true;
}
// Asserts that the rest of the line is empty and moves to the next one.
void SymFile::ExpectEmptyRestOfLine()
{
SkipWhitespace();
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
RaiseWarning("file doesn't end with newline");
else
RaiseError("unexpected null character");
}
else if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineStart = m_pos;
m_lineNum++;
}
else if (m_buffer[m_pos] == '\r')
{
RaiseError("only Unix-style LF newlines are supported");
}
else
{
RaiseError("junk at end of line");
}
}
void SymFile::SkipLine()
{
while (m_buffer[m_pos] != 0 && m_buffer[m_pos] != '\n')
m_pos++;
if (m_buffer[m_pos] == '\n')
m_pos++;
}
// Checks if we're at the end of the file.
bool SymFile::IsAtEnd()
{
return (m_pos >= m_size);
}
void SymFile::HandleLangConditional(std::string lang)
{
if (m_buffer[m_pos] != '#')
return;
m_pos++;
if (CheckForDirective("begin"))
{
if (m_inLangConditional)
RaiseError("already inside language conditional");
SkipWhitespace();
std::string label = GetLabel(false);
if (label.length() == 0)
RaiseError("no language name after #begin");
ExpectEmptyRestOfLine();
if (lang == label)
{
m_inLangConditional = true;
}
else
{
while (!IsAtEnd() && m_buffer[m_pos] != '#')
SkipLine();
if (m_buffer[m_pos] != '#')
RaiseError("unterminated language conditional");
m_pos++;
if (!CheckForDirective("end"))
RaiseError("expected #end");
ExpectEmptyRestOfLine();
}
}
else if (CheckForDirective("end"))
{
if (!m_inLangConditional)
RaiseError("not inside language conditional");
m_inLangConditional = false;
ExpectEmptyRestOfLine();
}
else
{
RaiseError("unknown # directive");
}
}
// Reports a diagnostic message.
void SymFile::ReportDiagnostic(const char* type, const char* format, std::va_list args)
{
const int bufferSize = 1024;
char buffer[bufferSize];
std::vsnprintf(buffer, bufferSize, format, args);
std::fprintf(stderr, "%s:%ld: %s: %s\n", m_filename.c_str(), m_lineNum, type, buffer);
}
#define DO_REPORT(type) \
do \
{ \
std::va_list args; \
va_start(args, format); \
ReportDiagnostic(type, format, args); \
va_end(args); \
} while (0)
// Reports an error diagnostic and terminates the program.
void SymFile::RaiseError(const char* format, ...)
{
DO_REPORT("error");
std::exit(1);
}
// Reports a warning diagnostic.
void SymFile::RaiseWarning(const char* format, ...)
{
DO_REPORT("warning");
}

View File

@ -0,0 +1,71 @@
// Copyright(c) 2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SYM_FILE_H
#define SYM_FILE_H
#include <cstdarg>
#include <cstdint>
#include <string>
#include "ramscrgen.h"
enum class Directive
{
Include,
Space,
Align,
Unknown
};
class SymFile
{
public:
SymFile(std::string filename);
SymFile(SymFile&& other);
SymFile(const SymFile&) = delete;
~SymFile();
Directive GetDirective();
std::string GetLabel(bool requireColon = true);
std::string ReadPath();
bool ReadInteger(unsigned long& value);
void ExpectEmptyRestOfLine();
void SkipLine();
bool IsAtEnd();
void HandleLangConditional(std::string lang);
void RaiseError(const char* format, ...);
void RaiseWarning(const char* format, ...);
private:
char* m_buffer;
long m_pos;
long m_size;
long m_lineNum;
long m_lineStart;
std::string m_filename;
bool m_inLangConditional;
bool ConsumeComma();
void RemoveComments();
bool CheckForDirective(std::string name);
void SkipWhitespace();
void ReportDiagnostic(const char* type, const char* format, std::va_list args);
};
#endif // SYM_FILE_H

1
tools/rsfont/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
rsfont

19
tools/rsfont/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2015-2016 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
tools/rsfont/Makefile Normal file
View File

@ -0,0 +1,24 @@
CC ?= gcc
CFLAGS = -Wall -Wextra -Werror -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
LIBS = -lpng -lz
SRCS = main.c convert_png.c util.c font.c
.PHONY: all clean
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: rsfont$(EXE)
@:
rsfont$(EXE): $(SRCS) convert_png.h gfx.h global.h util.h font.h
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) $(LIBS)
clean:
$(RM) rsfont rsfont.exe

169
tools/rsfont/convert_png.c Normal file
View File

@ -0,0 +1,169 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <setjmp.h>
#include <png.h>
#include "global.h"
#include "convert_png.h"
#include "gfx.h"
void ReadPng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
unsigned char sig[8];
if (fread(sig, 8, 1, fp) != 1)
FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
if (png_sig_cmp(sig, 0, 8))
FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG read struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
if (bit_depth != image->bitDepth)
FATAL_ERROR("\"%s\" has a bit depth of %d, but the expected bit depth is %d.\n", path, bit_depth, image->bitDepth);
int color_type = png_get_color_type(png_ptr, info_ptr);
if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE)
FATAL_ERROR("\"%s\" has an unsupported color type.\n", path);
// Check if the image has a palette so that we can tell if the colors need to be inverted later.
// Don't read the palette because it's not needed for now.
image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE);
image->width = png_get_image_width(png_ptr, info_ptr);
image->height = png_get_image_height(png_ptr, info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image->pixels = malloc(image->height * rowbytes);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate pixel buffer.\n");
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error reading from \"%s\".\n", path);
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(row_pointers);
fclose(fp);
}
void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette)
{
png_colorp colors = malloc(palette->numColors * sizeof(png_color));
if (colors == NULL)
FATAL_ERROR("Failed to allocate PNG palette.\n");
for (int i = 0; i < palette->numColors; i++) {
colors[i].red = palette->colors[i].red;
colors[i].green = palette->colors[i].green;
colors[i].blue = palette->colors[i].blue;
}
png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors);
free(colors);
}
void WritePng(char *path, struct Image *image)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
FATAL_ERROR("Failed to create PNG write struct.\n");
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
FATAL_ERROR("Failed to create PNG info struct.\n");
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing header for \"%s\".\n", path);
int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY;
png_set_IHDR(png_ptr, info_ptr, image->width, image->height,
image->bitDepth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (image->hasPalette) {
SetPngPalette(png_ptr, info_ptr, &image->palette);
if (image->hasTransparency) {
png_byte trans = 0;
png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0);
}
}
png_write_info(png_ptr, info_ptr);
png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
if (row_pointers == NULL)
FATAL_ERROR("Failed to allocate row pointers.\n");
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int i = 0; i < image->height; i++)
row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error writing \"%s\".\n", path);
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr)))
FATAL_ERROR("Error ending write of \"%s\".\n", path);
png_write_end(png_ptr, NULL);
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
}

View File

@ -0,0 +1,11 @@
// Copyright (c) 2015 YamaArashi
#ifndef CONVERT_PNG_H
#define CONVERT_PNG_H
#include "gfx.h"
void ReadPng(char *path, struct Image *image);
void WritePng(char *path, struct Image *image);
#endif // CONVERT_PNG_H

455
tools/rsfont/font.c Normal file
View File

@ -0,0 +1,455 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "global.h"
#include "font.h"
#include "gfx.h"
#include "util.h"
unsigned char gFontPalette[][3] =
{
{0xFF, 0xFF, 0xFF}, // bg (white)
{0x38, 0x38, 0x38}, // fg (dark grey)
{0xD8, 0xD8, 0xD8}, // shadow (light grey)
};
void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[(glyph * 8) + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
}
else
{
// layout type 1
int tile1Offset = glyph * 16;
int tile2Offset = tile1Offset + 8;
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[tile1Offset + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
for (int i = 0; i < 8; i++)
{
uint8_t srcRow = src[tile2Offset + i];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1;
}
}
}
}
}
void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[(glyph * 8) + i] = destRow;
}
}
else
{
// layout type 1
int tile1Offset = glyph * 16;
int tile2Offset = tile1Offset + 8;
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[tile1Offset + i] = destRow;
}
for (int i = 0; i < 8; i++)
{
uint8_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
unsigned char color = src[(y * 128) + x];
if (color > 1)
FATAL_ERROR("More than 2 colors in 1 BPP font.\n");
destRow <<= 1;
destRow |= color;
}
dest[tile2Offset + i] = destRow;
}
}
}
}
void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
static unsigned char table[16] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1,
};
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
int offset = glyph * 32;
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[offset + 3] << 24)
| (src[offset + 2] << 16)
| (src[offset + 1] << 8)
| src[offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
offset += 4;
}
}
else
{
int tile1Offset;
int tile2Offset;
if (layout == 1)
{
tile1Offset = glyph * 64;
tile2Offset = tile1Offset + 32;
}
else
{
tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32);
tile2Offset = tile1Offset + 512;
}
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[tile1Offset + 3] << 24)
| (src[tile1Offset + 2] << 16)
| (src[tile1Offset + 1] << 8)
| src[tile1Offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
tile1Offset += 4;
}
for (int i = 0; i < 8; i++)
{
uint32_t srcRow = (src[tile2Offset + 3] << 24)
| (src[tile2Offset + 2] << 16)
| (src[tile2Offset + 1] << 8)
| src[tile2Offset];
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
dest[(y * 128) + x] = table[srcRow & 0xF];
srcRow >>= 4;
}
tile2Offset += 4;
}
}
}
}
void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout)
{
static unsigned char table[3] =
{
0, 15, 14,
};
for (int glyph = 0; glyph < numGlyphs; glyph++)
{
if (layout == 0)
{
int offset = glyph * 32;
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 8) + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[offset] = destRow & 0xFF;
dest[offset + 1] = (destRow >> 8) & 0xFF;
dest[offset + 2] = (destRow >> 16) & 0xFF;
dest[offset + 3] = (destRow >> 24) & 0xFF;
offset += 4;
}
}
else
{
int tile1Offset;
int tile2Offset;
if (layout == 1)
{
tile1Offset = glyph * 64;
tile2Offset = tile1Offset + 32;
}
else
{
tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32);
tile2Offset = tile1Offset + 512;
}
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[tile1Offset] = destRow & 0xFF;
dest[tile1Offset + 1] = (destRow >> 8) & 0xFF;
dest[tile1Offset + 2] = (destRow >> 16) & 0xFF;
dest[tile1Offset + 3] = (destRow >> 24) & 0xFF;
tile1Offset += 4;
}
for (int i = 0; i < 8; i++)
{
uint32_t destRow = 0;
for (int j = 0; j < 8; j++)
{
int x = ((glyph % 16) * 8) + j;
int y = ((glyph / 16) * 16) + 8 + i;
unsigned char color = src[(y * 128) + x];
if (color > 2)
FATAL_ERROR("More than 3 colors in 4 BPP font.\n");
destRow >>= 4;
destRow |= (table[color] << 28);
}
dest[tile2Offset] = destRow & 0xFF;
dest[tile2Offset + 1] = (destRow >> 8) & 0xFF;
dest[tile2Offset + 2] = (destRow >> 16) & 0xFF;
dest[tile2Offset + 3] = (destRow >> 24) & 0xFF;
tile2Offset += 4;
}
}
}
}
static void SetFontPalette(struct Image *image)
{
image->hasPalette = true;
image->palette.numColors = 3;
for (int i = 0; i < image->palette.numColors; i++)
{
image->palette.colors[i].red = gFontPalette[i][0];
image->palette.colors[i].green = gFontPalette[i][1];
image->palette.colors[i].blue = gFontPalette[i][2];
}
image->hasTransparency = false;
}
int CalcFileSize(int numGlyphs, int bpp, int layout)
{
if (layout == 2)
{
// assume 4 BPP
int numFullRows = numGlyphs / 16;
int remainder = numGlyphs % 16;
int fullRowsSize = numFullRows * 1024;
int remainderSize = 0;
if (remainder != 0)
remainderSize = 1024 - (16 - remainder) * 32;
return fullRowsSize + remainderSize;
}
else
{
int tilesPerGlyph = layout > 0 ? 2 : 1;
int bytesPerTile = 8 * bpp;
return numGlyphs * tilesPerGlyph * bytesPerTile;
}
}
void ReadFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout)
{
int fileSize;
unsigned char *buffer = ReadWholeFile(path, &fileSize);
int expectedFileSize = CalcFileSize(numGlyphs, bpp, layout);
if (fileSize != expectedFileSize)
FATAL_ERROR("The file size is %d but should be %d.\n", fileSize, expectedFileSize);
int numRows = (numGlyphs + 15) / 16;
int rowHeight = layout > 0 ? 16 : 8;
image->width = 128;
image->height = numRows * rowHeight;
image->bitDepth = 8;
image->pixels = calloc(image->width * image->height, 1);
if (image->pixels == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
if (bpp == 1)
ConvertFromTiles1Bpp(buffer, image->pixels, numGlyphs, layout);
else
ConvertFromTiles4Bpp(buffer, image->pixels, numGlyphs, layout);
free(buffer);
SetFontPalette(image);
}
void WriteFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout)
{
if (image->width != 128)
FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width);
int numRows = (numGlyphs + 15) / 16;
int rowHeight = layout > 0 ? 16 : 8;
int expectedHeight = numRows * rowHeight;
if (image->height < expectedHeight)
FATAL_ERROR("The height of the font image (%d) is less than %d.\n", image->height, expectedHeight);
int fileSize = CalcFileSize(numGlyphs, bpp, layout);
unsigned char *buffer = calloc(fileSize, 1);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for font.\n");
if (bpp == 1)
ConvertToTiles1Bpp(image->pixels, buffer, numGlyphs, layout);
else
ConvertToTiles4Bpp(image->pixels, buffer, numGlyphs, layout);
WriteWholeFile(path, buffer, fileSize);
free(buffer);
}

30
tools/rsfont/font.h Normal file
View File

@ -0,0 +1,30 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef FONT_H
#define FONT_H
#include <stdbool.h>
#include "gfx.h"
void ReadFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout);
void WriteFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout);
#endif // FONT_H

50
tools/rsfont/gfx.h Normal file
View File

@ -0,0 +1,50 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef GFX_H
#define GFX_H
#include <stdint.h>
#include <stdbool.h>
struct Color
{
unsigned char red;
unsigned char green;
unsigned char blue;
};
struct Palette
{
struct Color colors[256];
int numColors;
};
struct Image {
int width;
int height;
int bitDepth;
unsigned char *pixels;
bool hasPalette;
struct Palette palette;
bool hasTransparency;
};
#endif // GFX_H

31
tools/rsfont/global.h Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2015 YamaArashi
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED
#else
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#define UNUSED __attribute__((__unused__))
#endif // _MSC_VER
#endif // GLOBAL_H

93
tools/rsfont/main.c Normal file
View File

@ -0,0 +1,93 @@
// Copyright(c) 2015-2016 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "global.h"
#include "util.h"
#include "gfx.h"
#include "convert_png.h"
#include "font.h"
int ExtensionToBpp(const char *extension)
{
if (!strcmp(extension, "1bpp"))
return 1;
else if (!strcmp(extension, "4bpp"))
return 4;
return 0;
}
int main(int argc, char **argv)
{
if (argc < 5)
FATAL_ERROR("Usage: rsfont INPUT_FILE OUTPUT_FILE NUM_GLYPHS LAYOUT_TYPE\n");
char *inputPath = argv[1];
char *outputPath = argv[2];
char *inputFileExtension = GetFileExtension(inputPath);
char *outputFileExtension = GetFileExtension(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);
int numGlyphs;
int bpp;
int layout;
if (!ParseNumber(argv[3], NULL, 10, &numGlyphs))
FATAL_ERROR("Failed to parse number of glyphs.\n");
if (!ParseNumber(argv[4], NULL, 10, &layout))
FATAL_ERROR("Failed to parse layout type.\n");
if (layout < 0 || layout > 2)
FATAL_ERROR("Layout type %d is invalid. Layout type must be 0, 1, or 2.\n", layout);
bool toPng;
if (!strcmp(inputFileExtension, "png") && (bpp = ExtensionToBpp(outputFileExtension)) != 0)
toPng = false;
else if ((bpp = ExtensionToBpp(inputFileExtension)) != 0 && !strcmp(outputFileExtension, "png"))
toPng = true;
else
FATAL_ERROR("Don't know how to convert \"%s\" to \"%s\".\n", inputPath, outputPath);
if (bpp == 1 && layout == 2)
FATAL_ERROR("Layout type 2 is not supported with 1 BPP fonts.\n");
struct Image image;
if (toPng)
{
ReadFont(inputPath, &image, numGlyphs, bpp, layout);
WritePng(outputPath, &image);
}
else
{
image.bitDepth = 8;
ReadPng(inputPath, &image);
WriteFont(outputPath, &image, numGlyphs, bpp, layout);
}
}

124
tools/rsfont/util.c Normal file
View File

@ -0,0 +1,124 @@
// Copyright (c) 2015 YamaArashi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <limits.h>
#include "global.h"
#include "util.h"
bool ParseNumber(char *s, char **end, int radix, int *intValue)
{
char *localEnd;
if (end == NULL)
end = &localEnd;
errno = 0;
const long longValue = strtol(s, end, radix);
if (*end == s)
return false; // not a number
if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE)
return false;
if (longValue > INT_MAX)
return false;
if (longValue < INT_MIN)
return false;
*intValue = (int)longValue;
return true;
}
char *GetFileExtension(char *path)
{
char *extension = path;
while (*extension != 0)
extension++;
while (extension > path && *extension != '.')
extension--;
if (extension == path)
return NULL;
extension++;
if (*extension == 0)
return NULL;
return extension;
}
unsigned char *ReadWholeFile(char *path, int *size)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = malloc(*size);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount)
{
FILE *fp = fopen(path, "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *buffer = calloc(*size + padAmount, 1);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path);
rewind(fp);
if (fread(buffer, *size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path);
fclose(fp);
return buffer;
}
void WriteWholeFile(char *path, void *buffer, int bufferSize)
{
FILE *fp = fopen(path, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
if (fwrite(buffer, bufferSize, 1, fp) != 1)
FATAL_ERROR("Failed to write to \"%s\".\n", path);
fclose(fp);
}

14
tools/rsfont/util.h Normal file
View File

@ -0,0 +1,14 @@
// Copyright (c) 2015 YamaArashi
#ifndef UTIL_H
#define UTIL_H
#include <stdbool.h>
bool ParseNumber(char *s, char **end, int radix, int *intValue);
char *GetFileExtension(char *path);
unsigned char *ReadWholeFile(char *path, int *size);
unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount);
void WriteWholeFile(char *path, void *buffer, int bufferSize);
#endif // UTIL_H

1
tools/scaninc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
scaninc

19
tools/scaninc/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2015 YamaArashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

24
tools/scaninc/Makefile Normal file
View File

@ -0,0 +1,24 @@
CXX ?= g++
CXXFLAGS = -Wall -Werror -std=c++11 -O2
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: all clean
ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif
all: scaninc$(EXE)
@:
scaninc$(EXE): $(SRCS) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
$(RM) scaninc scaninc.exe

192
tools/scaninc/asm_file.cpp Normal file
View File

@ -0,0 +1,192 @@
// Copyright(c) 2015-2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <string>
#include "scaninc.h"
#include "asm_file.h"
AsmFile::AsmFile(std::string path)
{
m_path = path;
FILE *fp = std::fopen(path.c_str(), "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str());
std::fseek(fp, 0, SEEK_END);
m_size = std::ftell(fp);
m_buffer = new char[m_size];
std::rewind(fp);
if (std::fread(m_buffer, m_size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path.c_str());
std::fclose(fp);
m_pos = 0;
m_lineNum = 1;
}
AsmFile::~AsmFile()
{
delete[] m_buffer;
}
IncDirectiveType AsmFile::ReadUntilIncDirective(std::string &path)
{
// At the beginning of each loop iteration, the current file position
// should be at the start of a line or at the end of the file.
for (;;)
{
SkipTabsAndSpaces();
IncDirectiveType incDirectiveType = IncDirectiveType::None;
char c = PeekChar();
if (c == '.' || c == '#')
{
m_pos++;
if (MatchIncDirective("incbin", path))
incDirectiveType = IncDirectiveType::Incbin;
else if (MatchIncDirective("include", path))
incDirectiveType = IncDirectiveType::Include;
}
for (;;)
{
int c = GetChar();
if (c == -1)
return incDirectiveType;
if (c == ';')
{
SkipEndOfLineComment();
break;
}
else if (c == '/' && PeekChar() == '*')
{
m_pos++;
SkipMultiLineComment();
}
else if (c == '"')
{
SkipString();
}
else if (c == '\n')
{
break;
}
}
if (incDirectiveType != IncDirectiveType::None)
return incDirectiveType;
}
}
std::string AsmFile::ReadPath()
{
int length = 0;
int startPos = m_pos;
for (;;)
{
int c = GetChar();
if (c == '"')
break;
if (c == -1)
FATAL_INPUT_ERROR("unexpected EOF in include string\n");
if (c == 0)
FATAL_INPUT_ERROR("unexpected NUL character in include string\n");
if (c == '\n')
FATAL_INPUT_ERROR("unexpected end of line character in include string\n");
// Don't bother allowing any escape sequences.
if (c == '\\')
FATAL_INPUT_ERROR("unexpected escape in include string\n");
length++;
if (length > SCANINC_MAX_PATH)
FATAL_INPUT_ERROR("path is too long");
}
return std::string(m_buffer + startPos, length);
}
void AsmFile::SkipEndOfLineComment()
{
int c;
do
{
c = GetChar();
} while (c != -1 && c != '\n');
}
void AsmFile::SkipMultiLineComment()
{
for (;;)
{
int c = GetChar();
if (c == '*')
{
if (PeekChar() == '/')
{
m_pos++;
return;
}
}
else if (c == -1)
{
return;
}
}
}
void AsmFile::SkipString()
{
for (;;)
{
int c = GetChar();
if (c == '"')
break;
if (c == -1)
FATAL_INPUT_ERROR("unexpected EOF in string\n");
if (c == '\\')
{
c = GetChar();
}
}
}

119
tools/scaninc/asm_file.h Normal file
View File

@ -0,0 +1,119 @@
// Copyright(c) 2015-2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ASM_FILE_H
#define ASM_FILE_H
#include <string>
#include "scaninc.h"
enum class IncDirectiveType
{
None,
Include,
Incbin
};
class AsmFile
{
public:
AsmFile(std::string path);
~AsmFile();
IncDirectiveType ReadUntilIncDirective(std::string& path);
private:
char *m_buffer;
int m_pos;
int m_size;
int m_lineNum;
std::string m_path;
int GetChar()
{
if (m_pos >= m_size)
return -1;
int c = m_buffer[m_pos++];
if (c == '\r')
{
if (m_pos < m_size && m_buffer[m_pos++] == '\n')
{
m_lineNum++;
return '\n';
}
else
{
FATAL_INPUT_ERROR("CR line endings are not supported\n");
}
}
if (c == '\n')
m_lineNum++;
return c;
}
// No newline translation because it's not needed for any use of this function.
int PeekChar()
{
if (m_pos >= m_size)
return -1;
return m_buffer[m_pos];
}
void SkipTabsAndSpaces()
{
while (m_pos < m_size && (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' '))
m_pos++;
}
bool MatchIncDirective(std::string directiveName, std::string& path)
{
int length = directiveName.length();
int i;
for (i = 0; i < length && m_pos + i < m_size; i++)
if (directiveName[i] != m_buffer[m_pos + i])
return false;
if (i < length)
return false;
m_pos += length;
SkipTabsAndSpaces();
if (GetChar() != '"')
FATAL_INPUT_ERROR("no path after \".%s\" directive\n", directiveName.c_str());
path = ReadPath();
return true;
}
std::string ReadPath();
void SkipEndOfLineComment();
void SkipMultiLineComment();
void SkipString();
};
#endif // ASM_FILE_H

307
tools/scaninc/c_file.cpp Normal file
View File

@ -0,0 +1,307 @@
// Copyright(c) 2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "c_file.h"
CFile::CFile(std::string path)
{
m_path = path;
FILE *fp = std::fopen(path.c_str(), "rb");
if (fp == NULL)
FATAL_ERROR("Failed to open \"%s\" for reading.\n", path.c_str());
std::fseek(fp, 0, SEEK_END);
m_size = std::ftell(fp);
m_buffer = new char[m_size + 1];
m_buffer[m_size] = 0;
std::rewind(fp);
if (std::fread(m_buffer, m_size, 1, fp) != 1)
FATAL_ERROR("Failed to read \"%s\".\n", path.c_str());
std::fclose(fp);
m_pos = 0;
m_lineNum = 1;
}
CFile::~CFile()
{
delete[] m_buffer;
}
void CFile::FindIncbins()
{
char stringChar = 0;
while (m_pos < m_size)
{
if (stringChar)
{
if (m_buffer[m_pos] == stringChar)
{
m_pos++;
stringChar = 0;
}
else if (m_buffer[m_pos] == '\\' && m_buffer[m_pos + 1] == stringChar)
{
m_pos += 2;
}
else
{
if (m_buffer[m_pos] == '\n')
m_lineNum++;
m_pos++;
}
}
else
{
SkipWhitespace();
CheckInclude();
CheckIncbin();
if (m_pos >= m_size)
break;
char c = m_buffer[m_pos++];
if (c == '\n')
m_lineNum++;
else if (c == '"')
stringChar = '"';
else if (c == '\'')
stringChar = '\'';
else if (c == 0)
FATAL_INPUT_ERROR("unexpected null character");
}
}
}
bool CFile::ConsumeHorizontalWhitespace()
{
if (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ')
{
m_pos++;
return true;
}
return false;
}
bool CFile::ConsumeNewline()
{
if (m_buffer[m_pos] == '\n')
{
m_pos++;
m_lineNum++;
return true;
}
if (m_buffer[m_pos] == '\r' && m_buffer[m_pos + 1] == '\n')
{
m_pos += 2;
m_lineNum++;
return true;
}
return false;
}
bool CFile::ConsumeComment()
{
if (m_buffer[m_pos] == '/' && m_buffer[m_pos + 1] == '*')
{
m_pos += 2;
while (m_buffer[m_pos] != '*' || m_buffer[m_pos + 1] != '/')
{
if (m_buffer[m_pos] == 0)
return false;
if (!ConsumeNewline())
m_pos++;
}
m_pos += 2;
return true;
}
else if (m_buffer[m_pos] == '/' && m_buffer[m_pos + 1] == '/')
{
m_pos += 2;
while (!ConsumeNewline())
{
if (m_buffer[m_pos] == 0)
return false;
m_pos++;
}
return true;
}
return false;
}
void CFile::SkipWhitespace()
{
while (ConsumeHorizontalWhitespace() || ConsumeNewline() || ConsumeComment())
;
}
bool CFile::CheckIdentifier(const std::string& ident)
{
unsigned int i;
for (i = 0; i < ident.length() && m_pos + i < (unsigned)m_size; i++)
if (ident[i] != m_buffer[m_pos + i])
return false;
return (i == ident.length());
}
void CFile::CheckInclude()
{
if (m_buffer[m_pos] != '#')
return;
std::string ident = "#include";
if (!CheckIdentifier(ident))
{
return;
}
m_pos += ident.length();
ConsumeHorizontalWhitespace();
std::string path = ReadPath();
if (!path.empty()) {
m_includes.emplace(path);
}
}
void CFile::CheckIncbin()
{
// Optimization: assume most lines are not incbins
if (!(m_buffer[m_pos+0] == 'I'
&& m_buffer[m_pos+1] == 'N'
&& m_buffer[m_pos+2] == 'C'
&& m_buffer[m_pos+3] == 'B'
&& m_buffer[m_pos+4] == 'I'
&& m_buffer[m_pos+5] == 'N'
&& m_buffer[m_pos+6] == '_'))
{
return;
}
std::string idents[6] = { "INCBIN_S8", "INCBIN_U8", "INCBIN_S16", "INCBIN_U16", "INCBIN_S32", "INCBIN_U32" };
int incbinType = -1;
for (int i = 0; i < 6; i++)
{
if (CheckIdentifier(idents[i]))
{
incbinType = i;
break;
}
}
if (incbinType == -1)
return;
long oldPos = m_pos;
long oldLineNum = m_lineNum;
m_pos += idents[incbinType].length();
SkipWhitespace();
if (m_buffer[m_pos] != '(')
{
m_pos = oldPos;
m_lineNum = oldLineNum;
return;
}
m_pos++;
while (true)
{
SkipWhitespace();
std::string path = ReadPath();
SkipWhitespace();
m_incbins.emplace(path);
if (m_buffer[m_pos] != ',')
break;
m_pos++;
}
if (m_buffer[m_pos] != ')')
FATAL_INPUT_ERROR("expected ')'");
m_pos++;
}
std::string CFile::ReadPath()
{
if (m_buffer[m_pos] != '"')
{
if (m_buffer[m_pos] == '<')
{
return std::string();
}
FATAL_INPUT_ERROR("expected '\"' or '<'");
}
m_pos++;
int startPos = m_pos;
while (m_buffer[m_pos] != '"')
{
if (m_buffer[m_pos] == 0)
{
if (m_pos >= m_size)
FATAL_INPUT_ERROR("unexpected EOF in path string");
else
FATAL_INPUT_ERROR("unexpected null character in path string");
}
if (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')
FATAL_INPUT_ERROR("unexpected end of line character in path string");
if (m_buffer[m_pos] == '\\')
FATAL_INPUT_ERROR("unexpected escape in path string");
m_pos++;
}
m_pos++;
return std::string(m_buffer + startPos, m_pos - 1 - startPos);
}

57
tools/scaninc/c_file.h Normal file
View File

@ -0,0 +1,57 @@
// Copyright(c) 2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef C_FILE_H
#define C_FILE_H
#include <string>
#include <set>
#include <memory>
#include "scaninc.h"
class CFile
{
public:
CFile(std::string path);
~CFile();
void FindIncbins();
const std::set<std::string>& GetIncbins() { return m_incbins; }
const std::set<std::string>& GetIncludes() { return m_includes; }
private:
char *m_buffer;
int m_pos;
int m_size;
int m_lineNum;
std::string m_path;
std::set<std::string> m_incbins;
std::set<std::string> m_includes;
bool ConsumeHorizontalWhitespace();
bool ConsumeNewline();
bool ConsumeComment();
void SkipWhitespace();
bool CheckIdentifier(const std::string& ident);
void CheckInclude();
void CheckIncbin();
std::string ReadPath();
};
#endif // C_FILE_H

128
tools/scaninc/scaninc.cpp Normal file
View File

@ -0,0 +1,128 @@
// Copyright(c) 2015-2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <cstdio>
#include <cstdlib>
#include <list>
#include <queue>
#include <set>
#include <string>
#include "scaninc.h"
#include "source_file.h"
bool CanOpenFile(std::string path)
{
FILE *fp = std::fopen(path.c_str(), "rb");
if (fp == NULL)
return false;
std::fclose(fp);
return true;
}
const char *const USAGE = "Usage: scaninc [-I INCLUDE_PATH] FILE_PATH\n";
int main(int argc, char **argv)
{
std::queue<std::string> filesToProcess;
std::set<std::string> dependencies;
std::vector<std::string> includeDirs;
argc--;
argv++;
while (argc > 1)
{
std::string arg(argv[0]);
if (arg.substr(0, 2) == "-I")
{
std::string includeDir = arg.substr(2);
if (includeDir.empty())
{
argc--;
argv++;
includeDir = std::string(argv[0]);
}
if (!includeDir.empty() && includeDir.back() != '/')
{
includeDir += '/';
}
includeDirs.push_back(includeDir);
}
else
{
FATAL_ERROR(USAGE);
}
argc--;
argv++;
}
if (argc != 1) {
FATAL_ERROR(USAGE);
}
std::string initialPath(argv[0]);
filesToProcess.push(initialPath);
while (!filesToProcess.empty())
{
std::string filePath = filesToProcess.front();
SourceFile file(filePath);
filesToProcess.pop();
includeDirs.push_back(file.GetSrcDir());
for (auto incbin : file.GetIncbins())
{
dependencies.insert(incbin);
}
for (auto include : file.GetIncludes())
{
bool exists = false;
std::string path("");
for (auto includeDir : includeDirs)
{
path = includeDir + include;
if (CanOpenFile(path))
{
exists = true;
break;
}
}
if (!exists && (file.FileType() == SourceFileType::Asm || file.FileType() == SourceFileType::Inc))
{
path = include;
}
bool inserted = dependencies.insert(path).second;
if (inserted && exists)
{
filesToProcess.push(path);
}
}
includeDirs.pop_back();
}
for (const std::string &path : dependencies)
{
std::printf("%s\n", path.c_str());
}
}

59
tools/scaninc/scaninc.h Normal file
View File

@ -0,0 +1,59 @@
// Copyright(c) 2015-2017 YamaArashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SCANINC_H
#define SCANINC_H
#include <cstdio>
#include <cstdlib>
#ifdef _MSC_VER
#define FATAL_INPUT_ERROR(format, ...) \
do { \
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, __VA_ARGS__); \
exit(1); \
} while (0)
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, __VA_ARGS__); \
exit(1); \
} while (0)
#else
#define FATAL_INPUT_ERROR(format, ...) \
do { \
fprintf(stderr, "%s:%d " format, m_path.c_str(), m_lineNum, ##__VA_ARGS__); \
exit(1); \
} while (0)
#define FATAL_ERROR(format, ...) \
do { \
fprintf(stderr, format, ##__VA_ARGS__); \
exit(1); \
} while (0)
#endif // _MSC_VER
#define SCANINC_MAX_PATH 255
#endif // SCANINC_H

View File

@ -0,0 +1,130 @@
// Copyright(c) 2019 Phlosioneer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <new>
#include "source_file.h"
SourceFileType GetFileType(std::string& path)
{
std::size_t pos = path.find_last_of('.');
if (pos == std::string::npos)
FATAL_ERROR("no file extension in path \"%s\"\n", path.c_str());
std::string extension = path.substr(pos + 1);
if (extension == "c")
return SourceFileType::Cpp;
else if (extension == "s")
return SourceFileType::Asm;
else if (extension == "h")
return SourceFileType::Header;
else if (extension == "inc")
return SourceFileType::Inc;
else
FATAL_ERROR("Unrecognized extension \"%s\"\n", extension.c_str());
// Unreachable
return SourceFileType::Cpp;
}
std::string GetDir(std::string& path)
{
std::size_t slash = path.rfind('/');
if (slash != std::string::npos)
return path.substr(0, slash + 1);
else
return std::string("");
}
SourceFile::SourceFile(std::string path)
{
m_file_type = GetFileType(path);
m_src_dir = GetDir(path);
if (m_file_type == SourceFileType::Cpp
|| m_file_type == SourceFileType::Header)
{
new (&m_source_file.c_file) CFile(path);
m_source_file.c_file.FindIncbins();
}
else
{
AsmFile file(path);
std::set<std::string> incbins;
std::set<std::string> includes;
IncDirectiveType incDirectiveType;
std::string outputPath;
while ((incDirectiveType = file.ReadUntilIncDirective(outputPath)) != IncDirectiveType::None)
{
if (incDirectiveType == IncDirectiveType::Include)
includes.insert(outputPath);
else
incbins.insert(outputPath);
}
new (&m_source_file.asm_wrapper) SourceFile::InnerUnion::AsmWrapper{incbins, includes};
}
}
SourceFileType SourceFile::FileType()
{
return m_file_type;
}
SourceFile::~SourceFile()
{
if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header)
{
m_source_file.c_file.~CFile();
}
else
{
m_source_file.asm_wrapper.asm_incbins.~set();
m_source_file.asm_wrapper.asm_includes.~set();
}
}
const std::set<std::string>& SourceFile::GetIncbins()
{
if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header)
return m_source_file.c_file.GetIncbins();
else
return m_source_file.asm_wrapper.asm_incbins;
}
const std::set<std::string>& SourceFile::GetIncludes()
{
if (m_file_type == SourceFileType::Cpp || m_file_type == SourceFileType::Header)
return m_source_file.c_file.GetIncludes();
else
return m_source_file.asm_wrapper.asm_includes;
}
std::string& SourceFile::GetSrcDir()
{
return m_src_dir;
}

View File

@ -0,0 +1,72 @@
// Copyright(c) 2019 Phlosioneer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef SOURCE_FILE_H
#define SOURCE_FILE_H
#include <string>
#include "scaninc.h"
#include "asm_file.h"
#include "c_file.h"
enum class SourceFileType
{
Cpp,
Header,
Asm,
Inc
};
SourceFileType GetFileType(std::string& path);
class SourceFile
{
public:
SourceFile(std::string path);
~SourceFile();
SourceFile(SourceFile const&) = delete;
SourceFile(SourceFile&&) = delete;
SourceFile& operator =(SourceFile const&) = delete;
SourceFile& operator =(SourceFile&&) = delete;
bool HasIncbins();
const std::set<std::string>& GetIncbins();
const std::set<std::string>& GetIncludes();
std::string& GetSrcDir();
SourceFileType FileType();
private:
union InnerUnion {
CFile c_file;
struct AsmWrapper {
std::set<std::string> asm_incbins;
std::set<std::string> asm_includes;
} asm_wrapper;
// Construction and destruction handled by SourceFile.
InnerUnion() {};
~InnerUnion() {};
} m_source_file;
SourceFileType m_file_type;
std::string m_src_dir;
};
#endif // SOURCE_FILE_H