2016-01-04 14:58:00 -05:00
|
|
|
#include "STDInclude.hpp"
|
2015-12-23 16:21:03 -05:00
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
bool AssetHandler::BypassState = false;
|
2016-01-12 13:08:26 -05:00
|
|
|
std::map<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces;
|
2015-12-23 16:21:03 -05:00
|
|
|
std::map<Game::XAssetType, AssetHandler::Callback> AssetHandler::TypeCallbacks;
|
2015-12-25 20:51:58 -05:00
|
|
|
std::vector<AssetHandler::RestrictCallback> AssetHandler::RestrictCallbacks;
|
2015-12-23 16:21:03 -05:00
|
|
|
|
2015-12-28 19:45:04 -05:00
|
|
|
std::map<void*, void*> AssetHandler::Relocations;
|
|
|
|
|
2016-01-22 12:10:45 -05:00
|
|
|
std::map<std::string, Game::XAssetHeader> AssetHandler::TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
|
|
|
|
|
2016-01-12 13:08:26 -05:00
|
|
|
void AssetHandler::RegisterInterface(IAsset* iAsset)
|
|
|
|
{
|
|
|
|
if (!iAsset) return;
|
|
|
|
if (iAsset->GetType() == Game::XAssetType::ASSET_TYPE_INVALID)
|
|
|
|
{
|
|
|
|
delete iAsset;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AssetHandler::AssetInterfaces.find(iAsset->GetType()) != AssetHandler::AssetInterfaces.end())
|
|
|
|
{
|
|
|
|
delete AssetHandler::AssetInterfaces[iAsset->GetType()];
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetHandler::AssetInterfaces[iAsset->GetType()] = iAsset;
|
|
|
|
}
|
|
|
|
|
2016-01-22 12:10:45 -05:00
|
|
|
void AssetHandler::ClearTemporaryAssets()
|
|
|
|
{
|
2016-01-24 13:58:13 -05:00
|
|
|
for (int i = 0; i < Game::XAssetType::ASSET_TYPE_COUNT; ++i)
|
2016-01-22 12:10:45 -05:00
|
|
|
{
|
|
|
|
AssetHandler::TemporaryAssets[i].clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetHandler::StoreTemporaryAsset(Game::XAssetType type, Game::XAssetHeader asset)
|
|
|
|
{
|
|
|
|
AssetHandler::TemporaryAssets[type][Game::DB_GetXAssetNameHandlers[type](&asset)] = asset;
|
|
|
|
}
|
|
|
|
|
2015-12-23 16:21:03 -05:00
|
|
|
Game::XAssetHeader AssetHandler::FindAsset(Game::XAssetType type, const char* filename)
|
|
|
|
{
|
2015-12-23 21:26:46 -05:00
|
|
|
Game::XAssetHeader header = { 0 };
|
2015-12-23 16:21:03 -05:00
|
|
|
|
2015-12-23 21:26:46 -05:00
|
|
|
// Allow call DB_FindXAssetHeader within the hook
|
2015-12-23 16:21:03 -05:00
|
|
|
AssetHandler::BypassState = true;
|
|
|
|
|
|
|
|
if (AssetHandler::TypeCallbacks.find(type) != AssetHandler::TypeCallbacks.end())
|
|
|
|
{
|
|
|
|
header = AssetHandler::TypeCallbacks[type](type, filename);
|
|
|
|
}
|
|
|
|
|
2015-12-23 21:26:46 -05:00
|
|
|
// Disallow calling DB_FindXAssetHeader ;)
|
2015-12-23 16:21:03 -05:00
|
|
|
AssetHandler::BypassState = false;
|
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __declspec(naked) AssetHandler::FindAssetStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
push ecx
|
|
|
|
push ebx
|
|
|
|
push ebp
|
|
|
|
push esi
|
|
|
|
push edi
|
|
|
|
|
|
|
|
// Check if custom handler should be bypassed
|
|
|
|
xor eax, eax
|
|
|
|
mov al, AssetHandler::BypassState
|
|
|
|
|
|
|
|
test al, al
|
|
|
|
jnz finishOriginal
|
|
|
|
|
|
|
|
mov ecx, [esp + 18h] // Asset type
|
|
|
|
mov ebx, [esp + 1Ch] // Filename
|
|
|
|
|
|
|
|
push ebx
|
|
|
|
push ecx
|
|
|
|
|
|
|
|
call AssetHandler::FindAsset
|
|
|
|
|
|
|
|
add esp, 8h
|
|
|
|
|
|
|
|
test eax, eax
|
|
|
|
jnz finishFound
|
|
|
|
|
|
|
|
finishOriginal:
|
|
|
|
// Asset not found using custom handlers, redirect to DB_FindXAssetHeader
|
|
|
|
mov ebx, ds:6D7190h // InterlockedDecrement
|
|
|
|
mov eax, 40793Bh
|
|
|
|
jmp eax
|
|
|
|
|
|
|
|
finishFound:
|
|
|
|
pop edi
|
|
|
|
pop esi
|
|
|
|
pop ebp
|
|
|
|
pop ebx
|
|
|
|
pop ecx
|
|
|
|
retn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-25 20:51:58 -05:00
|
|
|
bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset)
|
|
|
|
{
|
|
|
|
const char* name = Game::DB_GetXAssetNameHandlers[type](asset);
|
|
|
|
if (!name) return false;
|
|
|
|
|
|
|
|
for (auto callback : AssetHandler::RestrictCallbacks)
|
|
|
|
{
|
|
|
|
if (!callback(type, *asset, name))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __declspec(naked) AssetHandler::AddAssetStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
push [esp + 8]
|
|
|
|
push [esp + 8]
|
|
|
|
call AssetHandler::IsAssetEligible
|
|
|
|
add esp, 08h
|
|
|
|
|
|
|
|
test al, al
|
|
|
|
jz doNotLoad
|
|
|
|
|
|
|
|
mov eax, [esp + 8]
|
|
|
|
sub esp, 14h
|
|
|
|
mov ecx, 5BB657h
|
|
|
|
jmp ecx
|
|
|
|
|
|
|
|
doNotLoad:
|
|
|
|
mov eax, [esp + 8]
|
|
|
|
retn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-26 08:27:34 -05:00
|
|
|
void AssetHandler::OnFind(Game::XAssetType type, AssetHandler::Callback callback)
|
2015-12-23 16:21:03 -05:00
|
|
|
{
|
|
|
|
AssetHandler::TypeCallbacks[type] = callback;
|
|
|
|
}
|
|
|
|
|
2015-12-26 08:27:34 -05:00
|
|
|
void AssetHandler::OnLoad(RestrictCallback callback)
|
2015-12-25 20:51:58 -05:00
|
|
|
{
|
|
|
|
AssetHandler::RestrictCallbacks.push_back(callback);
|
|
|
|
}
|
|
|
|
|
2015-12-28 19:45:04 -05:00
|
|
|
void AssetHandler::Relocate(void* start, void* to, DWORD size)
|
|
|
|
{
|
|
|
|
for (DWORD i = 0; i < size; i += 4)
|
|
|
|
{
|
2016-01-24 06:19:34 -05:00
|
|
|
// Reinterpret cast is fine here, as we are working with low-level pointers (due to the relocation hook)
|
2015-12-28 19:45:04 -05:00
|
|
|
AssetHandler::Relocations[reinterpret_cast<char*>(start) + i] = reinterpret_cast<char*>(to) + i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-05 19:23:43 -05:00
|
|
|
void AssetHandler::OffsetToAlias(Utils::Stream::Offset* offset)
|
2015-12-28 19:45:04 -05:00
|
|
|
{
|
2016-01-24 06:19:34 -05:00
|
|
|
// Same here, reinterpret the value, as we're operating inside the game's environment
|
2016-01-05 19:23:43 -05:00
|
|
|
offset->pointer = *reinterpret_cast<void**>((*Game::g_streamBlocks)[offset->GetUnpackedBlock()].data + offset->GetUnpackedOffset());
|
2015-12-28 19:45:04 -05:00
|
|
|
|
2016-01-05 19:23:43 -05:00
|
|
|
if (AssetHandler::Relocations.find(offset->pointer) != AssetHandler::Relocations.end())
|
2015-12-28 19:45:04 -05:00
|
|
|
{
|
2016-01-05 19:23:43 -05:00
|
|
|
offset->pointer = AssetHandler::Relocations[offset->pointer];
|
2015-12-28 19:45:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-12 13:08:26 -05:00
|
|
|
void AssetHandler::ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder)
|
|
|
|
{
|
|
|
|
if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end())
|
|
|
|
{
|
|
|
|
AssetHandler::AssetInterfaces[asset.type]->Save(asset.header, builder);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetHandler::ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder)
|
|
|
|
{
|
|
|
|
if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end())
|
|
|
|
{
|
|
|
|
AssetHandler::AssetInterfaces[asset.type]->Mark(asset.header, builder);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-22 10:11:47 -05:00
|
|
|
Game::XAssetHeader AssetHandler::FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder)
|
|
|
|
{
|
|
|
|
Game::XAssetHeader header = { 0 };
|
2016-01-22 12:10:45 -05:00
|
|
|
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
|
|
|
|
|
|
|
|
auto tempPool = &AssetHandler::TemporaryAssets[type];
|
|
|
|
auto entry = tempPool->find(filename);
|
|
|
|
if (entry != tempPool->end())
|
|
|
|
{
|
|
|
|
return { entry->second };
|
|
|
|
}
|
2016-01-22 10:11:47 -05:00
|
|
|
|
|
|
|
if (AssetHandler::AssetInterfaces.find(type) != AssetHandler::AssetInterfaces.end())
|
|
|
|
{
|
|
|
|
AssetHandler::AssetInterfaces[type]->Load(&header, filename, builder);
|
2016-01-22 14:49:52 -05:00
|
|
|
|
|
|
|
if (header.data)
|
|
|
|
{
|
|
|
|
Components::AssetHandler::StoreTemporaryAsset(type, header);
|
|
|
|
}
|
2016-01-22 10:11:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!header.data)
|
|
|
|
{
|
|
|
|
header = Game::DB_FindXAssetHeader(type, filename.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
2016-01-14 09:29:39 -05:00
|
|
|
Game::XAssetHeader AssetHandler::FindOriginalAsset(Game::XAssetType type, const char* filename)
|
|
|
|
{
|
2016-01-15 13:23:07 -05:00
|
|
|
bool OriginalState = AssetHandler::BypassState;
|
|
|
|
|
2016-01-14 09:29:39 -05:00
|
|
|
AssetHandler::BypassState = true;
|
2016-01-24 15:08:14 -05:00
|
|
|
Game::XAssetHeader header = Game::DB_FindXAssetHeader(type, filename);
|
2016-01-15 13:23:07 -05:00
|
|
|
if(!OriginalState) AssetHandler::BypassState = false;
|
2016-01-14 09:29:39 -05:00
|
|
|
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
2015-12-23 16:21:03 -05:00
|
|
|
AssetHandler::AssetHandler()
|
|
|
|
{
|
2016-01-22 12:10:45 -05:00
|
|
|
AssetHandler::ClearTemporaryAssets();
|
|
|
|
|
2015-12-28 19:45:04 -05:00
|
|
|
// DB_FindXAssetHeader
|
2015-12-23 16:21:03 -05:00
|
|
|
Utils::Hook(Game::DB_FindXAssetHeader, AssetHandler::FindAssetStub).Install()->Quick();
|
2015-12-25 20:51:58 -05:00
|
|
|
|
2015-12-28 19:45:04 -05:00
|
|
|
// DB_ConvertOffsetToAlias
|
|
|
|
Utils::Hook(0x4FDFA0, AssetHandler::OffsetToAlias, HOOK_JUMP).Install()->Quick();
|
|
|
|
|
|
|
|
// DB_AddXAsset
|
2015-12-25 20:51:58 -05:00
|
|
|
Utils::Hook(0x5BB650, AssetHandler::AddAssetStub, HOOK_JUMP).Install()->Quick();
|
2016-01-12 13:08:26 -05:00
|
|
|
|
|
|
|
// Register asset interfaces
|
|
|
|
AssetHandler::RegisterInterface(new Assets::IRawFile());
|
2016-01-22 05:59:43 -05:00
|
|
|
AssetHandler::RegisterInterface(new Assets::IGfxImage());
|
|
|
|
AssetHandler::RegisterInterface(new Assets::IMaterial());
|
2016-01-12 13:08:26 -05:00
|
|
|
AssetHandler::RegisterInterface(new Assets::ILocalizedEntry());
|
2016-01-22 05:59:43 -05:00
|
|
|
AssetHandler::RegisterInterface(new Assets::IMaterialPixelShader());
|
|
|
|
AssetHandler::RegisterInterface(new Assets::IMaterialTechniqueSet());
|
|
|
|
AssetHandler::RegisterInterface(new Assets::IMaterialVertexShader());
|
|
|
|
AssetHandler::RegisterInterface(new Assets::IMaterialVertexDeclaration());
|
2015-12-23 16:21:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
AssetHandler::~AssetHandler()
|
|
|
|
{
|
2016-01-22 12:10:45 -05:00
|
|
|
ClearTemporaryAssets();
|
|
|
|
|
2016-01-24 13:58:13 -05:00
|
|
|
for (auto i = AssetHandler::AssetInterfaces.begin(); i != AssetHandler::AssetInterfaces.end(); ++i)
|
2016-01-12 13:08:26 -05:00
|
|
|
{
|
|
|
|
delete i->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetHandler::AssetInterfaces.clear();
|
2015-12-23 16:21:03 -05:00
|
|
|
AssetHandler::TypeCallbacks.clear();
|
|
|
|
}
|
|
|
|
}
|