Add gui mapents viewer + fixes
This commit is contained in:
parent
a5d3b55681
commit
7f4eb242d9
@ -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)
|
||||
@ -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();
|
||||
|
||||
|
146
src/client/component/gui/assets/mapents.cpp
Normal file
146
src/client/component/gui/assets/mapents.cpp
Normal file
@ -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)
|
@ -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"))
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
163
src/client/utils/mapents.cpp
Normal file
163
src/client/utils/mapents.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
34
src/client/utils/mapents.hpp
Normal file
34
src/client/utils/mapents.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
namespace mapents
|
||||
{
|
||||
using token_name_callback = std::function<std::string(const std::uint32_t)>;
|
||||
using token_id_callback = std::function<std::uint32_t(const std::string&)>;
|
||||
|
||||
struct spawn_var
|
||||
{
|
||||
std::string key;
|
||||
std::string value;
|
||||
bool sl_string;
|
||||
};
|
||||
|
||||
class mapents_entity
|
||||
{
|
||||
public:
|
||||
void clear();
|
||||
void add_var(const spawn_var& var);
|
||||
|
||||
std::string get(const std::string& key) const;
|
||||
std::vector<spawn_var> get_var_list() const;
|
||||
|
||||
private:
|
||||
std::vector<spawn_var> vars;
|
||||
};
|
||||
|
||||
struct mapents_list
|
||||
{
|
||||
std::vector<mapents_entity> entities;
|
||||
};
|
||||
|
||||
mapents_list parse(const std::string& data, const token_name_callback& token_name);
|
||||
}
|
@ -242,4 +242,9 @@ namespace utils::string
|
||||
|
||||
return *b_ == '\0';
|
||||
}
|
||||
|
||||
bool is_numeric(const std::string& text)
|
||||
{
|
||||
return std::to_string(atoi(text.data())) == text;
|
||||
}
|
||||
}
|
||||
|
@ -105,4 +105,6 @@ namespace utils::string
|
||||
std::string truncate(const std::string& text, const size_t length, const std::string& end);
|
||||
|
||||
bool strstr_lower(const char* a, const char* b);
|
||||
|
||||
bool is_numeric(const std::string& text);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user