pokeheartgold/tools/msgenc/MessagesEncoder.cpp
2021-12-21 10:51:53 -05:00

180 lines
6.1 KiB
C++

#include "MessagesEncoder.h"
#include "Gmm.h"
void MessagesEncoder::CmdmapRegisterCommand(string &command, uint16_t value)
{
cmdmap[command] = value;
}
void MessagesEncoder::CharmapRegisterCharacter(string &code, uint16_t value)
{
charmap[code] = value;
}
void MessagesEncoder::ReadMessagesFromText(string& fname) {
string text = ReadTextFile(fname);
size_t pos = 0;
do {
text = text.substr(pos);
if (text.empty())
break;
pos = text.find_first_of("\r\n");
vec_decoded.push_back(text.substr(0, pos));
pos = text.find_last_of("\r\n", pos + 1, 2);
if (pos == string::npos)
break;
pos++;
} while (pos != string::npos);
header.count = vec_decoded.size();
debug_printf("%d lines\n", header.count);
}
void MessagesEncoder::ReadMessagesFromGMM(string& filename) {
GMM(filename, std::ios::in).FromFile(*this);
header.count = vec_decoded.size();
debug_printf("%d lines\n", header.count);
}
u16string MessagesEncoder::EncodeMessage(const string & message, int & i) {
u16string encoded;
bool is_trname = false;
uint32_t trnamebuf = 0;
int bit = 0;
for (size_t j = 0; j < message.size(); j++) {
if (message[j] == '{') {
size_t k = message.find('}', j);
string enclosed = message.substr(j + 1, k - j - 1);
j = k;
size_t pos = enclosed.find(' ');
string command = enclosed.substr(0, pos);
enclosed = enclosed.substr(pos + 1);
if (cmdmap.find(command) != cmdmap.end()) {
uint16_t command_i = cmdmap[command];
encoded += (char16_t)(0xFFFE);
debug_printf("%04X ", 0xFFFE);
vector<uint16_t> args;
if (pos != string::npos) {
do {
k = enclosed.find(',');
string num = enclosed.substr(0, k);
uint16_t num_i = stoi(num);
args.push_back(num_i);
enclosed = enclosed.substr(k + 1);
} while (k != string::npos);
if (command.rfind("STRVAR_", 0) == 0) {
command_i |= args[0];
args.erase(args.begin());
}
}
encoded += (char16_t)(command_i);
debug_printf("%04X ", command_i);
encoded += (char16_t)(args.size());
debug_printf("%04X ", (unsigned)args.size());
for (auto num_i : args) {
encoded += (char16_t)(num_i);
debug_printf("%04X ", num_i);
}
} else if (command == "TRNAME") {
is_trname = true;
encoded += (char16_t)(0xF100);
debug_printf("%04X ", 0xF100);
} else {
encoded += (char16_t)(stoi(enclosed, nullptr, 16));
debug_printf("%04X ", stoi(enclosed, nullptr, 16));
}
} else {
uint16_t code = 0;
size_t k;
string substr;
for (k = 0; k < message.size() - j; k++) {
substr = message.substr(j, k + 1);
try {
code = charmap.at(substr);
break;
} catch (out_of_range& oor) { /* silently discard */}
}
if (code == 0 && substr != "\\x0000") {
stringstream ss;
ss << "unrecognized character in " << textfilename << ": line " << i << " pos " << (j + 1) << " value " << substr;
throw runtime_error(ss.str());
}
debug_printf("%04X ", code);
if (is_trname) {
if (code & ~0x1FF) {
stringstream ss;
ss << "invalid character for bitpacked string: " << substr;
throw runtime_error(ss.str());
}
trnamebuf |= code << bit;
bit += 9;
if (bit >= 15) {
bit -= 15;
encoded += (char16_t)(trnamebuf & 0x7FFF);
trnamebuf >>= 15;
}
} else {
encoded += (char16_t)(code);
}
j += k;
}
}
if (is_trname && bit > 1) {
trnamebuf |= 0xFFFF << bit;
encoded += (char16_t)(trnamebuf & 0x7FFF);
debug_printf("%04X ", trnamebuf & 0x7FFF);
}
encoded += (char16_t)(0xFFFF);
debug_printf("%04X ", 0xFFFF);
return encoded;
}
void MessagesEncoder::WriteMessagesToBin(string& filename) {
ofstream outfile(filename, ios_base::binary);
if (!outfile.good()) {
throw ofstream::failure("Unable to open file \"" + filename + "\" for writing");
}
outfile.write((char *)&header, sizeof(header));
for (int i = 1; i <= (int)alloc_table.size(); i++) {
alloc_table[i - 1].encrypt(header.key, i);
EncryptU16String(vec_encoded[i - 1], i);
}
outfile.write((char *)alloc_table.data(), (streamsize)(sizeof(MsgAlloc) * alloc_table.size()));
for (const u16string & m : vec_encoded) {
outfile.write((char *)m.c_str(), (streamsize)(m.size() * 2));
}
outfile.close();
}
// Public virtual functions
void MessagesEncoder::ReadInput()
{
switch (text_format) {
case PlainText:
ReadMessagesFromText(textfilename);
break;
case GamefreakGMM:
ReadMessagesFromGMM(textfilename);
break;
}
}
void MessagesEncoder::Convert() {
MsgAlloc alloc {(uint32_t)(sizeof(header) + sizeof(MsgAlloc) * header.count), 0};
int i = 1;
for (const auto& message : vec_decoded) {
u16string encoded = EncodeMessage(message, i);
alloc.length = encoded.size();
vec_encoded.push_back(encoded);
debug_printf("msg %d: at 0x%08X, count %d\n", i, alloc.offset, alloc.length);
alloc_table.push_back(alloc);
alloc.offset += alloc.length * 2;
i++;
}
}
void MessagesEncoder::WriteOutput() {
WriteMessagesToBin(binfilename);
}