Skip to content

Commit

Permalink
Add gui mapents viewer + fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
alicealys committed Jul 15, 2023
1 parent a5d3b55 commit 7f4eb24
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 4 deletions.
7 changes: 5 additions & 2 deletions src/client/component/gui/asset_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace gui::asset_list
void add_view_button(int id, game::XAssetType type, const char* name);

template <typename T>
void add_asset_view(game::XAssetType type, const std::function<void(T*)>& draw_callback)
void add_asset_view(game::XAssetType type, const std::function<bool(T*)>& draw_callback)
{
static std::unordered_set<std::string> opened_assets;
add_asset_view_callback(type, [](const std::string& name)
Expand All @@ -32,7 +32,10 @@ namespace gui::asset_list
auto is_open = true;
if (ImGui::Begin(name.data(), &is_open))
{
draw_callback(header);
if (!draw_callback(header))
{
is_open = false;
}
}
ImGui::End();

Expand Down
146 changes: 146 additions & 0 deletions src/client/component/gui/assets/mapents.cpp
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)
4 changes: 3 additions & 1 deletion src/client/component/gui/assets/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace gui::asset_list::material
return image_type_names[type];
}

void draw_material_window(game::Material* asset)
bool draw_material_window(game::Material* asset)
{
ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
if (ImGui::TreeNode("textures"))
Expand Down Expand Up @@ -99,6 +99,8 @@ namespace gui::asset_list::material
DRAW_ASSET_PROPERTY(materialType, "%i");
DRAW_ASSET_PROPERTY(layerCount, "%i");
DRAW_ASSET_PROPERTY(assetFlags, "%X");

return true;
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/client/component/gui/assets/xmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ namespace gui::asset_list::xmodel
}
}

void draw_xmodel_window(game::XModel* asset)
bool draw_xmodel_window(game::XModel* asset)
{
static bool flip_axis = false;
ImGui::Checkbox("flip axis", &flip_axis);
Expand Down Expand Up @@ -252,8 +252,17 @@ namespace gui::asset_list::xmodel

if (ImGui::TreeNode("surface materials"))
{
game::Material* prev_material = nullptr;

for (auto i = 0; i < asset->numsurfs; i++)
{
if (prev_material == asset->materialHandles[i])
{
continue;
}

prev_material = asset->materialHandles[i];

if (ImGui::Button(asset->materialHandles[i]->name))
{
gui::copy_to_clipboard(asset->materialHandles[i]->name);
Expand All @@ -275,11 +284,15 @@ namespace gui::asset_list::xmodel
{
gui::copy_to_clipboard(asset->compositeModels[i]->name);
}

gui::asset_list::add_view_button(i, game::ASSET_TYPE_XMODEL, asset->compositeModels[i]->name);
}

ImGui::TreePop();
}
}

return true;
}
}

Expand Down
163 changes: 163 additions & 0 deletions src/client/utils/mapents.cpp
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;
}
}
Loading

0 comments on commit 7f4eb24

Please sign in to comment.