wut/tools/rplgen/rplgen.cpp

191 lines
4.7 KiB
C++
Raw Normal View History

2018-05-23 12:10:23 +02:00
#include "utils.h"
2018-05-23 00:08:13 +02:00
#include <array>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <cstdint>
#include <iostream>
#include <functional>
#include <fstream>
#include <locale>
#include <vector>
#include <string>
#include <zlib.h>
// trim from start
// Taken from https://stackoverflow.com/a/217605
static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// trim from both ends
static inline void
trim(std::string &s)
{
ltrim(s);
rtrim(s);
}
enum class ReadMode
{
INVALID,
TEXT,
DATA
};
void
writeExports(std::ofstream &out,
const std::string &moduleName,
bool isData,
const std::vector<std::string> &exports)
{
// Align module name up to 8 bytes
auto moduleNameSize = (moduleName.length() + 1 + 7) & ~7;
// Calculate the data block size
auto exportSecSize = exports.size() * 8;
if (exportSecSize < moduleNameSize) {
exportSecSize = moduleNameSize;
}
// Calculate export hash
uint32_t exportsHash = crc32(0, Z_NULL, 0);
for (auto &exp : exports) {
exportsHash = crc32(exportsHash, reinterpret_cast<const Bytef *>(exp.data()), exp.size() + 1);
}
std::array<Bytef, 0xE> extraHashBytes;
extraHashBytes.fill(0);
exportsHash = crc32(exportsHash, extraHashBytes.data(), extraHashBytes.size());
// Setup section data
std::vector<uint32_t> secData;
secData.resize(exportSecSize / 4, 0);
memcpy(secData.data(), moduleName.c_str(), moduleName.length());
out << std::endl;
if (isData) {
out << ".section .dimport_";
} else {
out << ".section .fimport_";
}
out << moduleName << ", \"ax\", @0x80000002" << std::endl;
out << ".align 4" << std::endl;
out << std::endl;
out << ".long " << exports.size() << std::endl;
out << ".long 0x" << std::hex << exportsHash << std::endl;
out << std::endl;
const char *type = isData ? "@object" : "@function";
for (auto i = 0; i < exportSecSize / 8; ++i) {
if (i < exports.size()) {
out << ".global " << exports[i] << std::endl;
out << ".type " << exports[i] << ", " << type << std::endl;
out << exports[i] << ":" << std::endl;
}
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 0]) << std::endl;
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 1]) << std::endl;
out << std::endl;
}
}
int main(int argc, char **argv)
{
std::string moduleName;
std::vector<std::string> funcExports, dataExports;
ReadMode readMode = ReadMode::INVALID;
if (argc < 3) {
std::cout << argv[0] << " <exports.def> <output.S>" << std::endl;
return 0;
}
{
std::ifstream in;
in.open(argv[1]);
if (!in.is_open()) {
std::cout << "Could not open file " << argv[1] << " for reading" << std::endl;
return -1;
}
std::string line;
while (std::getline(in, line)) {
// Trim comments
std::size_t commentOffset = line.find("//");
if (commentOffset != std::string::npos) {
line = line.substr(0, commentOffset);
}
// Trim whitespace
trim(line);
// Skip blank lines
if (line.length() == 0) {
continue;
}
// Look for section headers
if (line[0] == ':') {
if (line.substr(1) == "TEXT") {
readMode = ReadMode::TEXT;
} else if (line.substr(1) == "DATA") {
readMode = ReadMode::DATA;
} else if (line.substr(1, 4) == "NAME") {
moduleName = line.substr(6);
} else {
std::cout << "Unexpected section type" << std::endl;
return -1;
}
continue;
}
if (readMode == ReadMode::TEXT) {
funcExports.push_back(line);
} else if (readMode == ReadMode::DATA) {
dataExports.push_back(line);
} else {
std::cout << "Unexpected section data" << std::endl;
return -1;
}
}
}
{
std::ofstream out;
out.open(argv[2]);
if (!out.is_open()) {
std::cout << "Could not open file " << argv[2] << " for writing" << std::endl;
return -1;
}
if (funcExports.size() > 0) {
writeExports(out, moduleName, false, funcExports);
}
if (dataExports.size() > 0) {
writeExports(out, moduleName, true, dataExports);
}
}
return 0;
}