msgenc: Allow leading whitespace, comments, and commands in charmap

This commit is contained in:
PikalaxALT 2021-08-17 11:23:15 -04:00
parent 15530ce0a6
commit f4ea6b23f0
9 changed files with 141 additions and 54 deletions

View File

@ -1,3 +1,9 @@
// Character mapping for Pokémon HeartGold and SoulSilver
// Version 2021.08.17
// Format is HEXCODE=<character> or HEXCODE={<command>}
// Comments are preceded by double forward slashes as in C99 and C++.
// Leading spaces and tabs are ignored; trailing spaces and tabs are not.
0000=\x0000
0001= 
0002=ぁ
@ -2874,3 +2880,21 @@
E000=\n
25BC=\r
25BD=\f
// Function codes
0100={STRVAR_1}
0300={STRVAR_3}
0400={STRVAR_4}
3400={STRVAR_34}
0200={YESNO}
0201={PAUSE}
0202={WAIT}
0203={CURSOR_X}
0204={CURSOR_Y}
0205={ALN_CENTER}
0206={ALN_RIGHT}
0207={UNK_207}
0208={UNK_208}
FF00={COLOR}
FF01={SIZE}
FF02={UNK_FF02}

View File

@ -415,8 +415,8 @@ MSGFILE_BIN := $(patsubst %.txt,%.bin,$(MSGFILE_TXT))
$(MSGDATA_MSG_DIR).narc: %.narc: $(MSGFILE_BIN)
$(KNARC) -d $* -p $@ -i
$(MSGFILE_BIN): %.bin: %.txt %.key
$(MSGENC) -e $^ charmap.txt $@
$(MSGFILE_BIN): %.bin: %.txt %.key charmap.txt
$(MSGENC) -e $^ $@
#%.narc:
# $(KNARC) -d $* -p $@

View File

@ -1,5 +1,15 @@
#include "MessagesConverter.h"
void MessagesConverter::CharmapRegisterCharacter(string &code, uint16_t value)
{
throw runtime_error("calling parent class method MessagesConverter::CharmapRegisterCharacter when child class method is required");
}
void MessagesConverter::CmdmapRegisterCommand(string &command, uint16_t value)
{
throw runtime_error("calling parent class method MessagesConverter::CmdmapRegisterCommand when child class method is required");
}
string MessagesConverter::ReadTextFile(string& filename) {
ifstream file(filename);
if (!file.good()) {
@ -20,21 +30,42 @@ void MessagesConverter::WriteTextFile(string& filename, string const& contents)
file.close();
}
void MessagesConverter::ReadCharmap(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);
void MessagesConverter::ReadCharmap() {
string raw = ReadTextFile(charmapfilename);
string line;
size_t pos, eqpos, last_pos = 0, lineno = 0;
for (
last_pos = 0;
last_pos != string::npos && (pos = raw.find_first_of("\r\n", last_pos)) != string::npos;
last_pos = raw.find_last_of("\r\n", pos + 1, 2) + 1, lineno++
) {
line = raw.substr(last_pos, pos - last_pos);
if (line.find("//") != string::npos) {
line = line.substr(0, line.find("//"));
}
if (line.find_first_not_of(" \t") == string::npos)
continue;
line = line.substr(
line.find_first_not_of(" \t")
);
eqpos = line.find('=');
if (eqpos == string::npos) {
stringstream s;
s << "charmap syntax error at " << (charmap.size() + 1);
s << "charmap syntax error at " << (lineno + 1);
throw(runtime_error(s.str()));
}
string value = raw.substr(last_pos, eqpos - last_pos);
string code = raw.substr(eqpos + 1, pos - eqpos - 1);
string value = line.substr(0, eqpos);
string code = line.substr(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;
if (code[0] == '{' && code[code.length() - 1] == '}') {
code = code.substr(1, code.length() - 2);
CmdmapRegisterCommand(code, value_i);
if (code.rfind("STRVAR_", 0) == 0)
strvar_codes.insert(value_i);
} else {
CharmapRegisterCharacter(code, value_i);
}
}
}

View File

@ -7,6 +7,7 @@
#include <map>
#include <sstream>
#include <vector>
#include <set>
using namespace std;
@ -52,38 +53,21 @@ static inline void DecryptU16String(u16string & message, int & i) {
}
class MessagesConverter{
virtual void CharmapRegisterCharacter(string& code, uint16_t value);
virtual void CmdmapRegisterCommand(string& command, uint16_t value);
protected:
ConvertMode mode;
string textfilename;
string keyfilename;
string charmapfilename;
string binfilename;
map<string, uint16_t> charmap;
MsgArcHeader header = {};
vector<MsgAlloc> alloc_table;
vector<string> vec_decoded;
vector<u16string> vec_encoded;
map<string, uint16_t> cmdmap = {
{"STRVAR_1", 0x0100},
{"STRVAR_3", 0x0300},
{"STRVAR_4", 0x0400},
{"STRVAR_34", 0x3400},
{"YESNO", 0x200},
{"PAUSE", 0x201},
{"WAIT", 0x202},
{"CURSOR_X", 0x203},
{"CURSOR_Y", 0x204},
{"ALN_CENTER", 0x205},
{"ALN_RIGHT", 0x206},
{"UNK_207", 0x207},
{"UNK_208", 0x208},
{"COLOR", 0xFF00},
{"SIZE", 0xFF01},
{"UNK_FF02", 0xFF02},
};
vector<uint16_t> strvar_codes = {0x0100, 0x0300, 0x0400, 0x3400};
set<uint16_t> strvar_codes;
template <typename key_type, typename mapped_type> void CreateInverseMap(map<key_type, mapped_type>const& _in, map<mapped_type, key_type>& _out) {
for (auto _pair : _in) {
@ -92,16 +76,15 @@ protected:
}
static string ReadTextFile(string& filename);
static void WriteTextFile(string& filename, string const & contents);
void ReadCharmap(string& charmapfname);
public:
MessagesConverter(string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) :
MessagesConverter(ConvertMode _mode, string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) :
mode(_mode),
textfilename(_textfilename),
keyfilename(_keyfilename),
charmapfilename(_charmapfilename),
binfilename(_binfilename)
{
ReadCharmap(charmapfilename);
}
{}
void ReadCharmap();
virtual void ReadInput() = 0;
virtual void Convert() = 0;
virtual void WriteOutput() = 0;

View File

@ -1,6 +1,16 @@
#include <algorithm>
#include "MessagesDecoder.h"
void MessagesDecoder::CmdmapRegisterCommand(string &command, uint16_t value)
{
cmdmap[value] = command;
}
void MessagesDecoder::CharmapRegisterCharacter(string &code, uint16_t value)
{
charmap[value] = code;
}
static string ConvertIntToHexStringN(unsigned value, StrConvMode mode, int n) {
string dest;
bool printing_zeroes = mode == STR_CONV_MODE_LEADING_ZEROS;
@ -79,8 +89,8 @@ string MessagesDecoder::DecodeMessage(u16string &message, int &i) {
uint16_t code = message[j];
debug_printf("%04X ", code);
if (charmap_dec.find(code) != charmap_dec.end()) {
decoded += charmap_dec[code];
if (charmap.find(code) != charmap.end()) {
decoded += charmap[code];
}
else if (code == (is_trname ? 0x01FF : 0xFFFF)) {
break;
@ -96,8 +106,8 @@ string MessagesDecoder::DecodeMessage(u16string &message, int &i) {
is_strvar = true;
command = "STRVAR_" + ConvertIntToHexStringN((code >> 8), STR_CONV_MODE_LEFT_ALIGN, 2);
}
else if (cmdmap_dec.find(code) != cmdmap_dec.end()) {
command = cmdmap_dec[code];
else if (cmdmap.find(code) != cmdmap.end()) {
command = cmdmap[code];
} else {
throw runtime_error("Invalid control code in " + binfilename + ": " + ConvertIntToHexStringN(code, STR_CONV_MODE_LEADING_ZEROS, 4) + " at line " + to_string(i) + ":" + to_string(j));
}

View File

@ -12,19 +12,18 @@ enum StrConvMode {
class MessagesDecoder : public MessagesConverter
{
map <uint16_t, string> cmdmap_dec;
map <uint16_t, string> charmap_dec;
map <uint16_t, string> cmdmap;
map <uint16_t, string> charmap;
void ReadMessagesFromBin(string& filename);
void WriteMessagesToText(string& filename);
static void WriteBinaryFile(string& filename, void* data, streamsize size);
static u16string DecodeTrainerNameMessage(u16string const &message);
string DecodeMessage(u16string& message, int& i);
void CharmapRegisterCharacter(string& code, uint16_t value) override;
void CmdmapRegisterCommand(string& command, uint16_t value) override;
public:
MessagesDecoder(string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) : MessagesConverter(_textfilename, _keyfilename, _charmapfilename, _binfilename) {
CreateInverseMap(charmap, charmap_dec);
CreateInverseMap(cmdmap, cmdmap_dec);
}
MessagesDecoder(string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) : MessagesConverter(CONV_DECODE, _textfilename, _keyfilename, _charmapfilename, _binfilename) {}
void ReadInput() override;
void Convert() override;
void WriteOutput() override;

View File

@ -1,5 +1,15 @@
#include "MessagesEncoder.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;
@ -62,7 +72,7 @@ u16string MessagesEncoder::EncodeMessage(const string & message, int & i) {
encoded += (char16_t)(command_i);
debug_printf("%04X ", command_i);
encoded += (char16_t)(args.size());
debug_printf("%04X ", args.size());
debug_printf("%04X ", (unsigned)args.size());
for (auto num_i : args) {
encoded += (char16_t)(num_i);
debug_printf("%04X ", num_i);
@ -81,9 +91,10 @@ u16string MessagesEncoder::EncodeMessage(const string & message, int & i) {
string substr;
for (k = 0; k < message.size() - j; k++) {
substr = message.substr(j, k + 1);
code = charmap[substr];
if (code != 0 || substr == "\\x0000")
try {
code = charmap.at(substr);
break;
} catch (out_of_range) { /* silently discard */}
}
if (code == 0 && substr != "\\x0000") {
stringstream ss;

View File

@ -6,12 +6,17 @@
class MessagesEncoder : public MessagesConverter
{
map <string, uint16_t> cmdmap;
map <string, uint16_t> charmap;
void ReadKeyFile(string& keyfname);
void ReadMessagesFromText(string& filename);
void WriteMessagesToBin(string& filename);
u16string EncodeMessage(const string& message, int & i);
void CharmapRegisterCharacter(string& code, uint16_t value) override;
void CmdmapRegisterCommand(string& command, uint16_t value) override;
public:
MessagesEncoder(string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) : MessagesConverter(_textfilename, _keyfilename, _charmapfilename, _binfilename) {}
MessagesEncoder(string &_textfilename, string &_keyfilename, string &_charmapfilename, string &_binfilename) : MessagesConverter(CONV_ENCODE, _textfilename, _keyfilename, _charmapfilename, _binfilename) {}
void ReadInput() override;
void Convert() override;
void WriteOutput() override;

View File

@ -5,21 +5,44 @@
* msgenc TXTFILE KEYFILE CHARMAP OUTFILE
*/
#include <iostream>
#include "MessagesDecoder.h"
#include "MessagesEncoder.h"
static const string version = "2021.08.17";
int main(int argc, char ** argv) {
// msgenc TXTFILE KEYFILE CHARMAP OUTFILE
if (argc < 6)
// msgenc -d|-e TXTFILE KEYFILE CHARMAP OUTFILE
if (argc < 2)
throw invalid_argument("usage: msgenc -d|-e TXTFILE KEYFILE CHARMAP BINFILE");
string mode_s(argv[1]);
ConvertMode mode;
if (mode_s == "-d")
mode = CONV_DECODE;
else if (mode_s == "-e")
mode = CONV_ENCODE;
else if (mode_s == "-h") {
cout << argv[0] << " v" << version << endl;
cout << "Usage: " << argv[0] << " [-h] [-v] -d|-e TEXTFILE KEYFILE CHARMAP BINFILE" << endl;
cout << endl;
cout << "-d Decode from binary to text, also save the key" << endl;
cout << "-e Encode from text to binary using the provided key" << endl;
cout << "TEXTFILE Path to the plaintext file to encode (-e) or write (-d)." << endl;
cout << "KEYFILE Path to a binary file that encodes the 16-bit encryption key for this message bank." << endl;
cout << "CHARMAP Path to a text file with a character mapping, for example pokeheartgold/charmap.txt." << endl;
cout << "BINFILE Path to the encoded binary file to decode (-d) or write (-e)." << endl;
cout << "-v Print the program version and exit." << endl;
cout << "-h Print this message and exit." << endl;
return 0;
} else if (mode_s == "-v") {
cout << argv[0] << " v" << version << endl;
return 0;
}
else
throw invalid_argument(string("invalid mode: ") + mode_s);
if (argc < 6)
throw invalid_argument("usage: msgenc -d|-e TXTFILE KEYFILE CHARMAP BINFILE");
string textfile(argv[2]);
string keyfile(argv[3]);
string charmapfile(argv[4]);
@ -31,6 +54,7 @@ int main(int argc, char ** argv) {
else
converter = new MessagesEncoder(textfile, keyfile, charmapfile, binfile);
converter->ReadInput();
converter->ReadCharmap();
converter->Convert();
converter->WriteOutput();
delete converter;