h1-mod/src/client/component/materials.cpp
2023-12-19 16:15:20 +01:00

230 lines
6.0 KiB
C++

#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "materials.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/concurrency.hpp>
namespace materials
{
namespace
{
utils::hook::detour db_material_streaming_fail_hook;
utils::hook::detour db_get_material_index_hook;
#ifdef DEBUG
utils::hook::detour material_compare_hook;
utils::hook::detour set_pixel_texture_hook;
const game::dvar_t* debug_materials = nullptr;
#endif
char constant_table[0x20] = {};
int db_material_streaming_fail_stub(game::Material* material)
{
if (material->constantTable == &constant_table)
{
return 0;
}
return db_material_streaming_fail_hook.invoke<int>(material);
}
unsigned int db_get_material_index_stub(game::Material* material)
{
if (material->constantTable == &constant_table)
{
return 0;
}
return db_get_material_index_hook.invoke<unsigned int>(material);
}
#ifdef DEBUG
char material_compare_stub(unsigned int index_a, unsigned int index_b)
{
char result = 0;
__try
{
result = material_compare_hook.invoke<char>(index_a, index_b);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
const auto* material_a = utils::hook::invoke<game::Material*>(0x395FE0_b, index_a);
const auto* material_b = utils::hook::invoke<game::Material*>(0x395FE0_b, index_b);
console::error("Material_Compare: %s - %s (%d - %d)",
material_a->name, material_b->name, material_a->info.sortKey, material_b->info.sortKey);
}
return result;
}
void print_material(const game::Material* material)
{
if (!debug_materials || !debug_materials->current.enabled)
{
return;
}
console::debug("current material is \"%s\"\n", material->name);
}
void print_current_material_stub(utils::hook::assembler& a)
{
const auto loc_6AD59B = a.newLabel();
a.pushad64();
a.mov(rcx, r15);
a.call_aligned(print_material);
a.popad64();
a.cmp(byte_ptr(rbx), 5);
a.mov(rax, ptr(r15, 0x130));
a.jnz(loc_6AD59B);
a.nop(dword_ptr(rax, rax, 0x00000000));
a.jmp(0x6AD570_b);
a.bind(loc_6AD59B);
a.jmp(0x6AD59B_b);
}
void set_pixel_texture_stub(void* cmd_buf_state, unsigned int a2, const game::GfxImage* image)
{
if (!debug_materials || !debug_materials->current.enabled)
{
set_pixel_texture_hook.invoke<void>(cmd_buf_state, a2, image);
return;
}
if (image && image->name)
{
console::debug("set_pixel_texture_stub: \"%s\"\n", image->name);
}
else
{
console::error("set_pixel_texture_stub: texture has no name or is nullptr\n");
}
set_pixel_texture_hook.invoke<void>(cmd_buf_state, a2, image);
}
#endif
}
bool setup_material_image(game::Material* material, const std::string& data)
{
if (*game::d3d11_device == nullptr)
{
console::error("Tried to create texture while d3d11 device isn't initialized\n");
return false;
}
const auto image = material->textureTable->u.image;
image->imageFormat = 0x1000003;
image->resourceSize = -1;
auto raw_image = utils::image{data};
D3D11_SUBRESOURCE_DATA resource_data{};
resource_data.SysMemPitch = raw_image.get_width() * 4;
resource_data.SysMemSlicePitch = resource_data.SysMemPitch * raw_image.get_height();
resource_data.pSysMem = raw_image.get_buffer();
game::Image_Setup(image, raw_image.get_width(), raw_image.get_height(), image->depth, image->numElements,
image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, image->name, &resource_data);
return true;
}
game::Material* create_material(const std::string& name)
{
const auto white = game::Material_RegisterHandle("$white");
const auto material = utils::memory::allocate<game::Material>();
const auto texture_table = utils::memory::allocate<game::MaterialTextureDef>();
const auto image = utils::memory::allocate<game::GfxImage>();
std::memcpy(material, white, sizeof(game::Material));
std::memcpy(texture_table, white->textureTable, sizeof(game::MaterialTextureDef));
std::memcpy(image, white->textureTable->u.image, sizeof(game::GfxImage));
material->constantTable = &constant_table;
material->name = utils::memory::duplicate_string(name);
image->name = material->name;
image->textures.map = nullptr;
image->textures.shaderView = nullptr;
image->textures.shaderViewAlternate = nullptr;
texture_table->u.image = image;
material->textureTable = texture_table;
return material;
}
void free_material(game::Material* material)
{
const auto try_release = []<typename T>(T** resource)
{
if (*resource != nullptr)
{
(*resource)->Release();
*resource = nullptr;
}
};
try_release(&material->textureTable->u.image->textures.map);
try_release(&material->textureTable->u.image->textures.shaderView);
try_release(&material->textureTable->u.image->textures.shaderViewAlternate);
utils::memory::free(material->textureTable->u.image);
utils::memory::free(material->textureTable);
utils::memory::free(material->name);
utils::memory::free(material);
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi())
{
return;
}
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub);
db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub);
#ifdef DEBUG
if (!game::environment::is_sp())
{
material_compare_hook.create(0x693B90_b, material_compare_stub);
set_pixel_texture_hook.create(0x6B33E0_b, set_pixel_texture_stub);
utils::hook::jump(0x6AD55C_b, utils::hook::assemble(print_current_material_stub), true);
scheduler::once([]
{
debug_materials = dvars::register_bool("debug_materials", false, game::DVAR_FLAG_NONE, "Print current material and images");
}, scheduler::main);
}
#endif
}
};
}
REGISTER_COMPONENT(materials::component)