r5sdk/r5dev/engine/modelloader.cpp
Kawe Mazidjatari 144d5f62e1 IDetour: code refactor
Utilize the new IDetour::DetourSetup() code, IDetour::Attach and IDetour::Detach have been removed in favor of this (significantly reduces chance of user error). Since the template check happens in the idetour header, it is much more aggressive on type mismatches, such as a difference in parameter types, between the function and detour, will now raise a compile time error. As a result, some type mismatches have been fixed in this commit as well.
2024-04-05 16:41:09 +02:00

356 lines
10 KiB
C++

//===========================================================================//
//
// Purpose: Model loading / unloading interface
//
// $NoKeywords: $
//===========================================================================//
#include "core/stdafx.h"
#include "engine/cmodel_bsp.h"
#include "engine/modelloader.h"
#include "datacache/mdlcache.h"
#ifndef DEDICATED
#include <vgui/vgui_baseui_interface.h>
#endif // !DEDICATED
#include <filesystem/filesystem.h>
//model_t* pErrorMDL = nullptr;
//-----------------------------------------------------------------------------
// Purpose: returns whether or not the lump type could be loaded from cache
// Input : lumpType -
//-----------------------------------------------------------------------------
bool IsLumpTypeCachable(int lumpType)
{
switch (lumpType)
{
case LUMP_PLANES:
case LUMP_VERTICES:
case LUMP_SHADOW_ENVIRONMENTS:
case LUMP_SURFACE_NAMES:
case LUMP_CONTENTS_MASKS:
case LUMP_SURFACE_PROPERTIES:
case LUMP_BVH_NODES:
case LUMP_BVH_LEAF_DATA:
case LUMP_PACKED_VERTICES:
case LUMP_VERTEX_NORMALS:
case LUMP_UNKNOWN_37:
case LUMP_UNKNOWN_38:
case LUMP_UNKNOWN_39:
case LUMP_VERTEX_UNLIT:
case LUMP_VERTEX_LIT_FLAT:
case LUMP_VERTEX_LIT_BUMP:
case LUMP_VERTEX_UNLIT_TS:
case LUMP_MESH_INDICES:
case LUMP_LIGHTMAP_DATA_SKY:
case LUMP_CSM_AABB_NODES:
case LUMP_CSM_OBJ_REFERENCES:
case LUMP_LIGHTPROBES:
case LUMP_LIGHTPROBE_TREE:
case LUMP_LIGHTPROBE_REFERENCES:
case LUMP_LIGHTMAP_DATA_REAL_TIME_LIGHTS:
case LUMP_CELL_BSP_NODES:
case LUMP_CELLS:
case LUMP_PORTALS:
case LUMP_PORTAL_VERTICES:
case LUMP_PORTAL_EDGES:
case LUMP_PORTAL_VERTEX_EDGES:
case LUMP_PORTAL_VERTEX_REFERENCES:
case LUMP_PORTAL_EDGE_REFERENCES:
case LUMP_PORTAL_EDGE_INTERSECT_AT_EDGE:
case LUMP_PORTAL_EDGE_INTERSECT_AT_VERTEX:
case LUMP_PORTAL_EDGE_INTERSECT_HEADER:
case LUMP_OCCLUSION_MESH_VERTICES:
case LUMP_OCCLUSION_MESH_INDICES:
case LUMP_CELL_AABB_NODES:
case LUMP_OBJ_REFERENCES:
case LUMP_OBJ_REFERENCE_BOUNDS:
case LUMP_SHADOW_MESH_OPAQUE_VERTICES:
case LUMP_SHADOW_MESH_INDICES:
case LUMP_SHADOW_MESHES:
return true;
default:
return false;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns whether or not the lump type can be treated as external in code
// Input : lumpType -
//-----------------------------------------------------------------------------
bool IsLumpTypeExternal(int lumpType)
{
switch (lumpType)
{
case LUMP_VERTEX_UNLIT:
case LUMP_VERTEX_LIT_FLAT:
case LUMP_VERTEX_LIT_BUMP:
case LUMP_VERTEX_UNLIT_TS:
case LUMP_LIGHTPROBES:
return false;
default:
return true;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns whether or not the lump type is only used on the client
// Input : lumpType -
//-----------------------------------------------------------------------------
bool IsLumpTypeClientOnly(int lumpType)
{
switch (lumpType)
{
case LUMP_TEXTURE_DATA:
case LUMP_LIGHTPROBE_PARENT_INFOS:
case LUMP_SHADOW_ENVIRONMENTS:
case LUMP_VERTEX_NORMALS:
case LUMP_LEAF_WATER_DATA:
case LUMP_UNKNOWN_38:
case LUMP_CUBEMAPS:
case LUMP_WORLD_LIGHTS:
case LUMP_WORLD_LIGHT_PARENT_INFOS:
case LUMP_VERTEX_UNLIT:
case LUMP_VERTEX_LIT_FLAT:
case LUMP_VERTEX_LIT_BUMP:
case LUMP_VERTEX_UNLIT_TS:
case LUMP_VERTEX_BLINN_PHONG:
case LUMP_VERTEX_RESERVED_5:
case LUMP_VERTEX_RESERVED_6:
case LUMP_VERTEX_RESERVED_7:
case LUMP_MESH_INDICES:
case LUMP_MESHES:
case LUMP_MESH_BOUNDS:
case LUMP_MATERIAL_SORT:
case LUMP_LIGHTMAP_HEADERS:
case LUMP_TWEAK_LIGHTS:
case LUMP_UNKNOWN_97:
case LUMP_LIGHTMAP_DATA_SKY:
case LUMP_CSM_AABB_NODES:
case LUMP_CSM_OBJ_REFERENCES:
case LUMP_LIGHTPROBES:
case LUMP_STATIC_PROP_LIGHTPROBE_INDICES:
case LUMP_LIGHTPROBE_TREE:
case LUMP_LIGHTPROBE_REFERENCES:
case LUMP_LIGHTMAP_DATA_REAL_TIME_LIGHTS:
case LUMP_PORTALS:
case LUMP_PORTAL_VERTICES:
case LUMP_PORTAL_EDGES:
case LUMP_PORTAL_VERTEX_EDGES:
case LUMP_PORTAL_VERTEX_REFERENCES:
case LUMP_PORTAL_EDGE_REFERENCES:
case LUMP_PORTAL_EDGE_INTERSECT_AT_EDGE:
case LUMP_PORTAL_EDGE_INTERSECT_AT_VERTEX:
case LUMP_PORTAL_EDGE_INTERSECT_HEADER:
case LUMP_OCCLUSION_MESH_VERTICES:
case LUMP_OCCLUSION_MESH_INDICES:
case LUMP_LIGHTMAP_DATA_RTL_PAGE:
case LUMP_SHADOW_MESH_OPAQUE_VERTICES:
case LUMP_SHADOW_MESH_ALPHA_VERTICES:
case LUMP_SHADOW_MESH_INDICES:
case LUMP_SHADOW_MESHES:
return true;
default:
return false;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *loader -
// *model -
//-----------------------------------------------------------------------------
void CModelLoader::LoadModel(CModelLoader* loader, model_t* model)
{
//if (!pErrorMDL)
//{
// if (strcmp(model->szPathName, ERROR_MODEL) == 0)
// {
// pErrorMDL = model;
// }
//}
//string svExtension = model->szPathName;
//size_t npos = svExtension.find(".");
//if (npos != string::npos)
//{
// svExtension = svExtension.substr(npos + 1);
//}
//if (strcmp(svExtension.c_str(), "rmdl") == 0 && strcmp(model->szPathName, ERROR_MODEL) != 0)
//{
// studiohdr_t* pStudioHDR = g_MDLCache->FindMDL(g_MDLCache->m_pVTable, model->studio, 0);
// if (pStudioHDR == pErrorStudioHDR)
// {
// model = pErrorMDL;
// }
//}
return CModelLoader__LoadModel(loader, model);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *loader -
// *model -
//-----------------------------------------------------------------------------
uint64_t CModelLoader::Map_LoadModelGuts(CModelLoader* loader, model_t* model)
{
return CModelLoader__Map_LoadModelGuts(loader, model);
}
void CMapLoadHelper::Constructor(CMapLoadHelper* loader, int lumpToLoad)
{
#ifndef DEDICATED
g_pEngineVGui->UpdateProgressBar(PROGRESS_DEFAULT);
#endif // !DEDICATED
if (lumpToLoad > HEADER_LUMPS-1)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Can't load lump %i, range is 0 to %i!!!\n", lumpToLoad, HEADER_LUMPS-1);
loader->m_nLumpID = lumpToLoad;
loader->m_nLumpSize = 0;
loader->m_pData = nullptr;
loader->m_pRawData = nullptr;
loader->m_pUncompressedData = nullptr;
loader->m_nUncompressedLumpSize = 0;
loader->m_bUncompressedDataExternal = 0;
loader->m_bExternal = false;
loader->m_bUnk = false;
loader->m_nLumpOffset = -1;
#ifdef DEDICATED
// Some of the lump loading code that is specific to
// the client is heavily inline in code, which makes
// it hard to patch it out from there.. to fix this,
// we just check from here and return if its cl only
if (IsLumpTypeClientOnly(lumpToLoad))
return;
#endif // DEDICATED
if (lumpToLoad <= s_MapHeader->lastLump)
{
const lump_t* lump = &s_MapHeader->lumps[lumpToLoad];
const int lumpOffset = lump->fileofs;
const int lumpSize = lump->filelen;
if (lumpSize <= 0)
{
loader->m_nLumpSize = 0;
loader->m_nLumpOffset = 0;
loader->m_nLumpVersion = 0;
// this lump has no data
return;
}
loader->m_nLumpSize = lumpSize;
loader->m_nLumpOffset = lumpOffset;
loader->m_nLumpVersion = lump->version;
FileHandle_t mapFileHandle = *s_MapFileHandle;
if (mapFileHandle == FILESYSTEM_INVALID_HANDLE)
{
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Can't load map from invalid handle!!!\n");
}
loader->m_nUncompressedLumpSize = lumpSize;
FileSystemCache fileCache;
fileCache.pBuffer = nullptr;
char lumpPathBuf[MAX_PATH];
V_snprintf(lumpPathBuf, sizeof(lumpPathBuf), "%s.%.4X.bsp_lump", s_szMapPathName, lumpToLoad);
// Determine whether to load the lump from filesystem cache or disk.
if (IsLumpTypeCachable(lumpToLoad) &&
FileSystem()->ReadFromCache(lumpPathBuf, &fileCache))
{
loader->m_pRawData = nullptr;
loader->m_pData = fileCache.pBuffer->pData;
loader->m_bExternal = IsLumpTypeExternal(lumpToLoad);
loader->m_bUnk = fileCache.pBuffer->nUnk0 == 0;
}
else
{
loader->m_pRawData = new byte[lumpSize];
loader->m_pData = loader->m_pRawData;
FileHandle_t hLumpFile = FileSystem()->Open(lumpPathBuf, "rb");
if (hLumpFile != FILESYSTEM_INVALID_HANDLE)
{
DevMsg(eDLL_T::ENGINE, "Loading lump %.4x from file. Buffer: %p\n", lumpToLoad, loader->m_pRawData);
FileSystem()->ReadEx(loader->m_pRawData, lumpSize, lumpSize, hLumpFile);
FileSystem()->Close(hLumpFile);
loader->m_pRawData = nullptr;
loader->m_bExternal = IsLumpTypeExternal(lumpToLoad);
}
else // Seek to offset in packed BSP file to load the lump.
{
FileSystem()->Seek(mapFileHandle, loader->m_nLumpOffset, FILESYSTEM_SEEK_HEAD);
FileSystem()->ReadEx(loader->m_pRawData, lumpSize, lumpSize, mapFileHandle);
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Hook 'AddGameLump' and load the external lump from the disk instead
// Input : *loader -
// *model -
//-----------------------------------------------------------------------------
void AddGameLump()
{
char lumpPathBuf[MAX_PATH];
V_snprintf(lumpPathBuf, sizeof(lumpPathBuf), "%s.%.4X.bsp_lump", s_szMapPathName, LUMP_GAME_LUMP);
FileHandle_t hLumpFile = FileSystem()->Open(lumpPathBuf, "rb");
if (hLumpFile != FILESYSTEM_INVALID_HANDLE)
{
// This function uses the 's_szMapPathName' internally to copy the map
// path to another static buffer which is used as the game lump path.
// We temporarily set the path to that of the game lump so that other
// routines are loading the game lump instead of the packed BSP.
char oldMapPathName[MAX_PATH];
strcpy(oldMapPathName, s_szMapPathName);
strcpy(s_szMapPathName, lumpPathBuf);
// This function uses the 's_MapFileHandle' internally.
// basically, the idea is to set this static filehandle
// to that of the GAME_LUMP lump, so it reads that instead.
FileHandle_t hOrigMapFileHandle = *s_MapFileHandle;
*s_MapFileHandle = hLumpFile;
// Set the file offset to 0, as we are loading it from
// the external lump instead of the one packed in the BSP.
lump_t* pLump = &s_MapHeader->lumps[LUMP_GAME_LUMP];
pLump->fileofs = 0;
v_AddGameLump();
// Restore...
strcpy(s_szMapPathName, oldMapPathName);
*s_MapFileHandle = hOrigMapFileHandle;
FileSystem()->Close(hLumpFile);
}
else
{
// Load the lump from the monolithic BSP file...
v_AddGameLump();
}
}
///////////////////////////////////////////////////////////////////////////////
void VModelLoader::Detour(const bool bAttach) const
{
DetourSetup(&CModelLoader__LoadModel, &CModelLoader::LoadModel, bAttach);
DetourSetup(&CModelLoader__Map_LoadModelGuts, &CModelLoader::Map_LoadModelGuts, bAttach);
DetourSetup(&CMapLoadHelper__CMapLoadHelper, &CMapLoadHelper::Constructor, bAttach);
DetourSetup(&v_AddGameLump, &AddGameLump, bAttach);
}