Improve gui asset list + add asset viewer
This commit is contained in:
parent
7a82426692
commit
91f4444a67
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
asset_num++;
|
||||
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:
|
||||
|
8
src/client/component/gui/asset_list.hpp
Normal file
8
src/client/component/gui/asset_list.hpp
Normal 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);
|
||||
}
|
152
src/client/component/gui/assets/material.cpp
Normal file
152
src/client/component/gui/assets/material.cpp
Normal 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)
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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
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
@ -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};
|
||||
|
@ -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_++;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user