-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
372 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
#include <std_include.hpp> | ||
#include "loader/component_loader.hpp" | ||
|
||
#include "game/game.hpp" | ||
#include "game/dvars.hpp" | ||
|
||
#include "component/scheduler.hpp" | ||
#include "component/command.hpp" | ||
#include "component/fastfiles.hpp" | ||
#include "component/gsc/script_loading.hpp" | ||
#include "../gui.hpp" | ||
#include "../asset_list.hpp" | ||
|
||
#include "utils/mapents.hpp" | ||
|
||
#include <utils/string.hpp> | ||
#include <utils/hook.hpp> | ||
#include <utils/concurrency.hpp> | ||
#include <utils/io.hpp> | ||
|
||
namespace gui::asset_list::mapents | ||
{ | ||
namespace | ||
{ | ||
using entity_t = std::vector<std::pair<std::string, std::string>>; | ||
|
||
struct mapents_t | ||
{ | ||
game::MapEnts* asset; | ||
std::string converted_mapents; | ||
std::thread parse_thread; | ||
std::atomic_bool is_parsing = false; | ||
std::atomic_bool done_parsing = false; | ||
}; | ||
|
||
std::unordered_set<std::string> temp_files; | ||
|
||
utils::concurrency::container<mapents_t, std::recursive_mutex> mapents; | ||
|
||
void parse_mapents(game::MapEnts* asset, mapents_t& data) | ||
{ | ||
data.is_parsing = true; | ||
data.asset = asset; | ||
const auto str_data = std::string{asset->entityString, static_cast<size_t>(asset->numEntityChars)}; | ||
data.parse_thread = std::thread([=] | ||
{ | ||
const auto new_data = ::mapents::parse(str_data, [](const std::uint16_t id) | ||
{ | ||
return gsc::gsc_ctx->token_name(id); | ||
}); | ||
|
||
std::string converted_mapents; | ||
for (const auto& entity : new_data.entities) | ||
{ | ||
converted_mapents.append("{\n"); | ||
const auto var_list = entity.get_var_list(); | ||
for (const auto& var : var_list) | ||
{ | ||
if (var.sl_string) | ||
{ | ||
converted_mapents.append( | ||
utils::string::va("0 \"%s\" \"%s\"\n", var.key.data(), var.value.data())); | ||
} | ||
else | ||
{ | ||
converted_mapents.append( | ||
utils::string::va("\"%s\" \"%s\"\n", var.key.data(), var.value.data())); | ||
} | ||
} | ||
converted_mapents.append("}\n"); | ||
} | ||
|
||
mapents.access([=](mapents_t& data) | ||
{ | ||
data.is_parsing = false; | ||
data.done_parsing = true; | ||
data.converted_mapents = converted_mapents; | ||
}); | ||
}); | ||
} | ||
|
||
void draw_entity_string(game::MapEnts* asset) | ||
{ | ||
ImGui::InputTextMultiline("entity_string", asset->entityString, asset->numEntityChars, | ||
ImGui::GetWindowSize(), ImGuiInputTextFlags_ReadOnly); | ||
} | ||
|
||
bool create_and_open_mapents_file(mapents_t& data) | ||
{ | ||
const auto current_path = std::filesystem::current_path().generic_string(); | ||
const std::string path = utils::string::va("%s\\h2-mod\\tmp\\%s", current_path.data(), data.asset->name); | ||
temp_files.insert(path); | ||
|
||
utils::io::write_file(path, data.converted_mapents, false); | ||
ShellExecuteA(nullptr, nullptr, path.data(), nullptr, nullptr, SW_SHOWNORMAL); | ||
|
||
return false; | ||
} | ||
|
||
bool draw_asset(game::MapEnts* asset) | ||
{ | ||
return mapents.access<bool>([&](mapents_t& data) | ||
{ | ||
if (!data.is_parsing && data.done_parsing) | ||
{ | ||
if (data.parse_thread.joinable()) | ||
{ | ||
data.parse_thread.join(); | ||
} | ||
else | ||
{ | ||
create_and_open_mapents_file(data); | ||
return false; | ||
} | ||
} | ||
|
||
if (!data.is_parsing && !data.done_parsing) | ||
{ | ||
parse_mapents(asset, data); | ||
} | ||
|
||
ImGui::Text("Parsing mapents..."); | ||
return true; | ||
}); | ||
} | ||
} | ||
|
||
class component final : public component_interface | ||
{ | ||
public: | ||
void post_unpack() override | ||
{ | ||
gui::asset_list::add_asset_view<game::MapEnts>(game::ASSET_TYPE_MAP_ENTS, draw_asset); | ||
} | ||
|
||
void pre_destroy() override | ||
{ | ||
for (const auto& file : temp_files) | ||
{ | ||
utils::io::remove_file(file); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
REGISTER_COMPONENT(gui::asset_list::mapents::component) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#include <std_include.hpp> | ||
|
||
#include "mapents.hpp" | ||
|
||
#include <utils/string.hpp> | ||
|
||
namespace mapents | ||
{ | ||
void mapents_entity::add_var(const spawn_var& var) | ||
{ | ||
this->vars.push_back(var); | ||
} | ||
|
||
std::string mapents_entity::get(const std::string& key) const | ||
{ | ||
for (const auto& var : this->vars) | ||
{ | ||
if (var.key == key) | ||
{ | ||
return var.value; | ||
} | ||
} | ||
|
||
return ""; | ||
} | ||
|
||
std::vector<spawn_var> mapents_entity::get_var_list() const | ||
{ | ||
return this->vars; | ||
} | ||
|
||
void mapents_entity::clear() | ||
{ | ||
this->vars.clear(); | ||
} | ||
|
||
mapents_list parse(const std::string& data, const token_name_callback& get_token_name) | ||
{ | ||
mapents_list list; | ||
mapents_entity current_entity; | ||
|
||
const auto lines = utils::string::split(data, '\n'); | ||
auto in_map_ent = false; | ||
auto in_comment = false; | ||
|
||
for (auto i = 0; i < lines.size(); i++) | ||
{ | ||
auto line = lines[i]; | ||
if (line.ends_with('\r')) | ||
{ | ||
line.pop_back(); | ||
} | ||
|
||
if (line.starts_with("/*") || line.ends_with("/*")) | ||
{ | ||
in_comment = true; | ||
continue; | ||
} | ||
|
||
if (line.starts_with("*/") || line.ends_with("*/")) | ||
{ | ||
in_comment = false; | ||
continue; | ||
} | ||
|
||
if (in_comment || line.starts_with("//") || line.empty()) | ||
{ | ||
continue; | ||
} | ||
|
||
if (line[0] == '{' && !in_map_ent) | ||
{ | ||
current_entity.clear(); | ||
in_map_ent = true; | ||
continue; | ||
} | ||
|
||
if (line[0] == '{' && in_map_ent) | ||
{ | ||
throw std::runtime_error(utils::string::va("Unexpected '{' on line %i", i)); | ||
} | ||
|
||
if (line[0] == '}' && in_map_ent) | ||
{ | ||
list.entities.push_back(current_entity); | ||
in_map_ent = false; | ||
continue; | ||
} | ||
|
||
if (line[0] == '}' && !in_map_ent) | ||
{ | ||
throw std::runtime_error(utils::string::va("Unexpected '}' on line %i", i)); | ||
} | ||
|
||
if (line[0] == '\n' || line[0] == '\0' || line[0] == '\n') | ||
{ | ||
continue; | ||
} | ||
|
||
spawn_var var{}; | ||
|
||
if (line.starts_with("0 \"")) | ||
{ | ||
std::regex expr(R"~(0 "(.+)" "(.*)")~"); | ||
std::smatch match{}; | ||
if (!std::regex_search(line, match, expr)) | ||
{ | ||
throw std::runtime_error(utils::string::va("Failed to parse line %i (%s)", i, line.data())); | ||
continue; | ||
} | ||
|
||
var.key = utils::string::to_lower(match[1].str()); | ||
var.value = match[2].str(); | ||
var.sl_string = true; | ||
} | ||
else | ||
{ | ||
std::regex expr(R"~((.+) "(.*)")~"); | ||
std::smatch match{}; | ||
if (!std::regex_search(line, match, expr)) | ||
{ | ||
throw std::runtime_error( | ||
utils::string::va("Failed to parse line %i (%s)", i, line.data())); | ||
continue; | ||
} | ||
|
||
var.key = utils::string::to_lower(match[1].str()); | ||
var.value = match[2].str(); | ||
|
||
if (utils::string::is_numeric(var.key) && !var.key.starts_with("\"") && !var.key.ends_with("\"")) | ||
{ | ||
var.key = get_token_name(static_cast<std::uint32_t>(std::atoi(var.key.data()))); | ||
} | ||
else if (var.key.starts_with("\"") && var.key.ends_with("\"") && var.key.size() >= 3) | ||
{ | ||
var.key = var.key.substr(1, var.key.size() - 2); | ||
} | ||
else | ||
{ | ||
throw std::runtime_error( | ||
utils::string::va("Invalid key ('%s') on line %i (%s)", var.key.data(), i, line.data())); | ||
continue; | ||
} | ||
} | ||
|
||
if (var.key.size() <= 0) | ||
{ | ||
throw std::runtime_error( | ||
utils::string::va("Invalid key ('%s') on line %i (%s)", var.key.data(), i, line.data())); | ||
continue; | ||
} | ||
|
||
if (var.value.size() <= 0) | ||
{ | ||
continue; | ||
} | ||
|
||
current_entity.add_var(var); | ||
} | ||
|
||
return list; | ||
} | ||
} |
Oops, something went wrong.