//===========================================================================// // // 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 #endif // !DEDICATED #include //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); }