iw4x-client/src/Components/Modules/Materials.cpp

350 lines
8.5 KiB
C++
Raw Normal View History

2017-01-19 16:23:59 -05:00
#include "STDInclude.hpp"
namespace Components
{
int Materials::ImageNameLength;
Utils::Hook Materials::ImageVersionCheckHook;
2017-06-05 08:31:45 -04:00
std::vector<Game::GfxImage*> Materials::ImageTable;
2017-06-04 18:00:46 -04:00
std::vector<Game::Material*> Materials::MaterialTable;
2018-12-17 08:29:18 -05:00
Game::Material* Materials::Create(const std::string& name, Game::GfxImage* image)
2017-06-04 18:00:46 -04:00
{
Game::Material* material = Utils::Memory::GetAllocator()->allocate<Game::Material>();
Game::MaterialTextureDef* texture = Utils::Memory::GetAllocator()->allocate<Game::MaterialTextureDef>();
material->textureCount = 1;
material->textureTable = texture;
2018-05-09 08:33:52 -04:00
material->info.name = Utils::Memory::GetAllocator()->duplicateString(name);
material->info.sortKey = 0x22;
material->info.textureAtlasColumnCount = 1;
material->info.textureAtlasRowCount = 1;
2017-06-04 18:00:46 -04:00
for (int i = 0; i < 48; ++i)
2017-06-04 18:00:46 -04:00
{
if (i != 4) material->stateBitsEntry[i] = -1;
2017-06-04 18:00:46 -04:00
}
material->stateFlags = 3;
material->cameraRegion = 4;
material->techniqueSet = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_TECHNIQUE_SET, "2d").techniqueSet;
material->textureTable->nameHash = Game::R_HashString("colorMap");
material->textureTable->nameStart = 'c';
material->textureTable->nameEnd = 'p';
2018-05-09 08:33:52 -04:00
material->textureTable->samplerState = -30;
material->textureTable->u.image = image;
2017-06-04 18:00:46 -04:00
Game::Material* cursor = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "ui_cursor").material;
if (cursor)
2017-06-04 18:00:46 -04:00
{
2018-05-09 08:33:52 -04:00
material->stateBitsTable = cursor->stateBitsTable;
2017-06-04 18:00:46 -04:00
material->stateBitsCount = cursor->stateBitsCount;
}
Materials::MaterialTable.push_back(material);
return material;
}
void Materials::Delete(Game::Material* material, bool deleteImage)
2017-06-04 18:00:46 -04:00
{
if (!material) return;
if (deleteImage)
{
for (char i = 0; i < material->textureCount; ++i)
{
2018-05-09 08:33:52 -04:00
Materials::DeleteImage(material->textureTable[i].u.image);
}
}
2017-06-04 18:00:46 -04:00
Utils::Memory::GetAllocator()->free(material->textureTable);
2018-05-09 08:33:52 -04:00
Utils::Memory::GetAllocator()->free(material->info.name);
2017-06-04 18:00:46 -04:00
Utils::Memory::GetAllocator()->free(material);
2017-06-05 08:31:45 -04:00
auto mat = std::find(Materials::MaterialTable.begin(), Materials::MaterialTable.end(), material);
if (mat != Materials::MaterialTable.end())
{
Materials::MaterialTable.erase(mat);
}
2017-06-04 18:00:46 -04:00
}
2018-12-17 08:29:18 -05:00
Game::GfxImage* Materials::CreateImage(const std::string& name, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format)
{
Game::GfxImage* image = Utils::Memory::GetAllocator()->allocate<Game::GfxImage>();
image->name = Utils::Memory::GetAllocator()->duplicateString(name);
Game::Image_Setup(image, width, height, depth, flags, format);
2017-06-05 08:31:45 -04:00
Materials::ImageTable.push_back(image);
return image;
}
void Materials::DeleteImage(Game::GfxImage* image)
{
if (!image) return;
Game::Image_Release(image);
Utils::Memory::GetAllocator()->free(image->name);
Utils::Memory::GetAllocator()->free(image);
2017-06-05 08:31:45 -04:00
auto img = std::find(Materials::ImageTable.begin(), Materials::ImageTable.end(), image);
if (img != Materials::ImageTable.end())
{
Materials::ImageTable.erase(img);
}
}
2017-06-04 18:00:46 -04:00
void Materials::DeleteAll()
{
std::vector<Game::Material*> materials;
Utils::Merge(&materials, Materials::MaterialTable);
Materials::MaterialTable.clear();
for (auto& material : materials)
2017-06-04 18:00:46 -04:00
{
Materials::Delete(material);
}
2017-06-05 08:31:45 -04:00
std::vector<Game::GfxImage*> images;
Utils::Merge(&images, Materials::ImageTable);
Materials::ImageTable.clear();
for (auto& image : images)
{
Materials::DeleteImage(image);
}
2017-06-04 18:00:46 -04:00
}
bool Materials::IsValid(Game::Material* material)
{
if (!material || !material->textureCount || !material->textureTable) return false;
for (char i = 0; i < material->textureCount; ++i)
{
2018-05-09 08:33:52 -04:00
if (!material->textureTable[i].u.image || !material->textureTable[i].u.image->texture.map)
{
return false;
}
}
return true;
}
2017-01-19 16:23:59 -05:00
__declspec(naked) void Materials::ImageVersionCheck()
{
__asm
{
cmp eax, 9
je returnSafely
jmp Materials::ImageVersionCheckHook.original
returnSafely:
mov al, 1
add esp, 18h
retn
}
}
Game::Material* Materials::ResolveMaterial(const char* stringPtr)
{
const char* imagePtr = stringPtr + 4;
unsigned int length = static_cast<unsigned int>(stringPtr[3] & 0xFF);
if (strlen(imagePtr) >= length)
{
Materials::ImageNameLength = 4 + length;
std::string image(imagePtr, length);
auto* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material;
if(material == nullptr || material->techniqueSet == nullptr || material->techniqueSet->name == nullptr || strcmp(material->techniqueSet->name, "2d") != 0)
return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material;
return material;
2017-01-19 16:23:59 -05:00
}
Materials::ImageNameLength = 4;
return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material;
}
__declspec(naked) void Materials::PostDrawMaterialStub()
{
__asm
{
mov eax, Materials::ImageNameLength
add [esp + 30h], eax
mov eax, 5358FFh
jmp eax
}
}
__declspec(naked) void Materials::DrawMaterialStub()
{
__asm
{
2017-02-01 07:44:25 -05:00
push eax
pushad
2017-01-19 16:23:59 -05:00
push ecx
call Materials::ResolveMaterial
add esp, 4h
mov [esp + 20h], eax
2017-02-01 07:44:25 -05:00
popad
pop eax
push 5310F0h
retn
2017-01-19 16:23:59 -05:00
}
}
int Materials::WriteDeathMessageIcon(char* string, int offset, Game::Material* material)
{
if (!material)
{
material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material;
}
2018-05-09 08:33:52 -04:00
int length = strlen(material->info.name);
2017-01-19 16:23:59 -05:00
string[offset++] = static_cast<char>(length);
2018-05-09 08:33:52 -04:00
strncpy_s(string + offset, 1024 - offset, material->info.name, length);
2017-01-19 16:23:59 -05:00
return offset + length;
}
__declspec(naked) void Materials::DeathMessageStub()
{
__asm
{
2017-02-01 07:44:25 -05:00
push eax
pushad
2017-01-19 16:23:59 -05:00
push edx // Material
push eax // offset
push ecx // String
call Materials::WriteDeathMessageIcon
2017-02-01 07:44:25 -05:00
add esp, 0Ch
mov [esp + 20h], eax
2017-02-01 07:44:25 -05:00
popad
pop eax
2017-01-19 16:23:59 -05:00
2017-02-01 07:44:25 -05:00
add esp, 8h
2017-01-19 16:23:59 -05:00
retn
}
}
int Materials::FormatImagePath(char* buffer, size_t size, int, int, const char* image)
{
#if 0
if (Utils::String::StartsWith(image, "preview_"))
{
std::string newImage = image;
Utils::String::Replace(newImage, "preview_", "loadscreen_");
if (FileSystem::FileReader(fmt::sprintf("images/%s.iwi", newImage.data())).exists())
{
image = Utils::String::VA("%s", newImage.data());
}
}
#endif
return _snprintf_s(buffer, size, size, "images/%s.iwi", image);
}
int Materials::MaterialComparePrint(Game::Material* m1, Game::Material* m2)
{
//__debugbreak();
return Utils::Hook::Call<int(Game::Material*, Game::Material*)>(0x5235B0)(m1, m2);
}
2017-01-19 16:23:59 -05:00
#ifdef DEBUG
void Materials::DumpImageCfg(int, const char*, const char* material)
{
Materials::DumpImageCfgPath(0, nullptr, Utils::String::VA("images/%s.iwi", material));
}
void Materials::DumpImageCfgPath(int, const char*, const char* material)
{
FILE* fp = nullptr;
if (!fopen_s(&fp, "dump.cfg", "a") && fp)
{
fprintf(fp, "dumpraw %s\n", material);
fclose(fp);
}
}
#endif
Materials::Materials()
{
Materials::ImageNameLength = 7;
// Allow codo images
Materials::ImageVersionCheckHook.initialize(0x53A456, Materials::ImageVersionCheck, HOOK_CALL)->install();
// Fix material pointer exploit
Utils::Hook(0x534E0C, Materials::DrawMaterialStub, HOOK_CALL).install()->quick();
// Increment string pointer accordingly
Utils::Hook(0x5358FA, Materials::PostDrawMaterialStub, HOOK_JUMP).install()->quick();
// Adapt death message to IW5 material format
Utils::Hook(0x5A30D9, Materials::DeathMessageStub, HOOK_JUMP).install()->quick();
// Resolve preview images to loadscreens
Utils::Hook(0x53AC19, Materials::FormatImagePath, HOOK_CALL).install()->quick();
// Debug material comparison
Utils::Hook::Set<void*>(0x523894, Materials::MaterialComparePrint);
2017-01-19 16:23:59 -05:00
#ifdef DEBUG
if (Flags::HasFlag("dump"))
{
Utils::Hook(0x51F5AC, Materials::DumpImageCfg, HOOK_CALL).install()->quick();
Utils::Hook(0x51F4C4, Materials::DumpImageCfg, HOOK_CALL).install()->quick();
Utils::Hook(0x53AC62, Materials::DumpImageCfgPath, HOOK_CALL).install()->quick();
}
else
{
// Ignore missing images
Utils::Hook::Nop(0x51F5AC, 5);
Utils::Hook::Nop(0x51F4C4, 5);
}
#endif
2017-06-05 08:31:45 -04:00
Renderer::OnDeviceRecoveryBegin([]()
{
for (auto& image : Materials::ImageTable)
{
Game::Image_Release(image);
2018-05-09 08:33:52 -04:00
image->texture.map = nullptr;
2017-06-05 08:31:45 -04:00
}
});
/*Renderer::OnDeviceRecoveryEnd([]()
2017-06-05 08:31:45 -04:00
{
for (auto& image : Materials::ImageTable)
{
Utils::Hook::Call<void(void*)>(0x51F7B0)(image);
}
});*/
2017-01-19 16:23:59 -05:00
}
Materials::~Materials()
{
2017-06-04 18:00:46 -04:00
Materials::DeleteAll();
2017-01-19 16:23:59 -05:00
Materials::ImageVersionCheckHook.uninstall();
}
}