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 | |
parent | 4e4192cd7007d16cefe00facbc7b721353c94f60 (diff) | |
parent | 05ded46ab7f556956a2eee2411a8d2968b7e8ad6 (diff) |
Merge branch 'master' into unk_02006D98
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bin2obj/.gitignore | 1 | ||||
-rw-r--r-- | tools/bin2obj/Makefile | 13 | ||||
-rw-r--r-- | tools/bin2obj/bin2obj.cpp | 295 | ||||
-rw-r--r-- | tools/msgenc/.gitignore | 1 | ||||
-rw-r--r-- | tools/msgenc/Makefile | 33 | ||||
-rw-r--r-- | tools/msgenc/MessagesConverter.cpp | 97 | ||||
-rw-r--r-- | tools/msgenc/MessagesConverter.h | 103 | ||||
-rw-r--r-- | tools/msgenc/MessagesDecoder.cpp | 185 | ||||
-rw-r--r-- | tools/msgenc/MessagesDecoder.h | 36 | ||||
-rw-r--r-- | tools/msgenc/MessagesEncoder.cpp | 165 | ||||
-rw-r--r-- | tools/msgenc/MessagesEncoder.h | 26 | ||||
-rw-r--r-- | tools/msgenc/msgenc.cpp | 319 | ||||
-rw-r--r-- | tools/msgenc/util.h | 17 | ||||
-rw-r--r-- | tools/nitrobanner/banner.h | 9 | ||||
-rw-r--r-- | tools/nitrogfx/Makefile | 2 | ||||
-rw-r--r-- | tools/nitrogfx/gfx.c | 15 | ||||
-rw-r--r-- | tools/nitrogfx/global.h | 6 | ||||
-rw-r--r-- | tools/nitrogfx/huff.c | 28 | ||||
-rw-r--r-- | tools/nitrogfx/main.c | 6 |
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); |