mirror of
https://github.com/pret/pokeheartgold.git
synced 2024-12-14 00:20:22 +00:00
180 lines
6.1 KiB
C++
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);
|
|
}
|