diff options
author | Rémi Calixte <remicalixte.rmc@gmail.com> | 2021-08-31 08:30:47 +0200 |
---|---|---|
committer | Rémi Calixte <remicalixte.rmc@gmail.com> | 2021-08-31 08:30:47 +0200 |
commit | 12bafff5c0ca6bfdcca3553a0717c80f21e27182 (patch) | |
tree | 7c07954a140c879b8c71e33a71c603f27076ab56 /tools/msgenc/msgenc.cpp | |
parent | 4e4192cd7007d16cefe00facbc7b721353c94f60 (diff) | |
parent | 05ded46ab7f556956a2eee2411a8d2968b7e8ad6 (diff) |
Merge branch 'master' into unk_02006D98
Diffstat (limited to 'tools/msgenc/msgenc.cpp')
-rw-r--r-- | tools/msgenc/msgenc.cpp | 319 |
1 files changed, 104 insertions, 215 deletions
diff --git a/tools/msgenc/msgenc.cpp b/tools/msgenc/msgenc.cpp index 9ec21ed2..5caaf31c 100644 --- a/tools/msgenc/msgenc.cpp +++ b/tools/msgenc/msgenc.cpp @@ -6,231 +6,120 @@ */ #include <iostream> -#include <string> -#include <sstream> -#include <fstream> -#include <map> -#include <vector> -#include <algorithm> - -struct MsgArcHeader -{ - uint16_t count; - uint16_t key; -}; - -struct MsgAlloc -{ - uint32_t offset; - uint32_t length; -}; - -using namespace std; - -string ReadTextFile(string filename) { - fstream file(filename); - if (!file.good()) { - stringstream s; - s << "unable to open file \"" << filename << "\" for reading"; - throw runtime_error(s.str()); - } - stringstream ss; - ss << file.rdbuf(); - file.close(); - return ss.str(); +#include "MessagesDecoder.h" +#include "MessagesEncoder.h" + +static const char* progname = "msgenc"; +static const char* version = "2021.08.27"; + + +static inline void usage() { + cout << progname << " v" << version << endl; + cout << "Usage: " << progname << " [-h] [-v] -d|-e [-k KEY] -c CHARMAP INFILE OUTFILE" << endl; + cout << endl; + cout << "INFILE Required: Path to the input file to convert (-e: plaintext; -d: binary)." << endl; + cout << "OUTFILE Required: Path to the output file (-e: binary; -d: plaintext)." << endl; + cout << "-c CHARMAP Required: Path to a text file with a character mapping, for example pokeheartgold/charmap.txt." << endl; + cout << "-d Decode from binary to text, also print the key" << endl; + cout << "-e Encode from text to binary using the provided key" << endl; + cout << "-k KEY The 16-bit encryption key for this message bank. Default: computes it from the binary file name" << endl; + cout << "-v Print the program version and exit." << endl; + cout << "-h Print this message and exit." << endl; + cout << "-D DUMPNAME Dump the intermediate binary (after decryption or before encryption)." << endl; } -static map<string, uint16_t> charmap; - -void read_charmap(string filename) { - string raw = ReadTextFile(filename); - size_t pos, eqpos, last_pos = 0; - while (last_pos != string::npos && (pos = raw.find_first_of("\r\n", last_pos)) != string::npos) { - eqpos = raw.find('=', last_pos); - if (eqpos == string::npos) - { - stringstream s; - s << "charmap syntax error at " << (charmap.size() + 1); - throw(runtime_error(s.str())); +struct Options { + ConvertMode mode = CONV_INVALID; + int key = 0; + vector<string> posargs; + string failReason; + string charmap; + bool printUsage = false; + bool printVersion = false; + string dumpBinary; + Options(int argc, char ** argv) { + for (int i = 1; i < argc; i++) { + string arg(argv[i]); + if (arg == "-d") { + mode = CONV_DECODE; + } else if (arg == "-e") { + mode = CONV_ENCODE; + } else if (arg == "-h") { + printUsage = true; + return; + } else if (arg == "-v") { + printVersion = true; + return; + } else if (arg == "-k") { + key = stoi(argv[++i], nullptr, 0); + // If the key is 0, ensure that it is not overridden by the CRC. + key &= 0xFFFF; + key |= 0x10000; + } else if (arg == "-c") { + charmap = argv[++i]; + } else if (arg == "-D") { + dumpBinary = argv[++i]; + } else if (arg[0] != '-') { + posargs.push_back(arg); + } else { + failReason = "unrecognized option: " + arg; + break; + } + } + if (posargs.size() < 2) { + failReason = "missing required positional argument: " + (string[]){"INFILE", "OUTFILE"}[posargs.size()]; + } + if (mode == CONV_INVALID) { + failReason = "missing mode flag: -d or -e is required"; + } + if (charmap.empty()) { + failReason = "missing charmap file: -c CHARMAP is required"; } - string value = raw.substr(last_pos, eqpos - last_pos); - string code = raw.substr(eqpos + 1, pos - eqpos - 1); - uint16_t value_i = stoi(value, nullptr, 16); - charmap[code] = value_i; - last_pos = raw.find_last_of("\r\n", pos + 1, 2) + 1; - } -} - -static MsgArcHeader header; -vector<MsgAlloc> alloc_table; -static vector<string> files; -static vector<u16string> outfiles; - -void read_key(string keyfname) { - fstream keyfile(keyfname, ios_base::in | ios_base::binary); - if (!keyfile.good()) { - stringstream s; - s << "unable to open file \"" << keyfname << "\" for reading"; - throw runtime_error(s.str()); } - keyfile.read((char *)&header.key, 2); -} - -void read_msgs(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"); - files.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 = files.size(); -} - -uint16_t enc_short(uint16_t value, uint16_t & seed) { - value ^= seed; - seed += 18749; - return value; -} - -static map<string, uint16_t> cmdmap = { - {"STRVAR", 0x0100}, - {"YESNO", 0x200}, - {"PAUSE", 0x201}, - {"WAIT", 0x202}, - {"CURSOR_X", 0x203}, - {"CURSOR_Y", 0x204}, - {"COLOR", 0xFF00}, - {"SIZE", 0xFF01} }; -void encode_messages() { - int i = 1; - for (auto message : files) { - u16string encoded; - uint16_t seed = i * 596947; - 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 += enc_short(0xFFFE, seed); - vector<uint16_t> args; - 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 == "STRVAR") { - command_i |= args[0]; - args.erase(args.begin()); - } - encoded += enc_short(command_i, seed); - encoded += enc_short(args.size(), seed); - for (auto num_i : args) { - encoded += enc_short(num_i, seed); - } - } else if (command == "TRNAME") { - is_trname = true; - encoded += enc_short(0xF100, seed); - } else { - encoded += enc_short(stoi(enclosed, nullptr, 16), seed); - } - } else { - uint16_t code = 0; - size_t k; - string substr; - for (k = 0; k < message.size() - j; k++) { - substr = message.substr(j, k + 1); - code = charmap[substr]; - if (code != 0 || substr == "\\x0000") - break; - } - if (code == 0 && substr != "\\x0000") { - stringstream ss; - ss << "unrecognized character: file " << i << " pos " << (j + 1); - throw runtime_error(ss.str()); - } - 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 += enc_short(trnamebuf & 0x7FFF, seed); - trnamebuf >>= 15; - } - } else { - encoded += enc_short(code, seed); - } - j += k; +int main(int argc, char ** argv) { + try { + Options options(argc, argv); + if (options.printUsage || !options.failReason.empty()) { + usage(); + if (!options.failReason.empty()) { + throw invalid_argument(options.failReason); } + return 0; + } else if (options.printVersion) { + cout << progname << " v" << version << endl; + return 0; } - if (is_trname && bit > 1) { - trnamebuf |= 0xFFFF << bit; - encoded += enc_short(trnamebuf & 0x7FFF, seed); + + MessagesConverter *converter; + if (options.mode == CONV_DECODE) + { + converter = new MessagesDecoder(options.posargs[1], options.key, options.charmap, options.posargs[0]); } - encoded += enc_short(0xFFFF, seed); - MsgAlloc alloc {0, 0}; - if (i > 1) { - alloc.offset = alloc_table[i - 2].offset + alloc_table[i - 2].length * 2; - } else { - alloc.offset = sizeof(header) + sizeof(MsgAlloc) * header.count; + else + { + converter = new MessagesEncoder(options.posargs[0], options.key, options.charmap, options.posargs[1]); } - alloc.length = encoded.size(); - outfiles.push_back(encoded); - alloc_table.push_back(alloc); - i++; - } - i = 1; - for (auto & x : alloc_table) { - uint32_t alloc_key = (765 * i * header.key) & 0xFFFF; - alloc_key |= alloc_key << 16; - x.offset ^= alloc_key; - x.length ^= alloc_key; - i++; - } -} - -void write_messages(string filename) { - ofstream outfile(filename, ios_base::binary); - outfile.write((char *)&header, sizeof(header)); - outfile.write((char *)alloc_table.data(), sizeof(MsgAlloc) * alloc_table.size()); - for (auto m : outfiles) { - outfile.write((char *)m.c_str(), m.size() * 2); + converter->ReadInput(); + converter->ReadCharmap(); + converter->Convert(); + if (!options.dumpBinary.empty()) + converter->WriteBinaryDecoded(options.dumpBinary); + converter->WriteOutput(); + if (options.mode == CONV_DECODE) { + cout << "Key: " << hex << converter->GetKey() << endl; + } + delete converter; + } catch (invalid_argument& ia) { + cerr << "Invalid Argument: " << ia.what() << endl; + return 1; + } catch (ios_base::failure& iof) { + cerr << "IO Failure: " << iof.what() << endl; + return 1; + } catch (runtime_error& exc) { + cerr << "Runtime Error: " << exc.what() << endl; + return 1; } - outfile.close(); -} - -int main(int argc, char ** argv) { - // msgenc TXTFILE KEYFILE CHARMAP OUTFILE - if (argc < 5) - throw invalid_argument("usage: msgenc TXTFILE KEYFILE CHARMAP OUTFILE"); - read_msgs(argv[1]); - read_key(argv[2]); - read_charmap(argv[3]); - encode_messages(); - write_messages(argv[4]); return 0; } |