Improve gui asset list + add asset viewer

This commit is contained in:
fed 2023-07-15 04:54:54 +02:00
parent 7a82426692
commit 91f4444a67
15 changed files with 6601 additions and 1932 deletions

View File

@ -62,7 +62,7 @@ namespace discord
}
const auto key = utils::string::va("PRESENCE_SP_%s", mapname);
if (game::DB_XAssetExists(game::ASSET_TYPE_LOCALIZE, key) && !game::DB_IsXAssetDefault(game::ASSET_TYPE_LOCALIZE, key))
if (game::DB_XAssetExists(game::ASSET_TYPE_LOCALIZE_ENTRY, key) && !game::DB_IsXAssetDefault(game::ASSET_TYPE_LOCALIZE_ENTRY, key))
{
mapname = game::UI_SafeTranslateString(key);
}

View File

@ -458,12 +458,12 @@ namespace fastfiles
void reallocate_asset_pools()
{
reallocate_xmodel_pool();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XMODELSURFS, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XMODEL_SURFS, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOADED_SOUND, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_XANIM, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOCALIZE, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SNDCURVE, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_LOCALIZE_ENTRY, 2>();
reallocate_asset_pool_multiplier<game::ASSET_TYPE_SOUND_CURVE, 2>();
}
void add_custom_level_load_zone(game::LevelLoad* load, const std::string& name, const size_t size_est)
@ -633,6 +633,39 @@ namespace fastfiles
}), &callback, includeOverride);
}
void enum_asset_entries(const game::XAssetType type, const std::function<void(game::XAssetEntry*)>& callback, bool include_override)
{
constexpr auto max_asset_count = 0x25D78;
auto hash = &game::db_hashTable[0];
for (auto c = 0; c < max_asset_count; c++)
{
for (auto i = *hash; i; )
{
const auto entry = &game::g_assetEntryPool[i];
if (entry->asset.type == type)
{
callback(entry);
if (include_override && entry->nextOverride)
{
auto next_ovveride = entry->nextOverride;
while (next_ovveride)
{
const auto override = &game::g_assetEntryPool[next_ovveride];
callback(override);
next_ovveride = override->nextOverride;
}
}
}
i = entry->nextHash;
}
++hash;
}
}
std::string get_current_fastfile()
{
std::string fastfile_copy;

View File

@ -6,6 +6,8 @@
namespace fastfiles
{
void enum_assets(const game::XAssetType type, const std::function<void(game::XAssetHeader)>& callback, const bool includeOverride);
void enum_asset_entries(const game::XAssetType type, const std::function<void(game::XAssetEntry*)>& callback, bool include_override);
std::string get_current_fastfile();
bool exists(const std::string& zone);

View File

@ -250,7 +250,7 @@ namespace fonts
}
const auto name = params.get(1);
const auto ttf = game::DB_FindXAssetHeader(game::XAssetType::ASSET_TYPE_TTF, name, false).ttf;
const auto ttf = game::DB_FindXAssetHeader(game::XAssetType::ASSET_TYPE_TTF, name, false).ttfDef;
if (ttf == nullptr)
{
console::error("Font does not exist\n");
@ -258,7 +258,7 @@ namespace fonts
}
const auto path = utils::string::va("dumps/%s", ttf->name);
utils::io::write_file(path, std::string(ttf->buffer, ttf->len), false);
utils::io::write_file(path, std::string(ttf->file, ttf->fileLen), false);
console::info("Dumped to %s", path);
});
}

View File

@ -8,6 +8,7 @@
#include "component/command.hpp"
#include "component/fastfiles.hpp"
#include "gui.hpp"
#include "asset_list.hpp"
#include <utils/string.hpp>
#include <utils/hook.hpp>
@ -19,12 +20,31 @@ namespace gui::asset_list
bool shown_assets[game::XAssetType::ASSET_TYPE_COUNT]{};
std::string asset_type_filter;
std::string assets_name_filter[game::XAssetType::ASSET_TYPE_COUNT];
std::string zone_name_filter[game::XAssetType::ASSET_TYPE_COUNT];
std::unordered_map<game::XAssetType, std::function<void(const std::string&)>> asset_view_callbacks;
void render_window()
{
static auto* enabled = &gui::enabled_menus["asset_list"];
ImGui::Begin("Asset list", enabled);
static bool show_asset_zone = true;
if (ImGui::TreeNode("loaded zones"))
{
for (auto i = 1u; i <= *game::g_zoneCount; i++)
{
if (ImGui::Button(game::g_zones[i].name))
{
gui::copy_to_clipboard(game::g_zones[i].name);
}
}
ImGui::TreePop();
}
ImGui::Checkbox("show asset zone", &show_asset_zone);
ImGui::InputText("asset type", &asset_type_filter);
ImGui::BeginChild("asset type list");
@ -33,7 +53,7 @@ namespace gui::asset_list
const auto name = game::g_assetNames[i];
const auto type = static_cast<game::XAssetType>(i);
if (utils::string::strstr_lower(name, asset_type_filter.data()))
if (asset_type_filter.size() == 0 || utils::string::strstr_lower(name, asset_type_filter.data()))
{
ImGui::Checkbox(name, &shown_assets[type]);
}
@ -55,25 +75,95 @@ namespace gui::asset_list
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
ImGui::Begin(name, &shown_assets[type]);
static bool default_only[game::ASSET_TYPE_COUNT] = {};
static int asset_count[game::ASSET_TYPE_COUNT] = {};
static bool disabled_zones[game::ASSET_TYPE_COUNT][0x100] = {};
ImGui::Text("count: %i / %i", asset_count[type], game::g_poolSize[type]);
ImGui::InputText("asset name", &assets_name_filter[type]);
if (ImGui::InputText("zone name", &zone_name_filter[type]))
{
for (auto zone = 0u; zone <= *game::g_zoneCount; zone++)
{
const auto zone_name = game::g_zones[zone].name;
disabled_zones[type][zone] = !utils::string::strstr_lower(zone_name, zone_name_filter[type].data());
}
}
const auto should_add_view_btn = asset_view_callbacks.contains(type);
ImGui::Checkbox("default assets only", &default_only[type]);
ImGui::BeginChild("assets list");
size_t asset_num{};
fastfiles::enum_assets(type, [type, &asset_num](const game::XAssetHeader header)
asset_count[type] = 0;
fastfiles::enum_asset_entries(type, [&](const game::XAssetEntry* entry)
{
const auto asset = game::XAsset{type, header};
asset_count[type]++;
const auto asset = entry->asset;
auto asset_name = game::DB_GetXAssetName(&asset);
if (asset_name[0] == '\0')
{
asset_name = utils::string::va("__%i", asset_num);
return;
}
if (utils::string::strstr_lower(asset_name, assets_name_filter[type].data()) && ImGui::Button(asset_name))
if (disabled_zones[type][entry->zoneIndex])
{
gui::copy_to_clipboard(asset_name);
return;
}
asset_num++;
const auto is_default = entry->zoneIndex == 0;
if (default_only[type] && !is_default)
{
return;
}
if (is_default)
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.43f, 0.15f, 0.15f, 1.f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.98f, 0.26f, 0.26f, 1.f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.98f, 0.06f, 0.06f, 1.f));
}
if (utils::string::strstr_lower(asset_name, assets_name_filter[type].data()))
{
if (show_asset_zone)
{
if (entry->zoneIndex > 0)
{
ImGui::Text(game::g_zones[entry->zoneIndex].name);
}
else
{
ImGui::Text("default");
}
ImGui::SameLine();
}
if (ImGui::Button(asset_name))
{
gui::copy_to_clipboard(asset_name);
}
if (should_add_view_btn)
{
ImGui::SameLine();
ImGui::PushID(asset_count[type]);
if (ImGui::Button("view"))
{
asset_view_callbacks.at(type)(asset_name);
}
ImGui::PopID();
}
}
if (is_default)
{
ImGui::PopStyleColor(3);
}
}, true);
ImGui::EndChild();
@ -82,6 +172,11 @@ namespace gui::asset_list
}
}
void add_asset_view_callback(game::XAssetType type, const std::function<void(const std::string&)>& callback)
{
asset_view_callbacks.insert(std::make_pair(type, callback));
}
class component final : public component_interface
{
public:

View File

@ -0,0 +1,8 @@
#pragma once
#include "game/structs.hpp"
namespace gui::asset_list
{
void add_asset_view_callback(game::XAssetType, const std::function<void(const std::string&)>& callback);
}

View File

@ -0,0 +1,152 @@
#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 "../gui.hpp"
#include "../asset_list.hpp"
#include <utils/string.hpp>
#include <utils/hook.hpp>
namespace gui::asset_list::assets::material
{
namespace
{
std::unordered_set<std::string> opened_assets;
std::unordered_map<unsigned char, std::string> image_type_names =
{
{0x0, "2D"},
{0x1, "FUNCTION"},
{0x2, "COLOR_MAP"},
{0x3, "DETAIL_MAP"},
{0x4, "UNUSED_2"},
{0x5, "NORMAL_MAP"},
{0x6, "UNUSED_3"},
{0x7, "UNUSED_4"},
{0x8, "SPECULAR_MAP"},
{0x9, "UNUSED_5"},
{0xA, "OCEANFLOW_DISPLACEMENT_MAP"},
{0xB, "WATER_MAP"},
{0xC, "OCEAN_DISPLACEMENT_MAP"},
{0xD, "DISPLACEMENT_MAP"},
{0xE, "PARALLAX_MAP"},
};
void add_material_view(const std::string& name)
{
opened_assets.insert(name);
}
std::string get_image_type_name(unsigned char type)
{
if (!image_type_names.contains(type))
{
return "UNKNOWN";
}
return image_type_names[type];
}
bool draw_material_window(const std::string& name)
{
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_MATERIAL, name.data(), 0).material;
if (asset == nullptr)
{
return false;
}
auto is_open = true;
if (ImGui::Begin(name.data(), &is_open))
{
ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
if (ImGui::TreeNode("textures"))
{
for (auto i = 0; i < asset->textureCount; i++)
{
if (asset->textureTable && asset->textureTable->u.image && asset->textureTable->u.image->texture.shaderView)
{
const auto type_name = get_image_type_name(asset->textureTable[i].semantic);
ImGui::Text("%s", type_name.data());
ImGui::SameLine();
if (ImGui::Button(asset->textureTable[i].u.image->name))
{
gui::copy_to_clipboard(asset->textureTable->u.image->name);
}
const auto width = asset->textureTable[i].u.image->width;
const auto height = asset->textureTable[i].u.image->height;
const auto ratio = static_cast<float>(width) / static_cast<float>(height);
constexpr auto size = 200.f;
ImGui::Image(asset->textureTable[i].u.image->texture.shaderView,
ImVec2(size * ratio, size)
);
}
}
ImGui::TreePop();
}
#define DRAW_ASSET_PROPERTY(__name__, __fmt__) \
ImGui::Text(#__name__ ": " __fmt__, asset->__name__); \
#define DRAW_ASSET_PROPERTY_COPY(__name__) \
ImGui::Text(#__name__ ": "); \
ImGui::SameLine(); \
if (ImGui::Button(asset->__name__)) \
{ \
gui::copy_to_clipboard(asset->__name__); \
} \
DRAW_ASSET_PROPERTY_COPY(name);
DRAW_ASSET_PROPERTY_COPY(techniqueSet->name);
DRAW_ASSET_PROPERTY(textureCount, "%i");
DRAW_ASSET_PROPERTY(constantCount, "%i");
DRAW_ASSET_PROPERTY(stateBitsCount, "%i");
DRAW_ASSET_PROPERTY(stateFlags, "%i");
DRAW_ASSET_PROPERTY(cameraRegion, "%i");
DRAW_ASSET_PROPERTY(materialType, "%i");
DRAW_ASSET_PROPERTY(layerCount, "%i");
DRAW_ASSET_PROPERTY(assetFlags, "%X");
}
ImGui::End();
return is_open;
}
void draw_materials()
{
for (auto i = opened_assets.begin(); i != opened_assets.end(); )
{
if (!draw_material_window(*i))
{
i = opened_assets.erase(i);
}
else
{
++i;
}
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
gui::asset_list::add_asset_view_callback(game::ASSET_TYPE_MATERIAL, add_material_view);
gui::register_callback(draw_materials, false);
}
};
}
REGISTER_COMPONENT(gui::asset_list::assets::material::component)

View File

@ -307,6 +307,14 @@ namespace gui
}, always);
}
void register_callback(const std::function<void()>& callback, bool always)
{
on_frame([=]
{
callback();
}, always);
}
class component final : public component_interface
{
public:

View File

@ -23,4 +23,6 @@ namespace gui
void register_menu(const std::string& name, const std::string& title,
const std::function<void()>& callback, bool always = false);
void register_callback(const std::function<void()>& callback, bool always = false);
}

View File

@ -73,14 +73,14 @@ namespace mapents
fastfiles::enum_assets(game::ASSET_TYPE_MAP_ENTS, [](game::XAssetHeader header)
{
if (header.mapents == nullptr)
if (header.mapEnts == nullptr)
{
console::info("Failed to dump mapents\n");
return;
}
const auto dest = utils::string::va("dumps/%s.ents", header.mapents->name);
const auto str = std::string(header.mapents->entityString, header.mapents->numEntityChars);
const auto dest = utils::string::va("dumps/%s.ents", header.mapEnts->name);
const auto str = std::string(header.mapEnts->entityString, header.mapEnts->numEntityChars);
const auto data = replace_mapents_keys(str);
utils::io::write_file(dest, data, false);
console::info("Mapents dumped to %s\n", dest);

View File

@ -17,8 +17,8 @@ namespace menus
{
game::UI_AddMenuList(context, menu_list, a3);
if (game::DB_XAssetExists(game::ASSET_TYPE_MENUFILE, patch_menu_list_name) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_MENUFILE, patch_menu_list_name))
if (game::DB_XAssetExists(game::ASSET_TYPE_MENULIST, patch_menu_list_name) &&
!game::DB_IsXAssetDefault(game::ASSET_TYPE_MENULIST, patch_menu_list_name))
{
const auto patch_code_list = game::UI_LoadMenus(patch_menu_list_name);
game::UI_AddMenuList(context, patch_code_list, a3);

6271
src/client/game/assets.hpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -296,6 +296,9 @@ namespace game
WEAK symbol<DB_FileSysInterface*> g_fileSystem{0x1420B27E8};
WEAK symbol<int> db_hashTable{0x142250000};
WEAK symbol<XAssetEntry> g_assetEntryPool{0x143CBB140};
WEAK symbol<map_t> maps{0x14097EE90};
WEAK symbol<language_values> languages{0x140BF9740};

View File

@ -228,7 +228,7 @@ namespace utils::string
while (*a_ != '\0' && *b_ != '\0')
{
if (std::tolower(*a_) == std::tolower(*b_))
if (*b_ == '*' || std::tolower(*a_) == std::tolower(*b_))
{
b_++;
}