You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
453 lines
12 KiB
453 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "mathlib/vector.h" |
|
#include "DownloadListGenerator.h" |
|
#include "filesystem.h" |
|
#include "filesystem_engine.h" |
|
#include "sys.h" |
|
#include "cmd.h" |
|
#include "common.h" |
|
#include "quakedef.h" |
|
#include "vengineserver_impl.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/icommandline.h" |
|
#include "checksum_engine.h" |
|
#include <ctype.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <tier0/dbg.h> |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
CDownloadListGenerator g_DownloadListGenerator; |
|
CDownloadListGenerator &DownloadListGenerator() |
|
{ |
|
return g_DownloadListGenerator; |
|
} |
|
|
|
ConVar sv_logdownloadlist( "sv_logdownloadlist", IsX360() ? "0" : "1" ); |
|
|
|
extern int GetSvPureMode(); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CDownloadListGenerator::CDownloadListGenerator() |
|
: m_AlreadyWrittenFileNames( 0, 0, true ) |
|
{ |
|
m_hReslistFile = FILESYSTEM_INVALID_HANDLE; |
|
m_pStringTable = NULL; |
|
m_mapName[0] = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::SetStringTable( INetworkStringTable *pStringTable ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
m_pStringTable = pStringTable; |
|
|
|
// reset the duplication list |
|
m_AlreadyWrittenFileNames.RemoveAll(); |
|
|
|
// add in the bsp file to the list, and its node graph and nav mesh |
|
char path[_MAX_PATH]; |
|
Q_snprintf(path, sizeof(path), "maps\\%s.bsp", m_mapName); |
|
OnResourcePrecached(path); |
|
|
|
bool useNodeGraph = true; |
|
KeyValues *modinfo = new KeyValues("ModInfo"); |
|
if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) ) |
|
{ |
|
useNodeGraph = modinfo->GetInt( "nodegraph", 1 ) != 0; |
|
} |
|
modinfo->deleteThis(); |
|
|
|
if ( useNodeGraph ) |
|
{ |
|
Q_snprintf(path, sizeof(path), "maps\\graphs\\%s.ain", m_mapName); |
|
OnResourcePrecached(path); |
|
} |
|
|
|
Q_snprintf(path, sizeof(path), "maps\\%s.nav", m_mapName); |
|
OnResourcePrecached(path); |
|
|
|
char resfilename[MAX_OSPATH]; |
|
KeyValues *resfilekeys = new KeyValues( "resourefiles" ); |
|
|
|
Q_snprintf( resfilename, sizeof( resfilename), "maps/%s.res", m_mapName ); |
|
|
|
if ( resfilekeys->LoadFromFile( g_pFileSystem, resfilename, "GAME" ) ) |
|
{ |
|
KeyValues *entry = resfilekeys->GetFirstSubKey(); |
|
while ( entry ) |
|
{ |
|
OnResourcePrecached( entry->GetName() ); |
|
entry = entry->GetNextKey(); |
|
} |
|
} |
|
resfilekeys->deleteThis(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: call to mark level load/end |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnLevelLoadStart(const char *levelName) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
// close the previous level reslist, if any |
|
if (m_hReslistFile != FILESYSTEM_INVALID_HANDLE) |
|
{ |
|
g_pFileSystem->Close(m_hReslistFile); |
|
m_hReslistFile = FILESYSTEM_INVALID_HANDLE; |
|
} |
|
|
|
// reset the duplication list |
|
m_AlreadyWrittenFileNames.RemoveAll(); |
|
|
|
if ( sv_logdownloadlist.GetBool() ) |
|
{ |
|
// open the new level reslist |
|
char path[MAX_OSPATH]; |
|
g_pFileSystem->CreateDirHierarchy( "DownloadLists", "MOD" ); |
|
Q_snprintf(path, sizeof(path), "DownloadLists/%s.lst", levelName); |
|
m_hReslistFile = g_pFileSystem->Open(path, "wt", "GAME"); |
|
} |
|
|
|
// add a slash to the end of com_gamedir, so we can only deal with files for this mod |
|
Q_snprintf( m_gameDir, sizeof(m_gameDir), "%s/", com_gamedir ); |
|
Q_FixSlashes( m_gameDir ); |
|
|
|
// save off the map name |
|
Q_snprintf( m_mapName, sizeof( m_mapName ), "%s", levelName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: call to mark level load/end |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnLevelLoadEnd() |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
if ( m_hReslistFile != FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
g_pFileSystem->Close(m_hReslistFile); |
|
m_hReslistFile = FILESYSTEM_INVALID_HANDLE; |
|
} |
|
m_pStringTable = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: logs and handles mdl files being precaches |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnModelPrecached(const char *relativePathFileName) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
if (Q_strstr(relativePathFileName, ".vmt")) |
|
{ |
|
// it's a materials file, make sure that it starts in the materials directory, and we get the .vtf |
|
char file[_MAX_PATH]; |
|
|
|
if (!Q_strnicmp(relativePathFileName, "materials", strlen("materials"))) |
|
{ |
|
Q_strncpy(file, relativePathFileName, sizeof(file)); |
|
} |
|
else |
|
{ |
|
// prepend the materials directory |
|
Q_snprintf(file, sizeof(file), "materials\\%s", relativePathFileName); |
|
} |
|
|
|
OnResourcePrecached(file); |
|
|
|
// get the matching vtf file |
|
char *ext = Q_strstr(file, ".vmt"); |
|
if (ext) |
|
{ |
|
Q_strncpy(ext, ".vtf", 5); |
|
OnResourcePrecached(file); |
|
} |
|
} |
|
else |
|
{ |
|
OnResourcePrecached(relativePathFileName); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: logs sound file access |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnSoundPrecached(const char *relativePathFileName) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
// skip any special characters |
|
if (!V_isalnum(relativePathFileName[0])) |
|
{ |
|
++relativePathFileName; |
|
} |
|
|
|
// prepend the sound/ directory if necessary |
|
char file[_MAX_PATH]; |
|
if (!Q_strnicmp(relativePathFileName, "sound", strlen("sound"))) |
|
{ |
|
Q_strncpy(file, relativePathFileName, sizeof(file)); |
|
} |
|
else |
|
{ |
|
// prepend the materials directory |
|
Q_snprintf(file, sizeof(file), "sound\\%s", relativePathFileName); |
|
} |
|
|
|
OnResourcePrecached(file); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: logs the precache as a file access |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnResourcePrecached(const char *relativePathFileName) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
// ignore empty string |
|
if (relativePathFileName[0] == 0) |
|
{ |
|
return; |
|
} |
|
|
|
// ignore files that start with '*' since they signify special models |
|
if (relativePathFileName[0] == '*') |
|
{ |
|
return; |
|
} |
|
|
|
char fullPath[_MAX_PATH]; |
|
if (g_pFileSystem->GetLocalPath(relativePathFileName, fullPath, sizeof(fullPath))) |
|
{ |
|
OnResourcePrecachedFullPath( fullPath, relativePathFileName); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: marks a precached file as needing a specific CRC on the client |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::ForceSimpleMaterial( const char *relativePathFileName ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
if ( !m_pStringTable ) |
|
return; |
|
|
|
if ( !Q_stristr(relativePathFileName, ".vmt") && !Q_stristr(relativePathFileName, ".vtf")) |
|
{ |
|
DevMsg( "Tried to enforce simple material on %s\n", relativePathFileName ); |
|
return; |
|
} |
|
|
|
// it's a materials file, make sure that it starts in the materials directory, and we get the .vtf |
|
char szFixedFilename[_MAX_PATH]; |
|
if (!Q_strnicmp(relativePathFileName, "materials", strlen("materials"))) |
|
{ |
|
V_strcpy_safe( szFixedFilename, relativePathFileName ); |
|
} |
|
else |
|
{ |
|
// prepend the materials directory |
|
V_sprintf_safe( szFixedFilename, "materials\\%s", relativePathFileName ); |
|
} |
|
V_FixSlashes( szFixedFilename ); |
|
if ( !g_pFullFileSystem->FileExists( szFixedFilename, "game" ) ) |
|
{ |
|
DevMsg( "Cannot force simple material on %s; file not found\n", szFixedFilename ); |
|
return; |
|
} |
|
|
|
ExactFileUserData userData; |
|
userData.consistencyType = CONSISTENCY_SIMPLE_MATERIAL; |
|
userData.crc = 0; |
|
|
|
// Only set consistency data if pure, otherwise just create entry in download list |
|
if ( GetSvPureMode() < 0 ) |
|
{ |
|
m_pStringTable->AddString( true, szFixedFilename ); |
|
} |
|
else |
|
{ |
|
int index = m_pStringTable->FindStringIndex( szFixedFilename ); |
|
if ( index != INVALID_STRING_INDEX ) |
|
{ |
|
m_pStringTable->SetStringUserData( index, sizeof( ExactFileUserData ), &userData ); |
|
} |
|
else |
|
{ |
|
m_pStringTable->AddString( true, szFixedFilename, sizeof( ExactFileUserData ), &userData ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: marks a precached model as having a maximum size on the client |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::ForceModelBounds( const char *relativePathFileName, const Vector &mins, const Vector &maxs ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
if ( !m_pStringTable ) |
|
return; |
|
|
|
if ( !relativePathFileName ) |
|
relativePathFileName = ""; |
|
|
|
if (!Q_stristr(relativePathFileName, ".mdl")) |
|
{ |
|
DevMsg( "Warning - trying to enforce model bounds on %s\n", relativePathFileName ); |
|
return; |
|
} |
|
|
|
char relativeFileName[_MAX_PATH]; |
|
Q_strncpy( relativeFileName, relativePathFileName, sizeof( relativeFileName ) ); |
|
Q_FixSlashes( relativeFileName ); |
|
|
|
// Only set consistency data if pure, otherwise just create entry in download list |
|
if ( GetSvPureMode() < 0 ) |
|
{ |
|
m_pStringTable->AddString( true, relativePathFileName ); |
|
} |
|
else |
|
{ |
|
ModelBoundsUserData userData; |
|
userData.consistencyType = CONSISTENCY_BOUNDS; |
|
userData.mins = mins; |
|
userData.maxs = maxs; |
|
|
|
int index = m_pStringTable->FindStringIndex( relativeFileName ); |
|
if ( index != INVALID_STRING_INDEX ) |
|
{ |
|
m_pStringTable->SetStringUserData( index, sizeof( ModelBoundsUserData ), &userData ); |
|
} |
|
else |
|
{ |
|
m_pStringTable->AddString( true, relativeFileName, sizeof( ModelBoundsUserData ), &userData ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Logs out file access to a file |
|
//----------------------------------------------------------------------------- |
|
void CDownloadListGenerator::OnResourcePrecachedFullPath( char *fullPathFileName, const char *relativeFileName ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
// not supporting |
|
return; |
|
} |
|
|
|
Q_FixSlashes( fullPathFileName ); |
|
|
|
if ( !g_pFileSystem->FileExists( fullPathFileName ) ) |
|
{ |
|
return; // don't allow files for download the server doesn't have |
|
} |
|
|
|
if ( Q_strncasecmp( m_gameDir, fullPathFileName, Q_strlen( m_gameDir ) ) ) |
|
{ |
|
return; // the game dir must be part of the full name |
|
} |
|
|
|
// make sure the filename hasn't already been written |
|
UtlSymId_t filename = m_AlreadyWrittenFileNames.Find( fullPathFileName ); |
|
if ( filename != UTL_INVAL_SYMBOL ) |
|
{ |
|
return; |
|
} |
|
|
|
// record in list, so we don't write it again |
|
m_AlreadyWrittenFileNames.AddString( fullPathFileName ); |
|
|
|
// add extras for mdl's |
|
if (Q_strstr(relativeFileName, ".mdl")) |
|
{ |
|
// it's a model, get it's other files as well |
|
char file[_MAX_PATH]; |
|
Q_strncpy(file, relativeFileName, sizeof(file) - 10); |
|
char *ext = Q_strstr(file, ".mdl"); |
|
|
|
Q_strncpy(ext, ".vvd", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".ani", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".dx80.vtx", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".dx90.vtx", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".sw.vtx", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".phy", 10); |
|
OnResourcePrecached(file); |
|
|
|
Q_strncpy(ext, ".jpg", 10); |
|
OnResourcePrecached(file); |
|
} |
|
|
|
FileHandle_t handle = m_hReslistFile; |
|
if ( handle != FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
g_pFileSystem->Write("\"", 1, handle); |
|
g_pFileSystem->Write( relativeFileName, Q_strlen(relativeFileName), handle ); |
|
g_pFileSystem->Write("\"\n", 2, handle); |
|
} |
|
if ( m_pStringTable ) |
|
{ |
|
m_pStringTable->AddString( true, relativeFileName ); |
|
} |
|
} |
|
|
|
|
|
|
|
|