diff --git a/examples/mods/aim1_community_fix/my_mod.cpp b/examples/mods/aim1_community_fix/my_mod.cpp index a625e6d..7fbbca0 100644 --- a/examples/mods/aim1_community_fix/my_mod.cpp +++ b/examples/mods/aim1_community_fix/my_mod.cpp @@ -37,6 +37,9 @@ deps: pub.lzwdgc.Polygon4.Tools.aim1.mod_maker-master // patch note: You should start new game after applying this mod. This is necessary for map changes // patch note: to became visible. // patch note: +// patch note: Changes from 0.0.4 +// patch note: fix Finder-2 textures +// patch note: // patch note: Changes from 0.0.3 // patch note: add locale suffix to all quest databases (localized strings). Example: quest_ru_RU.* // patch note: add en_US localization (quest database, quest_en_US.*) @@ -104,13 +107,7 @@ int main(int argc, char *argv[]) { #endif )); // patch note: add Finder-2 model and textures from aim2 game (lz) - // TODO: auto take models from aim2 - // TODO: auto fix models with mod_converter2 - mod.add_resource("MOD_GL_S3_PS_FINDER2"); - // TODO: copy data from aim2 db - mod.add_resource("TEX_GL_S3_PS_FINDER_2.TM"); - mod.add_resource("TEX_GL_S3_PS_FINDER_2_SPEC.TM"); - mod.add_resource("TEX_GL_S3_PS_FINDER_2_SPEC_1.TM"); + mod.copy_from_aim2("MOD_GL_S3_PS_FINDER2"); // patch note: set correct model for a plant (Streef) mod.patch("location6.mmo", 0x0005775D, 'R', 'F'); // patch note: fix 'TOV_POLYMER_PLATE' spawn (Streef) @@ -139,14 +136,10 @@ int main(int argc, char *argv[]) { // patch note: DB auto db = mod.db(); // patch note: set glider GL_S3_PS_FINDER2 model to MOD_GL_S3_PS_FINDER2 (lz) - db[u8"Глайдеры"]["GL_S3_PS_FINDER2"]["MODEL"] = "MOD_GL_S3_PS_FINDER2"; // patch note - maybe copy from finder1?: change MOD_GL_S3_PS_FINDER2 model radius to MOD_GL_S3_PS_FINDER1 radius (lz) - // patch note: copy MOD_GL_S3_PS_FINDER2 model radius from aim2 (lz) - db.copy_from_aim2(u8"Модели", "MOD_GL_S3_PS_FINDER2", "RADIUS"); - // patch note: copy MOD_GL_S3_PS_FINDER2 textures from aim2 (lz) - db.copy_from_aim2(u8"Текстуры", "TEX_GL_S3_PS_FINDER_2"); - db.copy_from_aim2(u8"Текстуры", "TEX_GL_S3_PS_FINDER_2_SPEC"); - db.copy_from_aim2(u8"Текстуры", "TEX_GL_S3_PS_FINDER_2_SPEC_1"); + db[u8"Глайдеры"]["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 textures data from aim2 (lz) // patch note: double gun for config CFG_NARGOON (double electro discharge) (lz) auto &tblcfg = db[u8"Конфигурации"]; tblcfg["CFG_NARGOON"]["HEAVYGUN1"] = "GUN_ELECTRO_DISCHARGER"; diff --git a/src/aim1_mod_maker/aim1_mod_maker.h b/src/aim1_mod_maker/aim1_mod_maker.h index d0a29df..ed018f1 100644 --- a/src/aim1_mod_maker/aim1_mod_maker.h +++ b/src/aim1_mod_maker/aim1_mod_maker.h @@ -74,7 +74,7 @@ struct aim_exe_v1_06_constants { struct mod_maker { struct db_wrapper { db2::files::db2_internal m; - db2::files::db2_internal m2; + db2::files::db2_internal *m2_{}; path fn; int codepage{1251}; bool written{}; @@ -94,16 +94,19 @@ struct mod_maker { auto &operator[](this auto &&d, const std::u8string &s) { return d.m[(const char *)s.c_str()]; } + auto &m2() { + return *m2_; + } 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 (auto it = m.find(key); it == m.end()) { throw std::runtime_error{err}; } }; - 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]; + 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) { auto check_val = [](auto &&m, const std::string &key, auto &&err) { @@ -111,9 +114,9 @@ struct mod_maker { throw std::runtime_error{err}; } }; - 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]; + 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]; } }; enum class file_type { @@ -363,7 +366,7 @@ struct mod_maker { auto db() { auto w = open_db("db", 1251); // always 1251 probably if (aim2_available()) { - w.m2 = db2{aim2_game_dir / "data" / "db", 1251}.open().to_map(); + w.m2_ = &open_aim2_db(); } return w; } @@ -398,7 +401,78 @@ struct mod_maker { } } + void copy_from_aim2(auto &&object) { + log("copying from aim2: {}", path{object}.filename().string()); + + auto ft = detect_file_type(object); + switch (ft) { + case file_type::model: { + auto p = aim2_game_dir / "data" / "aimmod.pak"; + unpak(p); + p = make_unpak_dir(p); + if (fs::exists(p / object)) { + p /= object; + } else { + p /= "data"; + p /= "models"; + p /= object; + if (!fs::exists(p)) { + throw std::runtime_error{std::format("aim2: model is not found: {}", p.string())}; + } + } + // TODO: this - fix add_resource()/find_real_filename() + //auto copied_fn = get_mod_dir() / object; + auto copied_fn = get_data_dir() / object; + fs::copy_file(p, copied_fn, fs::copy_options::overwrite_existing); + run_p4_tool("mod_converter2", copied_fn); + add_resource(object); + db().copy_from_aim2(u8"Модели", path{object}.stem().string()); + auto textures = read_lines(path{copied_fn} += ".textures.txt"); + auto &m2 = open_aim2_db(); + for (auto &&t : textures) { + path fn = std::get(m2[u8"Текстуры"][t]["FILENAME"]); + if (fn.empty()) { + throw std::runtime_error{"Can't find texture: "s + t}; + } + copy_from_aim2(fn); + } + break; + } + case file_type::tm: { + auto p = aim2_game_dir / "data" / "aimtex.pak"; + unpak(p); + p = make_unpak_dir(p); + if (fs::exists(p / object)) { + p /= object; + } else { + p /= "data"; + p /= "tm"; + p /= object; + if (!fs::exists(p)) { + throw std::runtime_error{std::format("aim2: model is not found: {}", p.string())}; + } + } + // TODO: this - fix add_resource()/find_real_filename() + // auto copied_fn = get_mod_dir() / object; + auto copied_fn = get_data_dir() / path{object}.filename().string(); + fs::copy_file(p, copied_fn, fs::copy_options::overwrite_existing); + add_resource(path{object}.filename()); + db().copy_from_aim2(u8"Текстуры", path{object}.stem().string()); + break; + } + default: + SW_UNIMPLEMENTED; + } + } + private: + db2::files::db2_internal &open_aim2_db() { + if (!aim2_available()) { + throw std::runtime_error{"aim2 is not available, setup it first"}; + } + static auto m2 = db2{aim2_game_dir / "data" / "db", 1251}.open().to_map(); + return m2; + } bool aim2_available() const { return !aim2_game_dir.empty(); } @@ -757,8 +831,10 @@ FF D7 ; call edi return; } if (fn.empty()) { + log("unpacking {}", p.string()); run_p4_tool("unpaker", p); } else { + log("unpacking {} from {}", fn.string(), p.string()); run_p4_tool("unpaker", p, fn); } }