h1-mod/src/client/component/materials.cpp

238 lines
6.2 KiB
C++
Raw Normal View History

2022-03-16 00:27:49 +01:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "materials.hpp"
#include "console.hpp"
2022-04-08 16:18:00 +02:00
#include "filesystem.hpp"
2022-03-16 00:27:49 +01:00
#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 material_register_handle_hook;
2022-04-08 16:18:00 +02:00
utils::hook::detour db_get_material_index_hook;
2023-01-11 10:06:24 -06:00
utils::hook::detour material_compare_hook;
2022-03-16 00:27:49 +01:00
struct material_data_t
{
std::unordered_map<std::string, game::Material*> materials;
std::unordered_map<std::string, std::string> images;
};
2022-03-17 23:56:15 +01:00
char constant_table[0x20] = {};
2022-03-16 00:27:49 +01:00
utils::concurrency::container<material_data_t> material_data;
game::GfxImage* setup_image(game::GfxImage* image, const utils::image& raw_image)
{
image->imageFormat = 0x1000003;
image->resourceSize = -1;
D3D11_SUBRESOURCE_DATA data{};
data.SysMemPitch = raw_image.get_width() * 4;
data.SysMemSlicePitch = data.SysMemPitch * raw_image.get_height();
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, &data);
return image;
}
game::Material* create_material(const std::string& name, const std::string& data)
{
2022-03-28 02:21:24 +02:00
const auto white = material_register_handle_hook.invoke<game::Material*>("white");
2022-03-16 00:27:49 +01:00
const auto material = utils::memory::get_allocator()->allocate<game::Material>();
const auto texture_table = utils::memory::get_allocator()->allocate<game::MaterialTextureDef>();
const auto image = utils::memory::get_allocator()->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));
2022-03-17 23:56:15 +01:00
material->constantTable = &constant_table;
2022-03-16 00:27:49 +01:00
material->name = utils::memory::get_allocator()->duplicate_string(name);
image->name = material->name;
material->textureTable = texture_table;
material->textureTable->u.image = setup_image(image, data);
return material;
}
2022-04-08 16:18:00 +02:00
void free_material(game::Material* material)
{
material->textureTable->u.image->textures.___u0.map->Release();
material->textureTable->u.image->textures.shaderView->Release();
utils::memory::get_allocator()->free(material->textureTable->u.image);
utils::memory::get_allocator()->free(material->textureTable);
utils::memory::get_allocator()->free(material->name);
utils::memory::get_allocator()->free(material);
}
2022-03-16 00:27:49 +01:00
game::Material* load_material(const std::string& name)
{
return material_data.access<game::Material*>([&](material_data_t& data_) -> game::Material*
{
if (const auto i = data_.materials.find(name); i != data_.materials.end())
{
return i->second;
}
std::string data{};
if (const auto i = data_.images.find(name); i != data_.images.end())
{
data = i->second;
}
2022-04-08 16:18:00 +02:00
if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
2022-03-16 00:27:49 +01:00
{
2022-03-17 23:56:15 +01:00
data_.materials[name] = nullptr;
2022-03-16 00:27:49 +01:00
return nullptr;
}
const auto material = create_material(name, data);
data_.materials[name] = material;
return material;
});
}
game::Material* try_load_material(const std::string& name)
{
2022-03-16 01:56:00 +01:00
if (name == "white")
{
return nullptr;
}
2022-03-16 00:27:49 +01:00
try
{
return load_material(name);
}
catch (const std::exception& e)
{
console::error("Failed to load material %s: %s\n", name.data(), e.what());
}
return nullptr;
}
game::Material* material_register_handle_stub(const char* name)
{
auto result = try_load_material(name);
if (result == nullptr)
{
result = material_register_handle_hook.invoke<game::Material*>(name);
}
return result;
}
2022-03-17 23:56:15 +01:00
int db_material_streaming_fail_stub(game::Material* material)
2022-03-16 00:27:49 +01:00
{
2022-03-17 23:56:15 +01:00
if (material->constantTable == &constant_table)
2022-03-16 00:27:49 +01:00
{
2022-03-17 23:56:15 +01:00
return 0;
2022-03-16 00:27:49 +01:00
}
2022-03-17 23:56:15 +01:00
return db_material_streaming_fail_hook.invoke<int>(material);
2022-03-16 00:27:49 +01:00
}
2022-04-08 16:18:00 +02:00
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);
}
2023-01-11 10:06:24 -06:00
#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;
}
#endif
2022-03-16 00:27:49 +01:00
}
void add(const std::string& name, const std::string& data)
{
material_data.access([&](material_data_t& data_)
{
data_.images[name] = data;
});
}
2022-04-11 11:05:21 +02:00
bool exists(const std::string& name)
{
return material_data.access<bool>([&](material_data_t& data_)
{
return data_.images.find(name) != data_.images.end();
});
}
2022-04-08 16:18:00 +02:00
void clear()
{
material_data.access([&](material_data_t& data_)
{
for (auto& material : data_.materials)
{
if (material.second == nullptr)
{
continue;
}
free_material(material.second);
}
data_.materials.clear();
});
}
2022-03-16 00:27:49 +01:00
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi())
{
return;
}
material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub);
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);
2023-01-11 10:06:24 -06:00
#ifdef DEBUG
material_compare_hook.create(0x693D1E_b, material_compare_stub);
#endif
2022-03-16 00:27:49 +01:00
}
};
}
2022-05-28 16:45:44 +02:00
REGISTER_COMPONENT(materials::component)