summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/bin2obj/.gitignore1
-rw-r--r--tools/bin2obj/Makefile13
-rw-r--r--tools/bin2obj/bin2obj.cpp295
-rw-r--r--tools/msgenc/.gitignore1
-rw-r--r--tools/msgenc/Makefile33
-rw-r--r--tools/msgenc/MessagesConverter.cpp97
-rw-r--r--tools/msgenc/MessagesConverter.h103
-rw-r--r--tools/msgenc/MessagesDecoder.cpp185
-rw-r--r--tools/msgenc/MessagesDecoder.h36
-rw-r--r--tools/msgenc/MessagesEncoder.cpp165
-rw-r--r--tools/msgenc/MessagesEncoder.h26
-rw-r--r--tools/msgenc/msgenc.cpp319
-rw-r--r--tools/msgenc/util.h17
-rw-r--r--tools/nitrobanner/banner.h9
-rw-r--r--tools/nitrogfx/Makefile2
-rw-r--r--tools/nitrogfx/gfx.c15
-rw-r--r--tools/nitrogfx/global.h6
-rw-r--r--tools/nitrogfx/huff.c28
-rw-r--r--tools/nitrogfx/main.c6
19 files changed, 1110 insertions, 247 deletions
diff --git a/tools/bin2obj/.gitignore b/tools/bin2obj/.gitignore
new file mode 100644
index 00000000..2fa716c9
--- /dev/null
+++ b/tools/bin2obj/.gitignore
@@ -0,0 +1 @@
+bin2obj
diff --git a/tools/bin2obj/Makefile b/tools/bin2obj/Makefile
new file mode 100644
index 00000000..760a66e2
--- /dev/null
+++ b/tools/bin2obj/Makefile
@@ -0,0 +1,13 @@
+CXX := g++
+CXXFLAGS := -O2 -g -std=c++17
+
+.PHONY: all clean
+
+all: bin2obj
+ @:
+
+clean:
+ $(RM) bin2obj bin2obj.exe
+
+bin2obj: bin2obj.cpp
+ $(CXX) $(CXXFLAGS) -o $@ $<
diff --git a/tools/bin2obj/bin2obj.cpp b/tools/bin2obj/bin2obj.cpp
new file mode 100644
index 00000000..6963438d
--- /dev/null
+++ b/tools/bin2obj/bin2obj.cpp
@@ -0,0 +1,295 @@
+#include <iostream>
+#include <string>
+#include <vector>
+#include <fstream>
+extern "C" {
+#include <elf.h>
+}
+
+enum Proc : unsigned char {
+ PROC_ARM = EM_ARM,
+ PROC_PPC = EM_PPC,
+};
+
+enum Endian : unsigned char {
+ END_LITTLE = ELFDATA2LSB,
+ END_BIG = ELFDATA2MSB,
+};
+
+enum Shndx {
+ SHNDX_SHSTRTAB = 1,
+ SHNDX_SYMTAB,
+ SHNDX_STRTAB,
+ SHNDX_PROGBITS,
+};
+
+static inline void usage() {
+ std::cout << "Usage: bin2obj [-b|--begin SYMBOL_BEGIN]" << std::endl;
+ std::cout << " [-e|--end SYMBOL_END]" << std::endl;
+ std::cout << " [-C|--compatible]" << std::endl;
+ std::cout << " [-a|--align ALIGNMENT]" << std::endl;
+ std::cout << " [-r|--readonly]" << std::endl;
+ std::cout << " [-s|--section SECTION]" << std::endl;
+ std::cout << " [-m|--machine [arm|ppc]]" << std::endl;
+ std::cout << " [-B|--big-endian]" << std::endl;
+ std::cout << " [-L|--little-endian] BINARY_FILE OBJECT_FILE" << std::endl;
+ std::cout << std::endl;
+ std::cout << " -b or --begin Set symbol name for top of binary. (*)" << std::endl;
+ std::cout << " -e or --end Set symbol name for bottom of binary. (*)" << std::endl;
+ std::cout << " -C or --compatible Use compatible symbols with BinToElf.exe." << std::endl;
+ std::cout << " Same as \"-b _binary_%f -e _binary_%f_end\"." << std::endl;
+ std::cout << " -a or --align Set binary data alignment in bytes." << std::endl;
+ std::cout << " -r or --readonly Handle binary data as readonly." << std::endl;
+ std::cout << " -s or --section Set section name." << std::endl;
+ std::cout << " -m or --machine [arm|ppc] Machine arch [arm|ppc].(default=arm)" << std::endl;
+ std::cout << " -B or --big-endian Output in big endian format." << std::endl;
+ std::cout << " -L or --little-endian Output in little endian format." << std::endl;
+ std::cout << std::endl;
+ std::cout << " (*) special % rules for symbols (ex. binary_file = \"filename.dat\")" << std::endl;
+ std::cout << " %f,%t replaced to file name of binary (%f = \"filename.dat\")" << std::endl;
+ std::cout << " %b replaced to base name of binary (%b = \"filename\")" << std::endl;
+ std::cout << " %e replaced to extension of binary (%e = \"dat\")" << std::endl;
+}
+
+std::string& ntrsprintf(std::string& templ, std::string binfname) {
+ size_t pos;
+ if ((pos = binfname.rfind('/')) != std::string::npos) {
+ binfname = binfname.substr(pos + 1);
+ }
+ size_t extpos = binfname.rfind('.');
+ if (extpos == std::string::npos) extpos = binfname.length() - 1;
+ if ((pos = templ.find("%f")) != std::string::npos || (pos = templ.find("%t")) != std::string::npos) {
+ templ = templ.substr(0, pos) + binfname + templ.substr(pos + 2);
+ }
+ if ((pos = templ.find("%b")) != std::string::npos) {
+ templ = templ.substr(0, pos) + binfname.substr(0, extpos) + templ.substr(pos + 2);
+ }
+ if ((pos = templ.find("%e")) != std::string::npos) {
+ templ = templ.substr(0, pos) + binfname.substr(extpos + 1) + templ.substr(pos + 2);
+ }
+ return templ;
+}
+
+int main(int argc, char** argv) {
+ std::vector<std::string> args(argv + 1, argv + argc);
+ std::vector<std::string> posargs;
+ std::string sym_begin;
+ std::string sym_end;
+ bool compatible = false;
+ int alignment = 4;
+ bool readonly = false;
+ std::string section;
+ Proc proc = PROC_ARM;
+ Endian endian = END_LITTLE;
+
+ for (auto arg_i = args.cbegin(); arg_i < args.cend(); arg_i++) {
+ const std::string cur_arg = *arg_i;
+ if (cur_arg == "-b" || cur_arg == "--begin") {
+ arg_i++;
+ sym_begin = *arg_i;
+ } else if (cur_arg == "-e" || cur_arg == "--end") {
+ arg_i++;
+ sym_end = *arg_i;
+ } else if (cur_arg == "-C" || cur_arg == "--compatible") {
+ compatible = true;
+ } else if (cur_arg == "-a" || cur_arg == "--align") {
+ arg_i++;
+ try {
+ alignment = std::stoi(*arg_i);
+ } catch (std::invalid_argument& e) {
+ if (e.what() == static_cast<std::string>("stoi")) {
+ std::cerr << "Invalid integer value for " << cur_arg << ": " << *arg_i << std::endl;
+ } else {
+ std::cerr << "Unexpected error while parsing value for " << cur_arg << ": " << e.what() << std::endl;
+ }
+ return 1;
+ }
+ } else if (cur_arg == "-r" || cur_arg == "--readonly") {
+ readonly = true;
+ } else if (cur_arg == "-s" || cur_arg == "--section") {
+ arg_i++;
+ section = *arg_i;
+ } else if (cur_arg == "-m" || cur_arg == "--machine") {
+ arg_i++;
+ if (*arg_i == "arm") {
+ proc = PROC_ARM;
+ } else if (*arg_i == "ppc") {
+ proc = PROC_PPC;
+ } else {
+ std::cerr << "Invalid argument for " << cur_arg << ": " << *arg_i << std::endl;
+ return 1;
+ }
+ } else if (cur_arg == "-B" || cur_arg == "--big-endian") {
+ endian = END_BIG;
+ } else if (cur_arg == "-L" || cur_arg == "--little-endian") {
+ endian = END_LITTLE;
+ } else {
+ posargs.push_back(cur_arg);
+ }
+ }
+
+ if (posargs.size() < 2) {
+ usage();
+ std::cerr << "Missing required position argument: " << (posargs.empty() ? "BINARY_FILE" : "OBJECT_FILE") << std::endl;
+ return 1;
+ } else if (posargs.size() > 2) {
+ usage();
+ std::cerr << "Unrecognized arguments (first one: " << posargs[2] << ")" << std::endl;
+ return 1;
+ }
+ if (compatible) {
+ sym_begin = "_binary_%f";
+ sym_end = "_binary_%f_end";
+ }
+ if (sym_begin.empty()) {
+ sym_begin = "%b_begin";
+ }
+ if (sym_end.empty()) {
+ sym_end = "%b_end";
+ }
+ sym_begin = ntrsprintf(sym_begin, posargs[0]);
+ sym_end = ntrsprintf(sym_end, posargs[0]);
+ if (section.empty()) {
+ section = readonly ? ".rodata" : ".data";
+ }
+ if (alignment == 0) {
+ alignment = 1;
+ } else {
+ int i;
+ for (i = 0; i < 10; i++) {
+ if ((alignment >> i) & 1) {
+ if ((alignment >> i) & ~1) {
+ std::cerr << "Alignment must be a power of 2" << std::endl;
+ return 1;
+ }
+ break;
+ }
+ }
+ if (i == 10) {
+ std::cerr << "Alignment exceeds maximum value of 512" << std::endl;
+ return 1;
+ }
+ }
+ if (alignment < 4) {
+ alignment = 4;
+ }
+ std::ifstream binfile(posargs[0], std::ios::binary | std::ios::ate);
+ if (!binfile.good()) {
+ std::cerr << "Unable to open file " << posargs[0] << " for reading" << std::endl;
+ return 1;
+ }
+ std::ofstream objfile(posargs[1], std::ios::binary);
+ if (!objfile.good()) {
+ binfile.close();
+ std::cerr << "Unable to open file " << posargs[1] << " for writing" << std::endl;
+ return 1;
+ }
+ size_t filesize = binfile.tellg();
+ binfile.seekg(0);
+
+ // Elf header
+ Elf32_Ehdr ehdr = {
+ .e_ident = {
+ ELFMAG0, // EI_MAG0
+ ELFMAG1, // EI_MAG1
+ ELFMAG2, // EI_MAG2
+ ELFMAG3, // EI_MAG3
+ ELFCLASS32, // EI_CLASS
+ endian, // EI_DATA
+ EV_CURRENT, // EI_VERSION
+ ELFOSABI_NONE, // EI_OSABI
+ 0, // EI_ABIVERSION
+ },
+ .e_type = ET_REL,
+ .e_machine = proc,
+ .e_version = EV_CURRENT,
+ .e_shoff = sizeof(Elf32_Ehdr),
+ .e_ehsize = sizeof(Elf32_Ehdr),
+ .e_shentsize = sizeof(Elf32_Shdr),
+ .e_shnum = 5,
+ .e_shstrndx = 1,
+ };
+
+ // Five sections: NULL, user section, symtab, strtab, shstrtab
+ Elf32_Shdr shdr[5] = {};
+
+ size_t sh_name = 1;
+ shdr[SHNDX_SHSTRTAB].sh_type = SHT_STRTAB;
+ shdr[SHNDX_SHSTRTAB].sh_offset = ehdr.e_shoff + 5 * sizeof(Elf32_Shdr);
+ shdr[SHNDX_SHSTRTAB].sh_name = sh_name;
+ sh_name += std::string(".shstrtab").length() + 1;
+ shdr[SHNDX_SYMTAB].sh_type = SHT_SYMTAB;
+ shdr[SHNDX_SYMTAB].sh_link = SHNDX_STRTAB;
+ shdr[SHNDX_SYMTAB].sh_info = 2;
+ shdr[SHNDX_SYMTAB].sh_addralign = 4;
+ shdr[SHNDX_SYMTAB].sh_name = sh_name;
+ shdr[SHNDX_SYMTAB].sh_entsize = sizeof(Elf32_Sym);
+ sh_name += std::string(".symtab").length() + 1;
+ shdr[SHNDX_STRTAB].sh_type = SHT_STRTAB;
+ shdr[SHNDX_STRTAB].sh_addralign = 1;
+ shdr[SHNDX_STRTAB].sh_name = sh_name;
+ sh_name += std::string(".strtab").length() + 1;
+ shdr[SHNDX_PROGBITS].sh_type = SHT_PROGBITS;
+ shdr[SHNDX_PROGBITS].sh_flags = SHF_ALLOC | (readonly ? 0 : SHF_WRITE);
+ shdr[SHNDX_PROGBITS].sh_addralign = alignment;
+ shdr[SHNDX_PROGBITS].sh_name = sh_name;
+ sh_name += section.length() + 1;
+ shdr[SHNDX_SHSTRTAB].sh_size = sh_name;
+ sh_name = (sh_name + 3) & ~3;
+ shdr[SHNDX_SYMTAB].sh_offset = shdr[SHNDX_SHSTRTAB].sh_offset + sh_name;
+ shdr[SHNDX_SYMTAB].sh_size = 3 * sizeof(Elf32_Sym);
+ shdr[SHNDX_STRTAB].sh_offset = shdr[SHNDX_SYMTAB].sh_offset + shdr[SHNDX_SYMTAB].sh_size;
+ size_t st_name = sym_begin.length() + sym_end.length() + 3;
+ shdr[SHNDX_STRTAB].sh_size = st_name;
+ st_name = (st_name + 3) & ~3;
+ shdr[SHNDX_PROGBITS].sh_offset = shdr[SHNDX_STRTAB].sh_offset + st_name;
+ shdr[SHNDX_PROGBITS].sh_size = filesize;
+
+ // symtab
+ Elf32_Sym syms[3] = {};
+ // begin
+ syms[1].st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT);
+ syms[1].st_shndx = SHNDX_PROGBITS;
+ syms[1].st_size = filesize;
+ syms[1].st_value = 0;
+ syms[1].st_name = 1;
+ // end
+ syms[2].st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT);
+ syms[2].st_shndx = SHNDX_PROGBITS;
+ syms[2].st_size = 0;
+ syms[2].st_value = filesize;
+ syms[2].st_name = sym_begin.length() + 2;
+
+ // Write to file
+ char zeroes[3] = {0, 0, 0};
+ unsigned padding;
+ // Elf header
+ objfile.write((char*)&ehdr, sizeof(ehdr));
+ // Section headers
+ objfile.write((char*)shdr, sizeof(shdr));
+ // Section string table
+ objfile.write(zeroes, 1);
+ objfile.write(".shstrtab", sizeof(".shstrtab"));
+ objfile.write(".symtab", sizeof(".symtab"));
+ objfile.write(".strtab", sizeof(".strtab"));
+ objfile.write(section.c_str(), static_cast<long>(section.length() + 1));
+ padding = (4 - (sizeof(".shstrtab") + sizeof(".symtab") + sizeof(".strtab") + section.length() + 2)) & 3;
+ objfile.write(zeroes, padding);
+ // Symbol table
+ objfile.write((char*)syms, sizeof(syms));
+ // String table
+ objfile.write(zeroes, 1);
+ objfile.write(sym_begin.c_str(), static_cast<long>(sym_begin.length() + 1));
+ objfile.write(sym_end.c_str(), static_cast<long>(sym_end.length() + 1));
+ padding = (4 - (sym_begin.length() + sym_end.length() + 3)) & 3;
+ objfile.write(zeroes, padding);
+ // Data
+ char* rdbuf = new char[filesize];
+ binfile.read(rdbuf, static_cast<long>(filesize));
+ objfile.write(rdbuf, static_cast<long>(filesize));
+ delete[] rdbuf;
+
+ binfile.close();
+ objfile.close();
+ return 0;
+}
diff --git a/tools/msgenc/.gitignore b/tools/msgenc/.gitignore
index 15921071..760f8a2e 100644
--- a/tools/msgenc/.gitignore
+++ b/tools/msgenc/.gitignore
@@ -1 +1,2 @@
msgenc
+.deps
diff --git a/tools/msgenc/Makefile b/tools/msgenc/Makefile
index 2fc4f3a9..b39d97cb 100644
--- a/tools/msgenc/Makefile
+++ b/tools/msgenc/Makefile
@@ -1,13 +1,40 @@
CXXFLAGS := -std=c++17 -O2 -Wall -Wno-switch
CFLAGS := -O2 -Wall -Wno-switch
+ifeq ($(DEBUG),)
+CXXFLAGS += -DNDEBUG
+endif
+
+DEPDIR := .deps
+DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
+
+define SRCS :=
+ msgenc.cpp
+ MessagesConverter.cpp
+ MessagesDecoder.cpp
+ MessagesEncoder.cpp
+endef
+
+OBJS := $(SRCS:%.cpp=%.o)
+
.PHONY: all clean
all: msgenc
@:
clean:
- $(RM) msgenc msgenc.exe
+ $(RM) -r msgenc msgenc.exe $(OBJS) $(DEPDIR)
+
+msgenc: $(OBJS)
+ $(CXX) $(LDFLAGS) -o $@ $^
+
+%.o: %.cpp
+%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR)
+ $(CXX) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
+
+$(DEPDIR): ; @mkdir -p $@
+
+DEPFILES := $(SRCS:%.cpp=$(DEPDIR)/%.d)
+$(DEPFILES):
-msgenc: msgenc.cpp
- $(CXX) $(CXXFLAGS) -o $@ $^
+include $(wildcard $(DEPFILES))
diff --git a/tools/msgenc/MessagesConverter.cpp b/tools/msgenc/MessagesConverter.cpp
new file mode 100644
index 00000000..ff75f7d7
--- /dev/null
+++ b/tools/msgenc/MessagesConverter.cpp
@@ -0,0 +1,97 @@
+#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()) {
+ throw ifstream::failure("unable to open file \"" + filename + "\" for reading");
+ }
+ stringstream ss;
+ ss << file.rdbuf();
+ file.close();
+ return ss.str();
+}
+
+void MessagesConverter::WriteTextFile(string& filename, string const& contents) {
+ ofstream file(filename);
+ if (!file.good()) {
+ throw ofstream::failure("unable to open file \"" + filename + "\" for writing");
+ }
+ file << contents;
+ file.close();
+}
+
+void MessagesConverter::ReadCharmap() {
+ string raw = ReadTextFile(charmapfilename);
+ string line;
+ size_t pos, eqpos, last_pos, 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 " << (lineno + 1);
+ throw runtime_error(s.str());
+ }
+ string value = line.substr(0, eqpos);
+ string code = line.substr(eqpos + 1);
+ uint16_t value_i = stoi(value, nullptr, 16);
+ if (code[0] == '{' && code[code.length() - 1] == '}') {
+ code = code.substr(1, code.length() - 2);
+ CmdmapRegisterCommand(code, value_i);
+ } else {
+ CharmapRegisterCharacter(code, value_i);
+ }
+ }
+}
+
+uint16_t MessagesConverter::CalcCRC()
+{
+ uint16_t crc = 0;
+ for (char d : binfilename) {
+ for (int i = 0; i < 8; i++) {
+ crc = (crc & 0x8000) ? ((crc << 1) ^ 0x8003) : (crc << 1);
+ if (d & 0x80) crc ^= 1;
+ d <<= 1;
+ }
+ }
+ return crc;
+}
+
+void MessagesConverter::WriteBinaryDecoded(string &filename)
+{
+ ofstream outfile(filename, ios::binary);
+ if (!outfile.good()) {
+ throw ios::failure("Unable to open file " + filename + " for writing");
+ }
+ outfile.write((char *)&header, sizeof(header));
+ outfile.write((char *)alloc_table.data(), alloc_table.size() * sizeof(MsgAlloc));
+ for (auto msg : vec_encoded) {
+ outfile.write((char *)msg.data(), msg.size() * 2);
+ }
+ outfile.close();
+}
+
+MessagesConverter::~MessagesConverter() = default;
diff --git a/tools/msgenc/MessagesConverter.h b/tools/msgenc/MessagesConverter.h
new file mode 100644
index 00000000..ad1ba5f6
--- /dev/null
+++ b/tools/msgenc/MessagesConverter.h
@@ -0,0 +1,103 @@
+#ifndef GUARD_MESSAGESCONVERTER_H
+#define GUARD_MESSAGESCONVERTER_H
+
+#include "util.h"
+#include <string>
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <vector>
+#include <set>
+
+using namespace std;
+
+static inline uint16_t enc_short(uint16_t value, uint16_t & seed) {
+ value ^= seed;
+ seed += 18749;
+ return value;
+}
+
+enum ConvertMode : uint8_t {
+ CONV_ENCODE = 0,
+ CONV_DECODE,
+ CONV_INVALID = 0xFF,
+};
+
+struct MsgArcHeader
+{
+ uint16_t count;
+ uint16_t key;
+};
+
+struct MsgAlloc
+{
+ uint32_t offset;
+ uint32_t length;
+ void encrypt(uint16_t key, int i) {
+ uint32_t alloc_key = (765 * i * key) & 0xFFFF;
+ alloc_key |= alloc_key << 16;
+ offset ^= alloc_key;
+ length ^= alloc_key;
+ }
+ void decrypt(uint16_t key, int i) { encrypt(key, i); }
+};
+
+static inline void EncryptU16String(u16string & message, int & i) {
+ uint16_t key = i * 596947;
+ for (auto & code : message) {
+ code = enc_short(code, key);
+ }
+}
+
+static inline void DecryptU16String(u16string & message, int & i) {
+ EncryptU16String(message, 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 charmapfilename;
+ string binfilename;
+
+ MsgArcHeader header = {};
+ vector<MsgAlloc> alloc_table;
+ vector<string> vec_decoded;
+ vector<u16string> vec_encoded;
+
+ 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) {
+ _out[_pair.second] = _pair.first;
+ }
+ }
+ static string ReadTextFile(string& filename);
+ static void WriteTextFile(string& filename, string const & contents);
+
+public:
+ MessagesConverter(ConvertMode _mode, string &_textfilename, int _key, string &_charmapfilename, string &_binfilename) :
+ mode(_mode),
+ textfilename(_textfilename),
+ charmapfilename(_charmapfilename),
+ binfilename(_binfilename)
+ {
+ header.key = (_key == 0) ? CalcCRC() : static_cast<uint16_t>(_key);
+ }
+ void ReadCharmap();
+ virtual void ReadInput() = 0;
+ virtual void Convert() = 0;
+ virtual void WriteOutput() = 0;
+ virtual ~MessagesConverter() = 0;
+
+ uint16_t CalcCRC();
+
+ uint16_t GetKey() {
+ return header.key;
+ }
+
+ void WriteBinaryDecoded(string &filename);
+};
+
+#endif //GUARD_MESSAGESCONVERTER_H
diff --git a/tools/msgenc/MessagesDecoder.cpp b/tools/msgenc/MessagesDecoder.cpp
new file mode 100644
index 00000000..f98b28c7
--- /dev/null
+++ b/tools/msgenc/MessagesDecoder.cpp
@@ -0,0 +1,185 @@
+#include <algorithm>
+#include "MessagesDecoder.h"
+
+void MessagesDecoder::CmdmapRegisterCommand(string &command, uint16_t value)
+{
+ cmdmap[value] = command;
+ if (command.rfind("STRVAR_", 0) == 0)
+ strvar_codes.insert(value);
+}
+
+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;
+ unsigned shift = 4 * (n - 1);
+
+ for (int i = 0; i < n; i++) {
+ unsigned nybble = (value >> shift) & 0xF;
+ if (nybble == 0 && !printing_zeroes) {
+ if (i == n - 1) printing_zeroes = true;
+ else if (mode == STR_CONV_MODE_RIGHT_ALIGN) {
+ dest += ' ';
+ continue;
+ }
+ }
+ if (nybble != 0 || printing_zeroes) {
+ if (nybble < 10) {
+ dest += (char)('0' + nybble);
+ } else {
+ dest += (char)('A' + nybble - 10);
+ }
+ }
+ shift -= 4;
+ }
+
+ return dest;
+}
+
+void MessagesDecoder::ReadMessagesFromBin(string& filename)
+{
+ ifstream infile(filename, ios_base::binary);
+ if (!infile.good()) {
+ throw ifstream::failure("Unable to open file \"" + filename + "\" for reading");
+ }
+ infile.read((char*)&header, sizeof(header));
+ debug_printf("%d lines\n", header.count);
+ alloc_table.resize(header.count);
+ infile.read((char*)alloc_table.data(), (streamsize)(sizeof(MsgAlloc) * header.count));
+ int i = 1;
+ for (auto & alloc : alloc_table) {
+ alloc.decrypt(header.key, i);
+ u16string str;
+ str.resize(alloc.length);
+ infile.read((char*)str.data(), (2 * alloc.length));
+ DecryptU16String(str, i);
+ vec_encoded.push_back(str);
+ i++;
+ }
+ infile.close();
+}
+
+u16string MessagesDecoder::DecodeTrainerNameMessage(u16string const &message)
+{
+ int bit = 0;
+ u16string out = u"\uf100";
+ auto src_p = message.cbegin() + 1;
+ char16_t cur_char;
+ do {
+ cur_char = ((*src_p >> bit) & 0x1FF);
+ bit += 9;
+ if (bit >= 15) {
+ src_p++;
+ bit -= 15;
+ if (bit != 0 && src_p < message.cend()) {
+ cur_char |= (*src_p << (9 - bit)) & 0x1FF;
+ }
+ }
+ out += cur_char;
+ } while (src_p < message.cend() && cur_char != 0x1FF);
+ return out;
+}
+
+string MessagesDecoder::DecodeMessage(u16string &message, int &i) {
+ string decoded;
+ bool is_trname = false;
+ for (size_t j = 0; j < message.size(); j++) {
+ uint16_t code = message[j];
+ debug_printf("%04X ", code);
+
+ if (charmap.find(code) != charmap.end()) {
+ decoded += charmap[code];
+ }
+ else if (code == (is_trname ? 0x01FF : 0xFFFF)) {
+ break;
+ }
+ else if (code == 0xFFFE) {
+ decoded += '{';
+ j++;
+ code = message[j++];
+ debug_printf("%04X ", code);
+ string command;
+ bool is_strvar = false;
+ if (find(strvar_codes.cbegin(), strvar_codes.cend(), code & 0xFF00) != strvar_codes.cend()) {
+ is_strvar = true;
+ command = "STRVAR_" + ConvertIntToHexStringN((code >> 8), STR_CONV_MODE_LEFT_ALIGN, 2);
+ }
+ 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));
+ }
+ decoded += command;
+ int nargs = message[j++];
+ debug_printf("%04X ", nargs);
+ if (is_strvar) {
+ decoded += ' ';
+ decoded += to_string(code & 0xFF);
+ if (nargs != 0)
+ decoded += ',';
+ }
+ for (int k = 0; k < nargs; k++) {
+ decoded += ' ';
+ decoded += to_string(message[j + k]);
+ debug_printf("%04X ", message[j + k]);
+ if (k != nargs - 1)
+ decoded += ',';
+ }
+ decoded += '}';
+ j += nargs - 1;
+ }
+ else if (code == 0xF100) {
+ decoded += "{TRNAME}";
+ message = DecodeTrainerNameMessage(message);
+ is_trname = true;
+ }
+ else {
+ throw runtime_error("invalid character in " + binfilename + ": " + ConvertIntToHexStringN(code, STR_CONV_MODE_LEADING_ZEROS, 4) + " at " + to_string(i) + ":" + to_string(j));
+ }
+ }
+ MsgAlloc & alloc = alloc_table[i];
+ debug_printf("msg %d: at 0x%08X, count %d\n", i + 1, alloc.offset, alloc.length);
+ return decoded;
+}
+
+template <typename T> void MessagesDecoder::WriteBinaryFile(string& filename, T& data) {
+ ofstream outfile(filename, ios_base::binary);
+ if (!outfile.good()) {
+ throw ofstream::failure("Unable to open file \"" + filename + "\" for writing");
+ }
+ outfile.write((const char *)&data, sizeof(data));
+ outfile.close();
+}
+
+void MessagesDecoder::WriteMessagesToText(string& filename) {
+ stringstream ss;
+ for (string& text : vec_decoded) {
+ ss << text << "\r\n";
+ }
+ WriteTextFile(filename, ss.str());
+}
+
+// Public virtual functions
+
+void MessagesDecoder::ReadInput()
+{
+ ReadMessagesFromBin(binfilename);
+}
+
+void MessagesDecoder::Convert()
+{
+ for (int i = 0; i < (int)vec_encoded.size(); i++) {
+ u16string message = vec_encoded[i];
+ string decoded = DecodeMessage(message, i);
+ vec_decoded.push_back(decoded);
+ }
+}
+
+void MessagesDecoder::WriteOutput()
+{
+ WriteMessagesToText(textfilename);
+}
diff --git a/tools/msgenc/MessagesDecoder.h b/tools/msgenc/MessagesDecoder.h
new file mode 100644
index 00000000..98c2383f
--- /dev/null
+++ b/tools/msgenc/MessagesDecoder.h
@@ -0,0 +1,36 @@
+#ifndef GUARD_MESSAGESDECODER_H
+#define GUARD_MESSAGESDECODER_H
+
+
+#include "MessagesConverter.h"
+
+enum StrConvMode {
+ STR_CONV_MODE_LEFT_ALIGN = 0,
+ STR_CONV_MODE_RIGHT_ALIGN,
+ STR_CONV_MODE_LEADING_ZEROS
+};
+
+class MessagesDecoder : public MessagesConverter
+{
+ map <uint16_t, string> cmdmap;
+ map <uint16_t, string> charmap;
+ set<uint16_t> strvar_codes;
+
+ void ReadMessagesFromBin(string& filename);
+ void WriteMessagesToText(string& filename);
+ template <typename T> void WriteBinaryFile(string& filename, T& data);
+ 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, int _key, string &_charmapfilename, string &_binfilename) : MessagesConverter(CONV_DECODE, _textfilename, _key, _charmapfilename, _binfilename) {}
+ void ReadInput() override;
+ void Convert() override;
+ void WriteOutput() override;
+ ~MessagesDecoder() override = default;
+};
+
+
+#endif //GUARD_MESSAGESDECODER_H
diff --git a/tools/msgenc/MessagesEncoder.cpp b/tools/msgenc/MessagesEncoder.cpp
new file mode 100644
index 00000000..8450fba0
--- /dev/null
+++ b/tools/msgenc/MessagesEncoder.cpp
@@ -0,0 +1,165 @@
+#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;
+ 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);
+}
+
+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()
+{
+ ReadMessagesFromText(textfilename);
+}
+
+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);
+}
diff --git a/tools/msgenc/MessagesEncoder.h b/tools/msgenc/MessagesEncoder.h
new file mode 100644
index 00000000..a7b9111c
--- /dev/null
+++ b/tools/msgenc/MessagesEncoder.h
@@ -0,0 +1,26 @@
+#ifndef GUARD_MESSAGESENCODER_H
+#define GUARD_MESSAGESENCODER_H
+
+
+#include "MessagesConverter.h"
+
+class MessagesEncoder : public MessagesConverter
+{
+ map <string, uint16_t> cmdmap;
+ map <string, uint16_t> charmap;
+
+ 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, int _key, string &_charmapfilename, string &_binfilename) : MessagesConverter(CONV_ENCODE, _textfilename, _key, _charmapfilename, _binfilename) {}
+ void ReadInput() override;
+ void Convert() override;
+ void WriteOutput() override;
+ ~MessagesEncoder() override = default;
+};
+
+
+#endif //GUARD_MESSAGESENCODER_H
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;
}
diff --git a/tools/msgenc/util.h b/tools/msgenc/util.h
new file mode 100644
index 00000000..5c93f4ca
--- /dev/null
+++ b/tools/msgenc/util.h
@@ -0,0 +1,17 @@
+#ifndef GUARD_UTIL_H
+#define GUARD_UTIL_H
+
+#include <cstdio>
+#include <cstdarg>
+
+static inline __attribute__((format(printf, 1, 2))) void debug_printf(const char * fmt, ...) {
+#ifndef NDEBUG
+ fputs("DEBUG: ", stderr);
+ va_list va_args;
+ va_start(va_args, fmt);
+ vfprintf(stderr, fmt, va_args);
+ va_end(va_args);
+#endif //NDEBUG
+}
+
+#endif //GUARD_UTIL_H
diff --git a/tools/nitrobanner/banner.h b/tools/nitrobanner/banner.h
index 70684172..4d9cc814 100644
--- a/tools/nitrobanner/banner.h
+++ b/tools/nitrobanner/banner.h
@@ -1,7 +1,6 @@
#pragma once
#include <array>
-#include <filesystem>
#include "types.h"
constexpr int TITLE_LENGTH = 0x100 / sizeof(u16); // 128 UTF-16 characters
@@ -43,13 +42,13 @@ template <typename T>
void SerializeData(std::ofstream& ostream, T data);
void ProcessSpecFileCommand(SpecFileData& specfile_data, const std::wstring_view& command_name, const std::wstring_view& command_argument);
-SpecFileData ParseSpecFile(const std::filesystem::path& specfile_path);
+SpecFileData ParseSpecFile(const filesystem::path& specfile_path);
-IconBitmap GetIconBitmap(const std::filesystem::path& icon_bitmap_filename);
-IconPalette GetIconPalette(const std::filesystem::path& icon_palette_filename);
+IconBitmap GetIconBitmap(const filesystem::path& icon_bitmap_filename);
+IconPalette GetIconPalette(const filesystem::path& icon_palette_filename);
void OutputBanner(std::ofstream& ostream, const Banner& banner);
-bool MakeBanner(const std::filesystem::path& specfile_path, const std::filesystem::path& outfile_path);
+bool MakeBanner(const filesystem::path& specfile_path, const filesystem::path& outfile_path);
inline bool CommandIsForTitleAndDeveloper(const std::wstring& command_name) {
return command_name == L"JP" ||
diff --git a/tools/nitrogfx/Makefile b/tools/nitrogfx/Makefile
index eadf2be5..18ecdbba 100644
--- a/tools/nitrogfx/Makefile
+++ b/tools/nitrogfx/Makefile
@@ -1,6 +1,6 @@
CC = gcc
-CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK
+CFLAGS = -Wall -Wextra -Werror -Wno-sign-compare -std=c11 -O2 -DPNG_SKIP_SETJMP_CHECK -D_CRT_SECURE_NO_WARNINGS
LIBS = -lpng -lz
diff --git a/tools/nitrogfx/gfx.c b/tools/nitrogfx/gfx.c
index f5ff30e7..ee7e0d6f 100644
--- a/tools/nitrogfx/gfx.c
+++ b/tools/nitrogfx/gfx.c
@@ -101,7 +101,7 @@ static uint32_t ConvertFromScanned4Bpp(unsigned char *src, unsigned char *dest,
uint16_t val = (src[i - 1] << 8) | src[i - 2];
val ^= (encValue & 0xFFFF);
src[i - 1] = (val >> 8);
- src[i - 2] = val;
+ src[i - 2] = (unsigned char)val;
encValue = encValue * 1103515245;
encValue = encValue + 24691;
}
@@ -225,7 +225,7 @@ static void ConvertToScanned4Bpp(unsigned char *src, unsigned char *dest, int fi
encValue = (encValue - 24691) * 4005161829;
val ^= (encValue & 0xFFFF);
dest[i] = (val >> 8);
- dest[i - 1] = val;
+ dest[i - 1] = (unsigned char)val;
}
}
@@ -704,7 +704,7 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in
fwrite(palHeader, 1, 0x18, fp);
- unsigned char colours[colourNum * 2];
+ unsigned char * colours = malloc(colourNum * 2);
//palette data
for (int i = 0; i < colourNum; i++)
{
@@ -733,6 +733,7 @@ void WriteNtrPalette(char *path, struct Palette *palette, bool ncpr, bool ir, in
}
fwrite(colours, 1, colourNum * 2, fp);
+ free(colours);
fclose(fp);
}
@@ -750,7 +751,7 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
{
for (int j = 0; j < options->labelCount; j++)
{
- totalSize += strlen(options->labels[j]) + 5; //strlen + terminator + pointer
+ totalSize += (unsigned)strlen(options->labels[j]) + 5; //strlen + terminator + pointer
}
}
@@ -865,7 +866,7 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
unsigned int lablSize = 8;
for (int j = 0; j < options->labelCount; j++)
{
- lablSize += strlen(options->labels[j]) + 5;
+ lablSize += (unsigned)strlen(options->labels[j]) + 5;
}
unsigned char *labl = malloc(lablSize);
@@ -884,14 +885,14 @@ void WriteNtrCell(char *path, struct JsonToCellOptions *options)
labl[i + 8] = position & 0xff;
labl[i + 9] = position >> 8;
- position += strlen(options->labels[j]) + 1;
+ position += (unsigned)strlen(options->labels[j]) + 1;
i += 4;
}
for (int j = 0; j < options->labelCount; j++)
{
strcpy((char *) (labl + (i + 8)), options->labels[j]);
- i += strlen(options->labels[j]) + 1;
+ i += (int)strlen(options->labels[j]) + 1;
}
fwrite(labl, 1, lablSize, fp);
diff --git a/tools/nitrogfx/global.h b/tools/nitrogfx/global.h
index 65dd351d..32378a9f 100644
--- a/tools/nitrogfx/global.h
+++ b/tools/nitrogfx/global.h
@@ -28,4 +28,10 @@ do { \
#endif // _MSC_VER
+#define PTR_ADD(ptr, value) ((void*)((uintptr_t)(ptr) + (value)))
+#define PTR_SUB(ptr, value) ((void*)((uintptr_t)(ptr) - (value)))
+#define PTR_IADD(ptr, value) do { (ptr) = PTR_ADD(ptr, value); } while (0)
+#define PTR_ISUB(ptr, value) do { (ptr) = PTR_SUB(ptr, value); } while (0)
+#define PTR_DIFF(right, left) ((uintptr_t)(right) - (uintptr_t)(left))
+
#endif // GLOBAL_H
diff --git a/tools/nitrogfx/huff.c b/tools/nitrogfx/huff.c
index 143ed79b..d718fb71 100644
--- a/tools/nitrogfx/huff.c
+++ b/tools/nitrogfx/huff.c
@@ -34,17 +34,17 @@ int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) {
case 2:
// Swap the two entries if the right one compares higher.
- if (cmp(data, data + size) > 0) {
+ if (cmp(data, PTR_ADD(data, size)) > 0) {
memcpy(buffer, data, size);
- memcpy(data, data + size, size);
- memcpy(data + size, buffer, size);
+ memcpy(data, PTR_ADD(data, size), size);
+ memcpy(PTR_ADD(data, size), buffer, size);
}
break;
default:
// Merge sort out-of-place.
leftPtr = data;
- leftEnd = rightPtr = data + count / 2 * size;
- rightEnd = data + count * size;
+ leftEnd = rightPtr = PTR_ADD(data, count / 2 * size);
+ rightEnd = PTR_ADD(data, count * size);
// Sort the left half
if (!msort_r(leftPtr, count / 2, size, cmp, buffer))
@@ -58,11 +58,11 @@ int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) {
i = 0;
do {
if (cmp(leftPtr, rightPtr) <= 0) {
- memcpy(buffer + i * size, leftPtr, size);
- leftPtr += size;
+ memcpy(PTR_ADD(buffer, i * size), leftPtr, size);
+ PTR_IADD(leftPtr, size);
} else {
- memcpy(buffer + i * size, rightPtr, size);
- rightPtr += size;
+ memcpy(PTR_ADD(buffer, i * size), rightPtr, size);
+ PTR_IADD(rightPtr, size);
}
} while (++i < count && leftPtr < leftEnd && rightPtr < rightEnd);
@@ -70,10 +70,10 @@ int msort_r(void * data, size_t count, size_t size, cmpfun cmp, void * buffer) {
// Copy the remainder
if (i < count) {
if (leftPtr < leftEnd) {
- memcpy(buffer + i * size, leftPtr, leftEnd - leftPtr);
+ memcpy(PTR_ADD(buffer, i * size), leftPtr, PTR_DIFF(leftEnd, leftPtr));
}
else {
- memcpy(buffer + i * size, rightPtr, rightEnd - rightPtr);
+ memcpy(PTR_ADD(buffer, i * size), rightPtr, PTR_DIFF(rightEnd, rightPtr));
}
}
@@ -163,7 +163,7 @@ static void write_tree(unsigned char * dest, HuffNode_t * tree, int nitems, stru
if (currNode->header.isLeaf) {
dest[5 + i] = traversal[i].leaf.key;
} else {
- dest[5 + i] = (((currNode->branch.right - traversal - i) / 2) - 1);
+ dest[5 + i] = (unsigned char)(((currNode->branch.right - traversal - i) / 2) - 1);
if (currNode->branch.left->header.isLeaf)
dest[5 + i] |= 0x80;
if (currNode->branch.right->header.isLeaf)
@@ -194,8 +194,8 @@ static inline void read_32_le(unsigned char * src, int * srcPos, uint32_t * buff
}
static void write_bits(unsigned char * dest, int * destPos, struct BitEncoding * encoding, int value, uint32_t * buff, int * buffBits) {
- int nbits = encoding[value].nbits;
- uint32_t bitstring = encoding[value].bitstring;
+ int nbits = (int)encoding[value].nbits;
+ uint32_t bitstring = (uint32_t)encoding[value].bitstring;
if (*buffBits + nbits >= 32) {
int diff = *buffBits + nbits - 32;
diff --git a/tools/nitrogfx/main.c b/tools/nitrogfx/main.c
index 171cb5f3..b2d3352a 100644
--- a/tools/nitrogfx/main.c
+++ b/tools/nitrogfx/main.c
@@ -63,13 +63,14 @@ void ConvertNtrToPng(char *inputPath, char *outputPath, struct GbaToPngOptions *
if (key)
{
- char string[strlen(outputPath) + 5];
+ char* string = malloc(strlen(outputPath) + 5);
sprintf(string, "%s.key", outputPath);
FILE *fp = fopen(string, "wb");
if (fp == NULL)
FATAL_ERROR("Failed to open key file for writing.\n");
fwrite(&key, 4, 1, fp);
fclose(fp);
+ free(string);
}
image.hasTransparency = options->hasTransparency;
@@ -103,7 +104,7 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
uint32_t key = 0;
if (options->scanned)
{
- char string[strlen(inputPath) + 5];
+ char* string = malloc(strlen(inputPath) + 5);
sprintf(string, "%s.key", inputPath);
FILE *fp2 = fopen(string, "rb");
if (fp2 == NULL)
@@ -112,6 +113,7 @@ void ConvertPngToNtr(char *inputPath, char *outputPath, struct PngToNtrOptions *
if (count != 1)
FATAL_ERROR("Not a valid key file.\n");
fclose(fp2);
+ free(string);
}
WriteNtrImage(outputPath, options->numTiles, image.bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->clobberSize, options->byteOrder, options->version101, options->sopc, options->scanned, key);