From a55f5874af414648c98b437cdb9e13a1bb6b5c5b Mon Sep 17 00:00:00 2001 From: m Date: Sat, 25 Jan 2025 20:54:59 -0600 Subject: [PATCH] map ents patch --- src/client/component/mapents.cpp | 212 +++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 src/client/component/mapents.cpp diff --git a/src/client/component/mapents.cpp b/src/client/component/mapents.cpp new file mode 100644 index 0000000..498b92f --- /dev/null +++ b/src/client/component/mapents.cpp @@ -0,0 +1,212 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "console.hpp" +#include "filesystem.hpp" + +#include +#include + +#include "gsc/script_loading.hpp" + +namespace mapents +{ + namespace + { + std::optional parse_mapents(const std::string& source) + { + std::string out_buffer{}; + + const auto lines = utils::string::split(source, '\n'); + auto in_map_ent = false; + auto empty = false; + auto in_comment = false; + + for (auto i = 0; i < lines.size(); i++) + { + auto line_num = i+1; + auto line = lines[i]; + if (line.ends_with('\r')) + { + line.pop_back(); + } + + if (line.starts_with("/*")) + { + in_comment = true; + continue; + } + + if (line.ends_with("*/")) + { + in_comment = false; + continue; + } + + if (in_comment) + { + continue; + } + + if (line.starts_with("//")) + { + continue; + } + + if (line[0] == '{' && !in_map_ent) + { + in_map_ent = true; + out_buffer.append("{\n"); + continue; + } + + if (line[0] == '{' && in_map_ent) + { + console::error("[map_ents parser] Unexpected '{' on line %i\n", line_num); + return {}; + } + + if (line[0] == '}' && in_map_ent) + { + if (empty) + { + out_buffer.append("\n}\n"); + } + else if (i < static_cast(lines.size()) - 1) + { + out_buffer.append("}\n"); + } + else + { + out_buffer.append("}\0"); + } + + in_map_ent = false; + continue; + } + + if (line[0] == '}' && !in_map_ent) + { + console::error("[map_ents parser] Unexpected '}' on line %i\n", line_num); + return {}; + } + + std::regex expr(R"~((.+) "(.*)")~"); + std::smatch match{}; + if (!std::regex_search(line, match, expr) && !line.empty()) + { + console::warn("[map_ents parser] Failed to parse line %i (%s)\n", line_num, line.data()); + continue; + } + + auto key = utils::string::to_lower(match[1].str()); + const auto value = match[2].str(); + + if (key.size() <= 0) + { + console::warn("[map_ents parser] Invalid key ('%s') on line %i (%s)\n", key.data(), line_num, line.data()); + continue; + } + + if (value.size() <= 0) + { + continue; + } + + empty = false; + + if (utils::string::is_numeric(key) || key.size() < 3 || !key.starts_with("\"") || !key.ends_with("\"")) + { + out_buffer.append(line); + out_buffer.append("\n"); + continue; + } + + const auto key_ = key.substr(1, key.size() - 2); + const auto id = gsc::gsc_ctx->token_id(key_); + if (id == 0) + { + console::warn("[map_ents parser] Key '%s' not found, on line %i (%s)\n", key_.data(), line_num, line.data()); + continue; + } + + out_buffer.append(utils::string::va("%i \"%s\"\n", id, value.data())); + } + + return {out_buffer}; + } + + std::string raw_ents; + bool load_raw_mapents() + { + auto mapents_name = utils::string::va("%s.ents", **reinterpret_cast(SELECT_VALUE(0x0, 0x1479E6940))); + if (filesystem::exists(mapents_name)) + { + try + { + console::info("Reading raw ents file \"%s\"\n", mapents_name); + raw_ents = filesystem::read_file(mapents_name); + if (!raw_ents.empty()) + { + return true; + } + } + catch (const std::exception& ex) + { + console::error("Failed to read raw ents file \"%s\"\n%s\n", mapents_name, ex.what()); + } + } + return false; + } + + std::string entity_string; + const char* cm_entity_string_stub() + { + const char* ents = nullptr; + if (load_raw_mapents()) + { + ents = raw_ents.data(); + } + else + { + if (!entity_string.empty()) + { + return entity_string.data(); + } + + ents = utils::hook::invoke(SELECT_VALUE(0x0, 0x1403A1F30)); + } + + const auto parsed = parse_mapents(ents); + if (parsed.has_value()) + { + entity_string = parsed.value(); + return entity_string.data(); + } + else + { + return ents; + } + } + + void cm_unload_stub(void* clip_map) + { + entity_string.clear(); + raw_ents.clear(); + utils::hook::invoke(SELECT_VALUE(0x0, 0x1403A1ED0), clip_map); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + utils::hook::call(SELECT_VALUE(0x0, 0x1402F5A54), cm_entity_string_stub); + utils::hook::call(SELECT_VALUE(0x0, 0x14026BF54), cm_unload_stub); + } + }; +} + +REGISTER_COMPONENT(mapents::component)