import from github
This commit is contained in:
2
tools/aif2pcm/.gitignore
vendored
Normal file
2
tools/aif2pcm/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
aif2pcm
|
||||
|
||||
20
tools/aif2pcm/LICENSE
Normal file
20
tools/aif2pcm/LICENSE
Normal 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
24
tools/aif2pcm/Makefile
Normal 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
172
tools/aif2pcm/extended.c
Normal 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
911
tools/aif2pcm/main.c
Normal 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
1
tools/bin2c/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bin2c
|
||||
19
tools/bin2c/LICENSE
Normal file
19
tools/bin2c/LICENSE
Normal 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
22
tools/bin2c/Makefile
Normal 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
201
tools/bin2c/bin2c.c
Normal 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
2
tools/gbafix/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
gbafix
|
||||
README
|
||||
674
tools/gbafix/COPYING
Normal file
674
tools/gbafix/COPYING
Normal 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
19
tools/gbafix/Makefile
Normal 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
3147
tools/gbafix/elf.h
Normal 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
317
tools/gbafix/gbafix.c
Normal 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
1
tools/gbagfx/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
gbagfx
|
||||
19
tools/gbagfx/LICENSE
Normal file
19
tools/gbagfx/LICENSE
Normal 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
27
tools/gbagfx/Makefile
Normal 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
254
tools/gbagfx/convert_png.c
Normal 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);
|
||||
}
|
||||
12
tools/gbagfx/convert_png.h
Normal file
12
tools/gbagfx/convert_png.h
Normal 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
326
tools/gbagfx/font.c
Normal 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
16
tools/gbagfx/font.h
Normal 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
507
tools/gbagfx/gfx.c
Normal 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
53
tools/gbagfx/gfx.h
Normal 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
31
tools/gbagfx/global.h
Normal 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
398
tools/gbagfx/huff.c
Normal 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
38
tools/gbagfx/huff.h
Normal 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
172
tools/gbagfx/jasc_pal.c
Normal 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
9
tools/gbagfx/jasc_pal.h
Normal 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
153
tools/gbagfx/lz.c
Normal 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
9
tools/gbagfx/lz.h
Normal 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
626
tools/gbagfx/main.c
Normal 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
28
tools/gbagfx/options.h
Normal 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
149
tools/gbagfx/rl.c
Normal 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
9
tools/gbagfx/rl.h
Normal 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
131
tools/gbagfx/util.c
Normal 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
15
tools/gbagfx/util.h
Normal 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
1
tools/jsonproc/.gitignore
vendored
Executable file
@@ -0,0 +1 @@
|
||||
jsonproc
|
||||
26
tools/jsonproc/Makefile
Executable file
26
tools/jsonproc/Makefile
Executable 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
3529
tools/jsonproc/inja.hpp
Executable 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
109
tools/jsonproc/jsonproc.cpp
Executable 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
32
tools/jsonproc/jsonproc.h
Executable 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
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
1
tools/mapjson/.gitignore
vendored
Executable file
@@ -0,0 +1 @@
|
||||
mapjson
|
||||
24
tools/mapjson/Makefile
Normal file
24
tools/mapjson/Makefile
Normal 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
786
tools/mapjson/json11.cpp
Normal 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
230
tools/mapjson/json11.h
Normal 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
566
tools/mapjson/mapjson.cpp
Normal 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
31
tools/mapjson/mapjson.h
Normal 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
1
tools/mid2agb/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
mid2agb
|
||||
19
tools/mid2agb/LICENSE
Normal file
19
tools/mid2agb/LICENSE
Normal 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
24
tools/mid2agb/Makefile
Normal 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
547
tools/mid2agb/agb.cpp
Normal 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
33
tools/mid2agb/agb.h
Normal 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
36
tools/mid2agb/error.cpp
Normal 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
26
tools/mid2agb/error.h
Normal 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
233
tools/mid2agb/main.cpp
Normal 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
39
tools/mid2agb/main.h
Normal 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
964
tools/mid2agb/midi.cpp
Normal 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
87
tools/mid2agb/midi.h
Normal 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
286
tools/mid2agb/tables.cpp
Normal 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
29
tools/mid2agb/tables.h
Normal 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
1
tools/preproc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
preproc
|
||||
19
tools/preproc/LICENSE
Normal file
19
tools/preproc/LICENSE
Normal 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
26
tools/preproc/Makefile
Normal 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
599
tools/preproc/asm_file.cpp
Normal 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
73
tools/preproc/asm_file.h
Normal 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
459
tools/preproc/c_file.cpp
Normal 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
61
tools/preproc/c_file.h
Normal 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
71
tools/preproc/char_util.h
Normal 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
408
tools/preproc/charmap.cpp
Normal 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
64
tools/preproc/charmap.h
Normal 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
164
tools/preproc/preproc.cpp
Normal 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
54
tools/preproc/preproc.h
Normal 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
|
||||
355
tools/preproc/string_parser.cpp
Normal file
355
tools/preproc/string_parser.cpp
Normal 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++;
|
||||
}
|
||||
55
tools/preproc/string_parser.h
Normal file
55
tools/preproc/string_parser.h
Normal 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
92
tools/preproc/utf8.cpp
Normal 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
34
tools/preproc/utf8.h
Normal 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
1
tools/ramscrgen/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ramscrgen
|
||||
19
tools/ramscrgen/LICENSE
Normal file
19
tools/ramscrgen/LICENSE
Normal 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
24
tools/ramscrgen/Makefile
Normal 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
|
||||
71
tools/ramscrgen/char_util.h
Normal file
71
tools/ramscrgen/char_util.h
Normal 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
272
tools/ramscrgen/elf.cpp
Normal 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
30
tools/ramscrgen/elf.h
Normal 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
187
tools/ramscrgen/main.cpp
Normal 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;
|
||||
}
|
||||
49
tools/ramscrgen/ramscrgen.h
Normal file
49
tools/ramscrgen/ramscrgen.h
Normal 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
|
||||
492
tools/ramscrgen/sym_file.cpp
Normal file
492
tools/ramscrgen/sym_file.cpp
Normal 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");
|
||||
}
|
||||
71
tools/ramscrgen/sym_file.h
Normal file
71
tools/ramscrgen/sym_file.h
Normal 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
1
tools/rsfont/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
rsfont
|
||||
19
tools/rsfont/LICENSE
Normal file
19
tools/rsfont/LICENSE
Normal 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
24
tools/rsfont/Makefile
Normal 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
169
tools/rsfont/convert_png.c
Normal 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);
|
||||
}
|
||||
11
tools/rsfont/convert_png.h
Normal file
11
tools/rsfont/convert_png.h
Normal 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
455
tools/rsfont/font.c
Normal 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
30
tools/rsfont/font.h
Normal 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
50
tools/rsfont/gfx.h
Normal 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
31
tools/rsfont/global.h
Normal 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
93
tools/rsfont/main.c
Normal 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
124
tools/rsfont/util.c
Normal 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
14
tools/rsfont/util.h
Normal 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
1
tools/scaninc/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
scaninc
|
||||
19
tools/scaninc/LICENSE
Normal file
19
tools/scaninc/LICENSE
Normal 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
24
tools/scaninc/Makefile
Normal 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
192
tools/scaninc/asm_file.cpp
Normal 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
119
tools/scaninc/asm_file.h
Normal 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
307
tools/scaninc/c_file.cpp
Normal 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
57
tools/scaninc/c_file.h
Normal 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
128
tools/scaninc/scaninc.cpp
Normal 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
59
tools/scaninc/scaninc.h
Normal 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
|
||||
130
tools/scaninc/source_file.cpp
Normal file
130
tools/scaninc/source_file.cpp
Normal 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;
|
||||
}
|
||||
|
||||
72
tools/scaninc/source_file.h
Normal file
72
tools/scaninc/source_file.h
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user