Skip to content

Instantly share code, notes, and snippets.

@mtijanic
Created September 5, 2019 09:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mtijanic/153271e463918c52a252893a623bbfdc to your computer and use it in GitHub Desktop.
Save mtijanic/153271e463918c52a252893a623bbfdc to your computer and use it in GitHub Desktop.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <algorithm>
#include "FileFormats/2da.hpp"
#include "FileFormats/Tlk.hpp"
static const char *HELP =
"NWN ItemProperty table builder\n"
"Usage: build_itemproperty_table [OPTIONS]\n"
"Options:\n"
" -h, --help Print this message\n"
" -p, --path=VALUE Path where all the resources can be found\n"
" -t, --tlk=NAME Name of custom tlk file\n"
" -f, --format=FORMAT output format; supported values: sql, csv, text (default)\n";
struct CFG {
std::string path;
std::string tlk;
enum { SQL, CSV, TEXT } format;
} cfg;
#define die(format, ...) \
do { \
fprintf(stderr, format " @ %s():%d\n", ##__VA_ARGS__ , __FUNCTION__, __LINE__); \
exit(~0); \
} while(0)
void parse_cmdline(int argc, char *argv[]) {
/* if (argc < 3) {
puts(HELP);
exit(0);
}*/
const char *fmt = "text";
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
puts(HELP);
exit(0);
}
if (!strcmp(argv[i], "-p"))
cfg.path = argv[++i];
else if (!strcmp(argv[i], "-t"))
cfg.tlk = argv[++i];
else if (!strcmp(argv[i], "-f"))
fmt = argv[++i];
else {
if (!strncmp(argv[i],"--path=",strlen("--path=")))
cfg.path = argv[i] + strlen("--path=");
else if (!strncmp(argv[i],"--tlk=",strlen("--tlk=")))
cfg.tlk = argv[i] + strlen("--tlk=");
else if (!strncmp(argv[i],"--format=",strlen("--format=")))
fmt = argv[i] + strlen("--format=");
else
die("Unkown argument %s", argv[i]);
}
}
switch (*fmt) {
case 's': cfg.format = CFG::SQL; break;
case 'c': cfg.format = CFG::CSV; break;
default: cfg.format = CFG::TEXT; break;
}
if (cfg.path != "")
cfg.path += "/";
}
FileFormats::TwoDA::Friendly::TwoDA load_2da(const std::string& name) {
FileFormats::TwoDA::Raw::TwoDA twoDARaw;
auto fullname = cfg.path + name;
std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower);
if (!FileFormats::TwoDA::Raw::TwoDA::ReadFromFile(fullname.c_str(), &twoDARaw))
die("Failed to load %s", fullname.c_str());
return FileFormats::TwoDA::Friendly::TwoDA(std::move(twoDARaw));
}
FileFormats::Tlk::Friendly::Tlk load_tlk(const std::string& name) {
FileFormats::Tlk::Raw::Tlk rawTlk;
auto fullname = cfg.path + name;
std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower);
if (!FileFormats::Tlk::Raw::Tlk::ReadFromFile(fullname.c_str(), &rawTlk))
die("Failed to load %s", fullname.c_str());
return FileFormats::Tlk::Friendly::Tlk(std::move(rawTlk));
}
std::string tlk_lookup(int strref) {
if (strref > 0x1000000) {
static auto tlk = load_tlk(cfg.tlk);
return tlk[strref - 0x1000000];
}
else {
static auto tlk = load_tlk("dialog.tlk");
return tlk[strref];
}
}
int as_int(std::string str) {
try {
return std::stoi(str);
} catch(...) {
return -1;
}
}
struct itemproperty {
int type;
int subtype;
int cost;
int cost_value;
int param;
int param_value;
std::string type_name;
std::string subtype_name;
std::string cost_name;
std::string param_name;
};
std::vector<itemproperty> build_ip_table() {
std::vector<itemproperty> iptable;
auto itempropdef = load_2da("itempropdef.2da");
auto iprp_costtable = load_2da("iprp_costtable.2da");
auto iprp_paramtable = load_2da("iprp_paramtable.2da");
for (auto &row: itempropdef) {
if (row["GameStrRef"] == "****")
continue;
auto type = row.RowIdx();
auto cost = as_int(row["CostTableResRef"]);
auto param = as_int(row["Param1ResRef"]);
auto type_name = tlk_lookup(as_int(row["GameStrRef"]));
std::vector<std::pair<int, std::string>> valid_subtypes;
if (row["SubTypeResRef"] != "****") {
auto subtype_2da = load_2da(row["SubTypeResRef"] + ".2da");
for (auto &subtype_row : subtype_2da) {
auto strref = as_int(subtype_row["Name"]);
if (strref != -1)
valid_subtypes.emplace_back(std::make_pair<>(subtype_row.RowIdx(), tlk_lookup(strref)));
}
}
if (valid_subtypes.empty())
valid_subtypes.emplace_back(std::make_pair<>(-1, ""));
std::vector<std::pair<int, std::string>> valid_costs;
if (cost != -1) {
auto cost_2da = load_2da(iprp_costtable[cost]["Name"] + ".2da");
for (auto &cost_row : cost_2da) {
auto strref = as_int(cost_row["Name"]);
if (strref != -1)
valid_costs.emplace_back(std::make_pair<>(cost_row.RowIdx(), tlk_lookup(strref)));
}
}
if (valid_costs.empty())
valid_costs.emplace_back(std::make_pair<>(-1, ""));
std::vector<std::pair<int, std::string>> valid_params;
if (param != -1) {
auto param_2da = load_2da(iprp_paramtable[param]["TableResRef"] + ".2da");
for (auto &param_row : param_2da) {
auto strref = as_int(param_row["Name"]);
if (strref != -1)
valid_params.emplace_back(std::make_pair<>(param_row.RowIdx(), tlk_lookup(strref)));
}
}
if (valid_params.empty())
valid_params.emplace_back(std::make_pair<>(-1, ""));
for (auto &subtype: valid_subtypes) {
for (auto &cost_value: valid_costs) {
for (auto &param_value: valid_params) {
itemproperty ip;
ip.type = type;
ip.subtype = subtype.first;
ip.cost = cost;
ip.cost_value = cost_value.first;
ip.param = param;
ip.param_value = param_value.first;
ip.type_name = type_name;
ip.subtype_name = subtype.second;
ip.cost_name = cost_value.second;
ip.param_name = param_value.second;
iptable.emplace_back(ip);
}
}
}
}
return std::move(iptable);
}
void print_ip(const itemproperty& ip) {
printf(" % 8d % 8d % 8d % 8d % 8d % 8d : %s %s %s %s\n",
ip.type, ip.subtype, ip.cost, ip.cost_value, ip.param, ip.param_value,
ip.type_name.c_str(), ip.subtype_name.c_str(), ip.cost_name.c_str(), ip.param_name.c_str());
}
int main(int argc, char** argv) {
parse_cmdline(argc, argv);
auto table = build_ip_table();
for (auto &ip: table)
print_ip(ip);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment