2017-01-16 11:42:50 -05:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
std::unordered_map<void*, IUnknown*> ModelSurfs::BufferMap;
|
|
|
|
std::unordered_map<std::string, Game::CModelAllocData*> ModelSurfs::AllocMap;
|
|
|
|
|
|
|
|
IUnknown* ModelSurfs::GetBuffer(void* buffer)
|
|
|
|
{
|
|
|
|
return ModelSurfs::BufferMap[buffer];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut)
|
|
|
|
{
|
|
|
|
*offsetOut = 0;
|
|
|
|
*bufferOut = ModelSurfs::BufferMap[buffer];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::CreateBuffers(Game::XModelSurfs* surfs)
|
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
for (int i = 0; i < surfs->numsurfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
Game::XSurface* surface = &surfs->surfs[i];
|
2017-01-16 11:42:50 -05:00
|
|
|
if (surface->zoneHandle == -1)
|
|
|
|
{
|
2017-01-18 14:49:19 -05:00
|
|
|
IDirect3DVertexBuffer9* vertexBuffer = nullptr;
|
|
|
|
IDirect3DIndexBuffer9* indexBuffer = nullptr;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
Game::Load_VertexBuffer(surface->verts0, &vertexBuffer, surface->vertCount * 32);
|
|
|
|
Game::Load_IndexBuffer(surface->triIndices, &indexBuffer, surface->triCount * 3);
|
|
|
|
|
2017-01-18 14:49:19 -05:00
|
|
|
if (vertexBuffer) ModelSurfs::BufferMap[surface->verts0] = vertexBuffer;
|
|
|
|
if (indexBuffer) ModelSurfs::BufferMap[surface->triIndices] = indexBuffer;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
Utils::Memory::Allocator allocator;
|
|
|
|
FileSystem::FileReader model(Utils::String::VA("models/%s", name.data()));
|
|
|
|
|
|
|
|
if (!model.exists())
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (Flags::HasFlag("dump"))
|
|
|
|
{
|
|
|
|
FILE* fp = nullptr;
|
|
|
|
if (!fopen_s(&fp, "dump.cfg", "a") && fp)
|
|
|
|
{
|
|
|
|
fprintf(fp, "dumpraw %s\n", model.getName().data());
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Logger::Error("Loading model %s failed!", name.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::CModelHeader header;
|
|
|
|
if (!model.read(&header, sizeof header))
|
|
|
|
{
|
|
|
|
Logger::Error("Reading header for model %s failed!", name.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header.version != 1)
|
|
|
|
{
|
|
|
|
Logger::Error("Model %s has an invalid version %d (should be 1)!", name.data(), header.version);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate section buffers
|
|
|
|
header.sectionHeader[Game::SECTION_MAIN].buffer = Utils::Memory::Allocate(header.sectionHeader[Game::SECTION_MAIN].size);
|
|
|
|
header.sectionHeader[Game::SECTION_INDEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_INDEX].size, 16);
|
|
|
|
header.sectionHeader[Game::SECTION_VERTEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_VERTEX].size, 16);
|
|
|
|
header.sectionHeader[Game::SECTION_FIXUP].buffer = allocator.allocateArray<char>(header.sectionHeader[Game::SECTION_FIXUP].size);
|
|
|
|
|
|
|
|
// Load section data
|
2017-01-27 08:43:52 -05:00
|
|
|
for (int i = 0; i < ARRAYSIZE(header.sectionHeader); ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
model.seek(header.sectionHeader[i].offset, Game::FS_SEEK_SET);
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!model.read(header.sectionHeader[i].buffer, header.sectionHeader[i].size))
|
|
|
|
{
|
|
|
|
Logger::Error("Reading section %d for model %s failed!", i, name.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fixup sections
|
|
|
|
unsigned int* fixups = reinterpret_cast<unsigned int*>(header.sectionHeader[Game::SECTION_FIXUP].buffer);
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
{
|
|
|
|
Game::CModelSectionHeader* section = &header.sectionHeader[i];
|
|
|
|
for (int j = section->fixupStart; j < section->fixupStart + section->fixupCount; ++j)
|
|
|
|
{
|
|
|
|
unsigned int fixup = fixups[j];
|
|
|
|
*reinterpret_cast<DWORD*>(reinterpret_cast<char*>(section->buffer) + (fixup >> 3)) += reinterpret_cast<DWORD>(header.sectionHeader[fixup & 3].buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store allocation data (not sure if this is correct)
|
|
|
|
Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray<Game::CModelAllocData>();
|
|
|
|
allocationData->mainArray = header.sectionHeader[Game::SECTION_MAIN].buffer;
|
|
|
|
allocationData->indexBuffer = header.sectionHeader[Game::SECTION_INDEX].buffer;
|
|
|
|
allocationData->vertexBuffer = header.sectionHeader[Game::SECTION_VERTEX].buffer;
|
|
|
|
|
|
|
|
AssertSize(Game::XSurface, 64);
|
|
|
|
Game::XModelSurfs* modelSurfs = reinterpret_cast<Game::XModelSurfs*>(allocationData->mainArray);
|
2018-05-09 08:33:52 -04:00
|
|
|
Game::XSurface* tempSurfaces = allocator.allocateArray<Game::XSurface>(modelSurfs->numsurfs);
|
|
|
|
char* surfaceData = reinterpret_cast<char*>(modelSurfs->surfs);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (ModelSurfs::AllocMap.find(modelSurfs->name) != ModelSurfs::AllocMap.end())
|
|
|
|
{
|
|
|
|
Game::CModelAllocData* allocData = ModelSurfs::AllocMap[modelSurfs->name];
|
|
|
|
|
|
|
|
if (allocData)
|
|
|
|
{
|
|
|
|
Utils::Memory::FreeAlign(allocData->indexBuffer);
|
|
|
|
Utils::Memory::FreeAlign(allocData->vertexBuffer);
|
|
|
|
Utils::Memory::Free(allocData->mainArray);
|
|
|
|
Utils::Memory::Free(allocData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ModelSurfs::AllocMap[modelSurfs->name] = allocationData;
|
|
|
|
*reinterpret_cast<void**>(reinterpret_cast<char*>(allocationData->mainArray) + 44) = allocationData;
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
for (int i = 0; i < modelSurfs->numsurfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
char* source = &surfaceData[i * 84];
|
|
|
|
|
|
|
|
std::memcpy(&tempSurfaces[i], source, 12);
|
|
|
|
std::memcpy(&tempSurfaces[i].triIndices, source + 16, 20);
|
|
|
|
std::memcpy(&tempSurfaces[i].vertListCount, source + 40, 8);
|
|
|
|
std::memcpy(&tempSurfaces[i].partBits, source + 52, 24);
|
|
|
|
tempSurfaces[i].zoneHandle = -1; // Fake handle for buffer interception
|
|
|
|
}
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
std::memcpy(surfaceData, tempSurfaces, 64 * modelSurfs->numsurfs);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
ModelSurfs::CreateBuffers(modelSurfs);
|
|
|
|
|
|
|
|
return modelSurfs;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ModelSurfs::LoadSurfaces(Game::XModel* model)
|
|
|
|
{
|
|
|
|
if (!model) return false;
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
short surfCount = 0;
|
|
|
|
|
|
|
|
for (char i = 0; i < model->numLods; ++i)
|
|
|
|
{
|
|
|
|
Game::XModelSurfs* surfs = model->lodInfo[i].modelSurfs;
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
if (!surfs->surfs)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
AssertOffset(Game::XModelLodInfo, partBits, 12);
|
|
|
|
Game::XModelSurfs* newSurfs = ModelSurfs::LoadXModelSurfaces(surfs->name);
|
|
|
|
if (!newSurfs) continue;
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
surfs->surfs = newSurfs->surfs;
|
|
|
|
surfs->numsurfs = newSurfs->numsurfs;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
model->lodInfo[i].surfs = newSurfs->surfs;
|
2017-01-16 11:42:50 -05:00
|
|
|
std::memcpy(&model->lodInfo[i].partBits, &newSurfs->partBits, 24);
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
short numSurfs = static_cast<short>(newSurfs->numsurfs);
|
2017-01-16 11:42:50 -05:00
|
|
|
model->lodInfo[i].numsurfs = numSurfs;
|
|
|
|
model->lodInfo[i].surfIndex = surfCount;
|
|
|
|
surfCount += numSurfs;
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header)
|
|
|
|
{
|
|
|
|
bool hasCustomSurface = false;
|
2018-05-09 08:33:52 -04:00
|
|
|
for (int i = 0; i < header.modelSurfs->numsurfs && header.modelSurfs->surfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
Game::XSurface* surface = &header.modelSurfs->surfs[i];
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (surface->zoneHandle == -1)
|
|
|
|
{
|
|
|
|
hasCustomSurface = true;
|
|
|
|
|
2017-01-18 09:18:25 -05:00
|
|
|
if (!ModelSurfs::BufferMap.empty())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-01-18 09:18:25 -05:00
|
|
|
auto buffer = ModelSurfs::BufferMap.find(surface->triIndices);
|
|
|
|
if (buffer != ModelSurfs::BufferMap.end())
|
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
if (buffer->second) buffer->second->Release();
|
2017-01-18 09:18:25 -05:00
|
|
|
ModelSurfs::BufferMap.erase(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = ModelSurfs::BufferMap.find(surface->verts0);
|
|
|
|
if (buffer != ModelSurfs::BufferMap.end())
|
|
|
|
{
|
2017-01-25 04:00:17 -05:00
|
|
|
if (buffer->second) buffer->second->Release();
|
2017-01-18 09:18:25 -05:00
|
|
|
ModelSurfs::BufferMap.erase(buffer);
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 09:18:25 -05:00
|
|
|
if (hasCustomSurface && !ModelSurfs::AllocMap.empty())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
auto allocData = ModelSurfs::AllocMap.find(header.modelSurfs->name);
|
2017-01-16 11:42:50 -05:00
|
|
|
if (allocData != ModelSurfs::AllocMap.end())
|
|
|
|
{
|
|
|
|
Utils::Memory::FreeAlign(allocData->second->indexBuffer);
|
|
|
|
Utils::Memory::FreeAlign(allocData->second->vertexBuffer);
|
|
|
|
Utils::Memory::Free(allocData->second->mainArray);
|
|
|
|
Utils::Memory::Free(allocData->second);
|
|
|
|
|
|
|
|
ModelSurfs::AllocMap.erase(allocData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::BeginRecover()
|
|
|
|
{
|
|
|
|
for (auto& buffer : ModelSurfs::BufferMap)
|
|
|
|
{
|
|
|
|
buffer.second->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
ModelSurfs::BufferMap.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::EndRecover()
|
|
|
|
{
|
2018-05-09 06:04:20 -04:00
|
|
|
Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, [](Game::XAssetHeader header, void* /*userdata*/)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
ModelSurfs::CreateBuffers(header.modelSurfs);
|
2017-01-16 11:42:50 -05:00
|
|
|
}, nullptr, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModelSurfs::XModelSurfsFixup(Game::XModel* model)
|
|
|
|
{
|
|
|
|
if (!ModelSurfs::LoadSurfaces(model))
|
|
|
|
{
|
|
|
|
Game::DB_XModelSurfsFixup(model);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void ModelSurfs::GetIndexBufferStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax, [esp + 4h]
|
|
|
|
cmp al, 0FFh
|
|
|
|
|
|
|
|
jne returnSafe
|
|
|
|
|
|
|
|
jmp ModelSurfs::SetBuffer
|
|
|
|
|
|
|
|
returnSafe:
|
|
|
|
movzx eax, [esp + 4h]
|
|
|
|
mov edx, 4B4DE5h
|
|
|
|
jmp edx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void ModelSurfs::GetIndexBufferStub2()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax, [esp + 4h]
|
|
|
|
cmp al, 0FFh
|
|
|
|
|
|
|
|
jne returnSafe
|
|
|
|
|
|
|
|
mov eax, [edi + 0Ch]
|
|
|
|
push eax
|
|
|
|
call ModelSurfs::GetBuffer
|
|
|
|
add esp, 4h
|
|
|
|
retn
|
|
|
|
|
|
|
|
returnSafe:
|
|
|
|
mov eax, 4FDC20h
|
|
|
|
jmp eax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void ModelSurfs::GetIndexBaseStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax, [esp + 4h]
|
|
|
|
cmp al, 0FFh
|
|
|
|
|
|
|
|
jne returnSafe
|
|
|
|
|
|
|
|
xor eax, eax
|
|
|
|
retn
|
|
|
|
|
|
|
|
returnSafe:
|
|
|
|
mov eax, 48C5F0h
|
|
|
|
jmp eax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
__declspec(naked) void ModelSurfs::GetVertexBufferStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax, [esp + 4h]
|
|
|
|
cmp al, 0FFh
|
|
|
|
|
|
|
|
jne returnSafe
|
|
|
|
|
|
|
|
jmp ModelSurfs::SetBuffer
|
|
|
|
|
|
|
|
returnSafe:
|
|
|
|
movzx eax, [esp + 4h]
|
|
|
|
mov edx, 5BC055h
|
|
|
|
jmp edx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ModelSurfs::ModelSurfs()
|
|
|
|
{
|
|
|
|
ModelSurfs::BufferMap.clear();
|
|
|
|
|
|
|
|
// Install release handler
|
2018-05-09 06:04:20 -04:00
|
|
|
Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_XMODEL_SURFS] = ModelSurfs::ReleaseModelSurf;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Install device recovery handlers
|
|
|
|
Renderer::OnDeviceRecoveryBegin(ModelSurfs::BeginRecover);
|
|
|
|
Renderer::OnDeviceRecoveryEnd(ModelSurfs::EndRecover);
|
|
|
|
|
|
|
|
// Install hooks
|
|
|
|
Utils::Hook(0x47A6BD, ModelSurfs::XModelSurfsFixup, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x558F12, ModelSurfs::GetIndexBaseStub, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x4B4DE0, ModelSurfs::GetIndexBufferStub, HOOK_JUMP).install()->quick();
|
|
|
|
Utils::Hook(0x558E70, ModelSurfs::GetIndexBufferStub2, HOOK_CALL).install()->quick();
|
|
|
|
Utils::Hook(0x5BC050, ModelSurfs::GetVertexBufferStub, HOOK_JUMP).install()->quick();
|
|
|
|
}
|
|
|
|
|
|
|
|
ModelSurfs::~ModelSurfs()
|
|
|
|
{
|
|
|
|
assert(ModelSurfs::BufferMap.empty());
|
|
|
|
assert(ModelSurfs::AllocMap.empty());
|
|
|
|
}
|
|
|
|
}
|