mirror of
https://github.com/pret/pokeplatinum.git
synced 2026-04-24 23:18:36 -05:00
242 lines
7.3 KiB
C
242 lines
7.3 KiB
C
#ifndef POKEPLATINUM_DATAGEN_H
|
|
#define POKEPLATINUM_DATAGEN_H
|
|
|
|
/*
|
|
* This is a shareable header of utility functions which are of use to data generation
|
|
* and packing programs.
|
|
*/
|
|
|
|
// clang-format off
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <rapidjson/document.h>
|
|
#include <rapidjson/error/en.h>
|
|
#include <narc/narc.h>
|
|
// clang-format on
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
// This attribute is useful when working with structures that have an element
|
|
// which must be word-aligned. For an example (and reasoning), see
|
|
// `SpeciesEvolutionList` in `datagen_species.cpp`.
|
|
#define ALIGN_4 __attribute__((aligned(4)))
|
|
|
|
typedef uint8_t u8;
|
|
typedef uint16_t u16;
|
|
typedef uint32_t u32;
|
|
typedef int8_t s8;
|
|
typedef int16_t s16;
|
|
typedef int32_t s32;
|
|
typedef bool BOOL;
|
|
|
|
#define TRUE true
|
|
#define FALSE false
|
|
|
|
// A lookup-table entry, mirroring the definition of structures generated by `metang`.
|
|
struct LookupEntry {
|
|
const long value;
|
|
const char *def;
|
|
};
|
|
|
|
// Search through a lookup-table for a string value.
|
|
static inline long Search(const LookupEntry *lookupTable, int low, int high, const char *val)
|
|
{
|
|
while (low <= high) {
|
|
int mid = low + (high - low) / 2;
|
|
|
|
if (strcmp(val, lookupTable[mid].def) == 0) {
|
|
return lookupTable[mid].value;
|
|
}
|
|
|
|
const char *val_p = val;
|
|
const char *def_p = lookupTable[mid].def;
|
|
while (*val_p && *def_p && *val_p == *def_p) {
|
|
val_p++;
|
|
def_p++;
|
|
}
|
|
|
|
if (*val_p > *def_p) {
|
|
low = mid + 1;
|
|
} else {
|
|
high = mid - 1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Lookup a constant from a lookup-table. If the value is not found, then an
|
|
// `invalid_argument` exception will be thrown. A C-string is taken for the
|
|
// lookup-value for compatibility with rapidjson, which only uses C-strings.
|
|
static inline long Lookup(const LookupEntry *lookupTable, int low, int high, const char *val, const std::string &valDesc)
|
|
{
|
|
long result = Search(lookupTable, low, high, val);
|
|
if (result < 0) {
|
|
std::stringstream buf;
|
|
buf << "no match found for " << val << " as " << valDesc;
|
|
throw std::invalid_argument(buf.str());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// A C++-string wrapper to lookup a constant from a lookup table.
|
|
static inline long Lookup(const LookupEntry *lookupTable, int low, int high, const std::string &val, const std::string &valDesc)
|
|
{
|
|
return Lookup(lookupTable, low, high, val.c_str(), valDesc);
|
|
}
|
|
|
|
// This macro is a standardized bit of short-hand to lookup a constant value
|
|
// of a particular type. e.g., to lookup an Ability constant string's value, you
|
|
// would invoke `LookupConst(abilityString, Ability)`; this would expand to:
|
|
// `Lookup((LookupEntry *)lookup__Ability, 0, num__Ability, abilityString, "Ability")`
|
|
#define LookupConst(val, T) Lookup(reinterpret_cast<const LookupEntry *>(lookup__##T), 0, lengthof__##T, val, #T)
|
|
|
|
// Align a value to the next 4-byte word-boundary.
|
|
static inline long AlignToWord(long val)
|
|
{
|
|
return val + (-val & 3);
|
|
}
|
|
|
|
// Tokenize a string by a given delimiter into a vector of components.
|
|
static inline std::vector<std::string> Tokenize(const std::string &s, const char delim = ' ')
|
|
{
|
|
std::vector<std::string> tokens;
|
|
std::size_t start = s.find_first_not_of(delim, 0);
|
|
std::size_t end = 0;
|
|
while (start != std::string::npos) {
|
|
end = s.find_first_of(delim, start);
|
|
tokens.emplace_back(s.substr(start, end - start));
|
|
start = s.find_first_not_of(delim, end);
|
|
}
|
|
|
|
return tokens;
|
|
}
|
|
|
|
// Pack a NARC to an output path from a VFS context.
|
|
static inline void PackNarc(vfs_pack_ctx *ctx, fs::path path)
|
|
{
|
|
narc *narc = narc_pack(ctx);
|
|
|
|
std::ofstream ofs(path);
|
|
ofs.write(reinterpret_cast<char *>(narc), narc->size);
|
|
|
|
free(narc);
|
|
}
|
|
|
|
// Pack a single-file NARC of binary elements to an output path.
|
|
template <typename T>
|
|
static inline void PackSingleFileNarc(std::vector<T> &elems, fs::path path)
|
|
{
|
|
if (elems.empty()) {
|
|
return;
|
|
}
|
|
|
|
vfs_pack_ctx *vfs = narc_pack_start();
|
|
narc_pack_file_copy(vfs, reinterpret_cast<unsigned char *>(elems.data()), sizeof(elems[0]) * elems.size());
|
|
PackNarc(vfs, path);
|
|
}
|
|
|
|
// Read a whole file into a C++-string.
|
|
static inline std::string ReadWholeFile(std::ifstream &ifs)
|
|
{
|
|
ifs.seekg(0, std::ios::end);
|
|
size_t size = ifs.tellg();
|
|
std::string contents(size, ' ');
|
|
ifs.seekg(0);
|
|
ifs.read(&contents[0], size);
|
|
|
|
return contents;
|
|
}
|
|
|
|
// Read a whole file into a C++-string.
|
|
// This routine is a potential exit-point if the file cannot be loaded for reading.
|
|
static inline std::string ReadWholeFile(fs::path &fname)
|
|
{
|
|
std::ifstream ifs(fname, std::ios::in);
|
|
if (ifs.fail()) {
|
|
std::cerr << "could not open file " << fs::relative(fname).generic_string() << std::endl;
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return ReadWholeFile(ifs);
|
|
}
|
|
|
|
// Read a file's lines into a vector of C++ strings.
|
|
static inline std::vector<std::string> ReadFileLines(fs::path &fname)
|
|
{
|
|
std::ifstream ifs(fname);
|
|
std::vector<std::string> lines;
|
|
std::string line;
|
|
while (std::getline(ifs, line)) {
|
|
lines.emplace_back(line);
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
// Read a registry-type environment variable into a vector of C++ strings.
|
|
static inline std::vector<std::string> ReadRegistryEnvVar(const char *var)
|
|
{
|
|
const char *val_p = NULL;
|
|
if ((val_p = std::getenv(var)) == NULL) {
|
|
std::cerr << "Missing definition for registry environment variable " << var << std::endl;
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
std::string val = val_p;
|
|
return Tokenize(val, ';');
|
|
}
|
|
|
|
struct Slice {
|
|
long begin;
|
|
long length;
|
|
};
|
|
|
|
static inline void ReportJsonError(rapidjson::ParseResult ok, std::string &json, fs::path &sourcepath)
|
|
{
|
|
std::vector<Slice> linecoords { Slice { 0, 0 } };
|
|
for (int i = 0; i < json.length(); i++) {
|
|
if (json.at(i) == '\n') {
|
|
linecoords.back().length = i - linecoords.back().begin;
|
|
linecoords.emplace_back(Slice { i + 1, 0 });
|
|
}
|
|
}
|
|
linecoords.back().length = json.length() - linecoords.back().begin;
|
|
|
|
auto line = std::find_if(linecoords.begin(), linecoords.end(), [&ok](Slice slice) {
|
|
return slice.length + slice.begin >= ok.Offset();
|
|
});
|
|
auto lineidx = std::distance(linecoords.begin(), line);
|
|
auto linenum = lineidx + 1;
|
|
auto colnum = ok.Offset() - line->begin + 1;
|
|
|
|
std::cerr << "\033[1;37m" << fs::relative(sourcepath).generic_string() << ":" << linenum << ":" << colnum << ": ";
|
|
std::cerr << "\033[1;31merror: ";
|
|
std::cerr << "\033[1;37m" << rapidjson::GetParseError_En(ok.Code());
|
|
std::cerr << "\033[0m" << std::endl;
|
|
|
|
if (lineidx > 0) {
|
|
auto &prev = linecoords.at(lineidx - 1);
|
|
std::cerr << std::setw(5) << linenum - 1 << " | " << json.substr(prev.begin, prev.length) << std::endl;
|
|
}
|
|
|
|
std::cerr << std::setw(5) << linenum << " | " << "\033[0;31m" << json.substr(line->begin, line->length) << "\033[0m" << std::endl;
|
|
|
|
if (lineidx < linecoords.size() - 1) {
|
|
auto &next = linecoords.at(lineidx + 1);
|
|
std::cerr << std::setw(5) << linenum + 1 << " | " << json.substr(next.begin, next.length) << std::endl;
|
|
}
|
|
}
|
|
|
|
#endif // POKEPLATINUM_DATAGEN_H
|