Take languages from json files on github. Overhaul work with databases.

This commit is contained in:
lzwdgc 2024-04-03 18:33:03 +03:00
parent 3d7970df53
commit ace9535747
4 changed files with 124 additions and 85 deletions

View file

@ -143,7 +143,7 @@ int main(int argc, char *argv[]) {
// patch note: Database Changes // patch note: Database Changes
// patch note: DB // patch note: DB
auto db = mod.db(); auto &db = mod.db();
// patch note: set glider GL_S3_PS_FINDER2 model to MOD_GL_S3_PS_FINDER2 (lz) // patch note: set glider GL_S3_PS_FINDER2 model to MOD_GL_S3_PS_FINDER2 (lz)
db["Глайдеры"]["GL_S3_PS_FINDER2"]["MODEL"] = "MOD_GL_S3_PS_FINDER2"; db["Глайдеры"]["GL_S3_PS_FINDER2"]["MODEL"] = "MOD_GL_S3_PS_FINDER2";
// patch note: copy MOD_GL_S3_PS_FINDER2 model from aim2 (lz) // patch note: copy MOD_GL_S3_PS_FINDER2 model from aim2 (lz)
@ -170,24 +170,12 @@ int main(int argc, char *argv[]) {
// patch note: double gun for config CFG_EYEDSTONE_2: from GUN_FAST_ELECTROMAGNETIC_BEAM to double GUN_FAST_ELECTROMAGNETIC_BEAM (lz) // patch note: double gun for config CFG_EYEDSTONE_2: from GUN_FAST_ELECTROMAGNETIC_BEAM to double GUN_FAST_ELECTROMAGNETIC_BEAM (lz)
tblcfg["CFG_EYEDSTONE_2"]["LIGHTGUN1"] = "GUN_FAST_ELECTROMAGNETIC_BEAM"; tblcfg["CFG_EYEDSTONE_2"]["LIGHTGUN1"] = "GUN_FAST_ELECTROMAGNETIC_BEAM";
// end of db changes // end of db changes
#ifdef NDEBUG
db.write();
#endif
// patch note: INFORMATION // patch note: INFORMATION
{ auto &quest = mod.quest();
auto quest = mod.quest("ru_RU"); // patch note: add name for SINIGR armor, it was unnamed before (lz)
// patch note: add name for SINIGR armor, it was unnamed before (lz) quest["ru_RU"]["INFORMATION"]["EQP_ZERO_ARMOR_S_SIN"]["NAME"] = "Особая нуль-броня";
quest["INFORMATION"]["EQP_ZERO_ARMOR_S_SIN"]["NAME"] = "Особая нуль-броня"; quest["en_US"]["INFORMATION"]["EQP_ZERO_ARMOR_S_SIN"]["NAME"] = "Special zero armor";
}
{
auto quest = mod.quest("en_US");
quest["INFORMATION"]["EQP_ZERO_ARMOR_S_SIN"]["NAME"] = "Special zero armor";
}
// more known langs: cs_CZ, de_DE, et_EE, fr_FR
// you can find vanilla dbs here (not sure if it is 1.00 or 1.03, probably 1.00):
// Supercluster discord
// https://discord.gg/CFFKpTwYZD
// https://discord.com/channels/463656710666584064/516316063747538945/615934366593581067
// patch note: // patch note:
// patch note: Game Changes // patch note: Game Changes
@ -217,13 +205,12 @@ int main(int argc, char *argv[]) {
// patch note dev: make EQP_VACUUM_DRIVE_S4 more powerful // patch note dev: make EQP_VACUUM_DRIVE_S4 more powerful
db["Оборудование"]["EQP_VACUUM_DRIVE_S4"]["VALUE1"] = 4158000.f; db["Оборудование"]["EQP_VACUUM_DRIVE_S4"]["VALUE1"] = 4158000.f;
// end of db changes in dev mode // end of db changes in dev mode
auto m2_gliders = mod.open_aim2_db()["Глайдеры"]; auto m2_gliders = mod.open_aim2_db().at("Глайдеры");
for (auto &&[n,_] : db["Глайдеры"]) { for (auto &&[n,_] : db["Глайдеры"]) {
m2_gliders.erase(n); m2_gliders.erase(n);
} }
m2_gliders.erase("GL_BOT"); m2_gliders.erase("GL_BOT");
m2_gliders.erase("GL_RACE1"); m2_gliders.erase("GL_RACE1");
db.write();
// patch note dev: copy gliders from m2: GL_M4_C_MASTODON, GL_M4_S_FLASH, GL_M4_A_FORWARD, GL_M4_A_FORWARD_BLACK // patch note dev: copy gliders from m2: GL_M4_C_MASTODON, GL_M4_S_FLASH, GL_M4_A_FORWARD, GL_M4_A_FORWARD_BLACK
auto add_glider = [&, after = "GL_M1_A_ATTACKER"s](auto &&name) mutable { auto add_glider = [&, after = "GL_M1_A_ATTACKER"s](auto &&name) mutable {
mod.copy_glider_from_aim2(name); mod.copy_glider_from_aim2(name);

View file

@ -74,7 +74,7 @@ struct aim_exe_v1_06_constants {
struct mod_maker { struct mod_maker {
struct db_wrapper { struct db_wrapper {
db2::files::db2_internal m; db2::files::db2_internal m;
db2::files::db2_internal *m2_{}; db2::files::db2_internal m2;
path fn; path fn;
int codepage{1251}; int codepage{1251};
bool written{}; bool written{};
@ -88,33 +88,56 @@ struct mod_maker {
m.save(fn, codepage); m.save(fn, codepage);
written = true; written = true;
} }
auto write(const path &fn) {
auto files = m.save(fn, codepage);
written = true;
return files;
}
auto &operator[](this auto &&d, const std::string &s) { auto &operator[](this auto &&d, const std::string &s) {
return d.m[s]; return d.m[s];
} }
auto &m2() {
return *m2_;
}
void copy_from_aim2(auto &&table_name, auto &&value_name, auto &&field_name) { void copy_from_aim2(auto &&table_name, auto &&value_name, auto &&field_name) {
auto check_val = [](auto &&m, const std::string &key, auto &&err) { if (m2.empty()) {
if (auto it = m.find(key); it == m.end()) { return;
throw std::runtime_error{err}; }
} m[table_name][value_name][field_name] = m2.at(table_name).at(value_name).at(field_name);
};
check_val(m2().m, (const char *)table_name, "aim2: no such table");
check_val(m2()[table_name], value_name, "aim2: no such value");
check_val(m2()[table_name][value_name], field_name, "aim2: no such field");
m[table_name][value_name][field_name] = m2()[table_name][value_name][field_name];
} }
void copy_from_aim2(auto &&table_name, auto &&value_name) { void copy_from_aim2(auto &&table_name, auto &&value_name) {
auto check_val = [](auto &&m, const std::string &key, auto &&err) { if (m2.empty()) {
if (auto it = m.find(key); it == m.end()) { return;
throw std::runtime_error{err}; }
} m[table_name][value_name] = m2.at(table_name).at(value_name);
};
check_val(m2().m, (const char *)table_name, "aim2: no such table");
check_val(m2()[table_name], value_name, "aim2: no such value");
m[table_name][value_name] = m2()[table_name][value_name];
} }
bool empty() const { return m.empty(); }
};
struct quest_wrapper {
std::map<std::string, db_wrapper> m;
bool written{};
quest_wrapper() = default;
quest_wrapper(const quest_wrapper &) = delete;
auto write(const path &datadir) {
std::set<path> files;
for (auto &&[fn,v] : m) {
files.merge(v.write(datadir / ("quest_" + fn)));
}
written = true;
return files;
}
auto &operator[](this auto &&d, const std::string &s) {
return d.m[s];
}
void copy_from_aim2(auto &&table_name, auto &&value_name, auto &&field_name) {
for (auto &&[_, v] : m) {
v.copy_from_aim2(table_name, value_name, field_name);
}
}
void copy_from_aim2(auto &&table_name, auto &&value_name) {
for (auto &&[_, v] : m) {
v.copy_from_aim2(table_name, value_name);
}
}
bool empty() const { return m.empty(); }
}; };
enum class file_type { enum class file_type {
unknown, unknown,
@ -138,6 +161,8 @@ struct mod_maker {
std::set<path> restored_files; std::set<path> restored_files;
std::set<path> copied_files; std::set<path> copied_files;
std::source_location loc; std::source_location loc;
db_wrapper dw;
quest_wrapper qw;
mod_maker(std::source_location loc = std::source_location::current()) : loc{loc} { mod_maker(std::source_location loc = std::source_location::current()) : loc{loc} {
init(fs::current_path()); init(fs::current_path());
@ -171,6 +196,10 @@ struct mod_maker {
} }
} }
void apply() { void apply() {
dw.write();
auto quest_dbs = qw.write(get_data_dir());
files_to_distribute.merge(quest_dbs);
std::vector<std::string> files; std::vector<std::string> files;
for (auto &&p : files_to_pak) { for (auto &&p : files_to_pak) {
if (p.filename() == aim_exe) { if (p.filename() == aim_exe) {
@ -398,9 +427,8 @@ struct mod_maker {
add_resource(copied_fn); add_resource(copied_fn);
db().copy_from_aim2("Модели", path{object}.stem().string()); db().copy_from_aim2("Модели", path{object}.stem().string());
auto textures = read_lines(path{copied_fn} += ".textures.txt"); auto textures = read_lines(path{copied_fn} += ".textures.txt");
auto &m2 = open_aim2_db();
for (auto &&t : textures) { for (auto &&t : textures) {
path fn = std::get<std::string>(m2["Текстуры"][t]["FILENAME"]); path fn = std::get<std::string>(db().m2.at("Текстуры").at(t).at("FILENAME"));
if (fn.empty()) { if (fn.empty()) {
throw std::runtime_error{"Can't find texture: "s + t}; throw std::runtime_error{"Can't find texture: "s + t};
} }
@ -437,44 +465,65 @@ struct mod_maker {
copy_from_aim2("MOD_"s + object); copy_from_aim2("MOD_"s + object);
db().copy_from_aim2("Глайдеры", path{object}.stem().string()); db().copy_from_aim2("Глайдеры", path{object}.stem().string());
// may be absent - try..catch? quest().copy_from_aim2("INFORMATION", path{object}.stem().string());
quest("ru_RU").copy_from_aim2("INFORMATION", path{object}.stem().string());
} }
auto db() { auto &db() {
auto w = open_db("db", 1251); // always 1251 probably if (dw.empty()) {
if (aim2_available()) { auto cp = 1251; // always 1251 or 0 probably for db
w.m2_ = &open_aim2_db(); dw = open_db("db", cp);
}
return w;
}
auto quest(const std::string &language = {}) {
// TODO: check if it's possible to use utf8/16 in aim game
// set codepages here until we fix or implement unicode
int db_codepage = 1251;
if (language == "fr_fr") {
// change cp. Also for other langs
}
if (language.empty()) {
auto w = open_db("quest", db_codepage);
if (aim2_available()) { if (aim2_available()) {
w.m2_ = &open_aim2_quest(); dw.m2 = db2{aim2_game_dir / "data" / "db"}.open().to_map(cp);
} }
return w;
} else {
auto w = open_db("quest_" + language, db_codepage);
if (aim2_available()) {
w.m2_ = &open_aim2_quest();
}
return w;
} }
return dw;
} }
db2::files::db2_internal &open_aim2_db() { auto &quest() {
// check if it's possible to use utf8/16 in aim game
// | set codepages here until we fix or implement unicode
// probably not possible, so use default codepages
if (qw.empty()) {
// TODO: maybe add vanilla db into translations repository as well?
prepare_languages();
}
return qw;
}
const auto &open_aim2_db() {
if (!aim2_available()) { if (!aim2_available()) {
throw std::runtime_error{"aim2 is not available, setup it first"}; throw std::runtime_error{"aim2 is not available, setup it first"};
} }
static auto m2 = db2{aim2_game_dir / "data" / "db"}.open().to_map(1251); return db().m2;
return m2; }
void prepare_languages() {
auto trdirname = "translations";
auto trdir = get_mod_dir().parent_path() / trdirname;
primitives::Command c;
c.push_back("git");
if (!fs::exists(trdir)) {
c.working_directory = get_mod_dir().parent_path();
c.push_back("clone");
c.push_back("https://github.com/aimrebirth/translations");
c.push_back(trdirname);
} else {
c.working_directory = trdir;
c.push_back("pull");
c.push_back("origin");
c.push_back("master");
}
run_command(c);
for (auto &&p : fs::directory_iterator{trdir / "aim1"}) {
if (!fs::is_regular_file(p) || p.path().extension() != ".json") {
continue;
}
auto s = split_string(p.path().stem().string(), "_");
auto lang = std::format("{}_{}", s.at(1), s.at(2));
qw[lang].m.load_from_json(p);
qw[lang].codepage = code_pages.at(s.at(1));
auto m2fn = trdir / "aim2" / p.path().filename();
if (fs::exists(m2fn)) {
qw[lang].m2.load_from_json(m2fn);
}
}
} }
private: private:
@ -483,13 +532,6 @@ private:
fs::copy_file(from, to, fs::copy_options::overwrite_existing); fs::copy_file(from, to, fs::copy_options::overwrite_existing);
} }
} }
db2::files::db2_internal &open_aim2_quest() {
if (!aim2_available()) {
throw std::runtime_error{"aim2 is not available, setup it first"};
}
static auto m2 = db2{aim2_game_dir / "data" / "quest"}.open().to_map(1251);
return m2;
}
bool aim2_available() const { bool aim2_available() const {
return !aim2_game_dir.empty(); return !aim2_game_dir.empty();
} }
@ -871,7 +913,8 @@ FF D7 ; call edi
game_dir = fs::absolute(game_dir).lexically_normal(); game_dir = fs::absolute(game_dir).lexically_normal();
} }
void detect_tools() { void detect_tools() {
//check_in_path("git"); // for languages/translations support
check_in_path("git");
// also --self-upgrade? // also --self-upgrade?
check_in_path("sw"); check_in_path("sw");
} }

View file

@ -30,7 +30,7 @@ static const std::map<std::string, int> code_pages
{ "en", 0 }, { "en", 0 },
{ "cz", 1250 }, { "cz", 1250 },
{ "ru", 1251 }, { "ru", 1251 },
{ "ge", 1252 }, { "de", 1252 },
{ "fr", 1252 }, { "fr", 1252 },
{ "et", 1257 }, { "et", 1257 },
}; };

View file

@ -186,6 +186,9 @@ struct db2 {
auto &operator[](this auto &&d, const std::string &s) { auto &operator[](this auto &&d, const std::string &s) {
return d.m[s]; return d.m[s];
} }
auto &at(this auto &&d, const std::string &s) {
return d.m.at(s);
}
auto to_json() const { auto to_json() const {
nlohmann::json ja; nlohmann::json ja;
for (auto &&[tn,t] : m) { for (auto &&[tn,t] : m) {
@ -222,7 +225,7 @@ struct db2 {
} }
} }
} }
void save(const path &fn, int codepage) { auto save(const path &fn, int codepage) {
auto s_to_char20 = [&](char20 &dst, const std::string &in, int codepage) { auto s_to_char20 = [&](char20 &dst, const std::string &in, int codepage) {
auto s = utf8_to_dbstr(in, codepage); auto s = utf8_to_dbstr(in, codepage);
if (s.size() + 1 > sizeof(char20)) { if (s.size() + 1 > sizeof(char20)) {
@ -291,9 +294,15 @@ struct db2 {
++table_id; ++table_id;
} }
write_file(path{fn} += ".tab", tabv.d); std::set<path> files;
write_file(path{fn} += ".ind", indv.d); auto f = [&](auto &&fn, auto &&d) {
write_file(path{fn} += ".dat", datv.d); write_file(fn, d);
files.insert(fn);
};
f(path{fn} += ".tab", tabv.d);
f(path{fn} += ".ind", indv.d);
f(path{fn} += ".dat", datv.d);
return files;
} }
}; };