mirror of
https://github.com/pret/pokeplatinum.git
synced 2026-08-13 04:28:05 -05:00
232 lines
7.1 KiB
C++
232 lines
7.1 KiB
C++
#include "Json.h"
|
|
|
|
#include <cctype>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <ios>
|
|
#include <regex>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "dataproc.h"
|
|
|
|
#include "MessagesConverter.h"
|
|
|
|
string ReadWholeFile(const string_view& fpath) {
|
|
constexpr size_t read_size = 4096;
|
|
|
|
ifstream stream(fpath.data());
|
|
stream.exceptions(ios_base::badbit);
|
|
if (!stream) {
|
|
throw ios_base::failure("file does not exist");
|
|
}
|
|
|
|
string out;
|
|
string buf(read_size, '\0');
|
|
while (stream.read(&buf[0], read_size)) {
|
|
out.append(buf, 0, stream.gcount());
|
|
}
|
|
|
|
out.append(buf, 0, stream.gcount());
|
|
return out;
|
|
}
|
|
|
|
// Read header constants from the supplied file.
|
|
// Constants are expected to be of the format `#define +{name} +{int value}`,
|
|
// and the integer value should be sequential starting from 0.
|
|
void Json::ReadHeader(const string &_filename) {
|
|
string hstring = ReadWholeFile(_filename);
|
|
regex pattern(R"(#define\s+(\w+)\s+([0-9]+))");
|
|
smatch results;
|
|
id_strings.clear();
|
|
while (regex_search(hstring, results, pattern)) {
|
|
id_strings.emplace_back(results[1]);
|
|
hstring = results.suffix().str();
|
|
}
|
|
}
|
|
|
|
// Write header constants to the supplied file.
|
|
// Print constants in the format `#define {name} {int value}` such that the
|
|
// integer values are sequentially ordered starting from 0.
|
|
void Json::WriteHeader(const string &_filename) {
|
|
ofstream hstrm(_filename);
|
|
string guard(_filename);
|
|
for (auto &c : guard) {
|
|
switch (c) {
|
|
case '/':
|
|
case '.':
|
|
case '-':
|
|
c = '_';
|
|
break;
|
|
|
|
default:
|
|
c = toupper(c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
hstrm << "/***************************************************\n";
|
|
hstrm << " * WARNING: This file was autogenerated by msgenc. *\n";
|
|
hstrm << " * DO NOT MODIFY *\n";
|
|
hstrm << " ***************************************************/\n";
|
|
hstrm << "\n";
|
|
hstrm << "#ifndef MSGENC_" << guard << "\n";
|
|
hstrm << "#define MSGENC_" << guard << "\n";
|
|
hstrm << "\n";
|
|
for (size_t i = 0; i < id_strings.size(); i++) {
|
|
vector<string> message_lines = SplitMessage(messages[i]);
|
|
for (const auto& line : message_lines) {
|
|
hstrm << "// " << line << "\n";
|
|
}
|
|
hstrm << "#define " << id_strings[i] << " " << i << "\n";
|
|
}
|
|
|
|
hstrm << "#define " << guard.substr(4, guard.size() - 6) << "_ENTRY_COUNT" << " " << id_strings.size() << "\n";
|
|
|
|
hstrm << "\n";
|
|
hstrm << "#endif // MSGENC_" << guard << "\n";
|
|
hstrm.flush();
|
|
}
|
|
|
|
// Read messages from JSON into memory to be converted
|
|
int Json::FromFile(MessagesConverter &converter) {
|
|
int key = JSON_KEY_NOT_DEFINED;
|
|
|
|
if (dp_load(&this->doc, filename.c_str()) == 0) {
|
|
key = dp_u16(dp_get(&this->doc, ".key"));
|
|
key &= 0xFFFF;
|
|
key |= 0x10000;
|
|
|
|
datanode_t messages = dp_get(&this->doc, ".messages");
|
|
std::size_t numMessages = dp_arrlen(messages);
|
|
for (std::size_t i = 0; i < numMessages; i++) {
|
|
string message;
|
|
datanode_t elem = dp_arrelem(messages, i);
|
|
if (dp_hasmemb(elem, "en_US")) {
|
|
datanode_t content = dp_objmemb(elem, "en_US");
|
|
std::size_t numLines = 0;
|
|
switch (content.type) {
|
|
case DATAPROC_T_STRING:
|
|
message.append(dp_string(content));
|
|
break;
|
|
|
|
case DATAPROC_T_ARRAY:
|
|
numLines = dp_arrlen(content);
|
|
for (std::size_t j = 0; j < numLines; j++) {
|
|
const char *line = dp_string(dp_arrelem(content, j));
|
|
if (line) message.append(line);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
dp_error(&content, "expected an array or string");
|
|
continue;
|
|
}
|
|
}
|
|
else if (dp_hasmemb(elem, "garbage")) {
|
|
message.resize(dp_int(dp_objmemb(elem, "garbage")), ' ');
|
|
}
|
|
else {
|
|
dp_error(&elem, "expected a definition for one of 'garbage' or 'en_US'");
|
|
continue;
|
|
}
|
|
|
|
converter.GetDecodedMessages().emplace_back(message); // emplace a copy
|
|
id_strings.emplace_back(dp_string(dp_objmemb(elem, "id")));
|
|
this->messages.push_back(message);
|
|
IncRowNoBuf();
|
|
}
|
|
}
|
|
|
|
if (dp_report(&this->doc) == DIAG_ERROR) {
|
|
throw std::runtime_error("JSON parse error");
|
|
}
|
|
|
|
if (!converter.GetHeaderFilename().empty()) {
|
|
WriteHeader(converter.GetHeaderFilename());
|
|
}
|
|
|
|
dp_free(&this->doc);
|
|
return key;
|
|
}
|
|
|
|
void Json::ToFile(MessagesConverter &converter) {
|
|
if (!converter.GetHeaderFilename().empty()) {
|
|
ReadHeader(converter.GetHeaderFilename());
|
|
}
|
|
|
|
auto it = id_strings.cbegin();
|
|
this->doc = dp_new();
|
|
datanode_t root = dp_set_obj(&this->doc);
|
|
dp_obj_putint(&root, "key", converter.GetKey());
|
|
|
|
datanode_t messages = dp_obj_putarray(&root, "messages");
|
|
|
|
char keybuf[256];
|
|
string prefix = filename.substr(filename.find_last_of('/') + 1);
|
|
prefix = prefix.substr(0, prefix.find_first_of('.'));
|
|
|
|
for (const auto &message : converter.GetDecodedMessages()) {
|
|
datanode_t entry = dp_arr_appobject(&messages);
|
|
|
|
if (it != id_strings.cend()) {
|
|
dp_obj_putstring(&entry, "id", it->c_str());
|
|
it++;
|
|
} else {
|
|
std::snprintf(keybuf, 256, "%s_%s", prefix.c_str(), row_no_buf);
|
|
dp_obj_putstring(&entry, "id", keybuf);
|
|
}
|
|
|
|
if (message.find_first_not_of(' ') == string::npos) {
|
|
dp_obj_putuint(&entry, "garbage", message.size());
|
|
} else {
|
|
vector<string> message_lines = SplitMessage(message, true);
|
|
if (message_lines.size() == 1) {
|
|
dp_obj_putstring(&entry, "en_US", message.c_str());
|
|
} else {
|
|
datanode_t lines = dp_obj_putarray(&entry, "en_US");
|
|
for (const auto& line : message_lines) {
|
|
dp_arr_appstring(&lines, line.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
IncRowNoBuf();
|
|
}
|
|
|
|
const char *output = dp_dump(&this->doc);
|
|
ofstream jstrm(filename);
|
|
jstrm << output << endl;
|
|
}
|
|
|
|
vector<string> Json::SplitMessage(const string &message, bool preserve) {
|
|
vector<string> v;
|
|
|
|
size_t start = 0, i = 0;
|
|
for (; i < message.size(); i++) {
|
|
if (message[i] == '\r' || message[i] == '\n' || message[i] == '\f') {
|
|
if (preserve) {
|
|
v.push_back(message.substr(start, i - start + 1));
|
|
} else {
|
|
v.push_back(message.substr(start, i - start));
|
|
}
|
|
|
|
start = i + 1;
|
|
}
|
|
}
|
|
|
|
if (start < message.size()) {
|
|
if (preserve) {
|
|
v.push_back(message.substr(start, i - start + 1));
|
|
} else {
|
|
v.push_back(message.substr(start, i - start));
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|