Counter Strike : Global Offensive Source Code
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.

415 lines
11 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "mathlib/vector.h"
  7. #include "DownloadListGenerator.h"
  8. #include "filesystem.h"
  9. #include "filesystem_engine.h"
  10. #include "sys.h"
  11. #include "cmd.h"
  12. #include "common.h"
  13. #include "quakedef.h"
  14. #include "vengineserver_impl.h"
  15. #include "tier1/strtools.h"
  16. #include "tier0/icommandline.h"
  17. #include "checksum_engine.h"
  18. #include <ctype.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <tier0/dbg.h>
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. CDownloadListGenerator g_DownloadListGenerator;
  25. CDownloadListGenerator &DownloadListGenerator()
  26. {
  27. return g_DownloadListGenerator;
  28. }
  29. ConVar sv_logdownloadlist( "sv_logdownloadlist", "0" );
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Constructor
  32. //-----------------------------------------------------------------------------
  33. CDownloadListGenerator::CDownloadListGenerator()
  34. : m_AlreadyWrittenFileNames( 0, 0, true )
  35. {
  36. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  37. m_pStringTable = NULL;
  38. m_mapName[0] = 0;
  39. }
  40. //-----------------------------------------------------------------------------
  41. //-----------------------------------------------------------------------------
  42. void CDownloadListGenerator::SetStringTable( INetworkStringTable *pStringTable )
  43. {
  44. if ( IsX360() )
  45. {
  46. // not supporting
  47. return;
  48. }
  49. m_pStringTable = pStringTable;
  50. // reset the duplication list
  51. m_AlreadyWrittenFileNames.RemoveAll();
  52. // add in the bsp file to the list, and its node graph and nav mesh
  53. char path[_MAX_PATH];
  54. Q_snprintf(path, sizeof(path), "maps\\%s.bsp", m_mapName);
  55. OnResourcePrecached(path);
  56. bool useNodeGraph = true;
  57. KeyValues *modinfo = new KeyValues("ModInfo");
  58. if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
  59. {
  60. useNodeGraph = modinfo->GetInt( "nodegraph", 1 ) != 0;
  61. }
  62. modinfo->deleteThis();
  63. if ( useNodeGraph )
  64. {
  65. Q_snprintf(path, sizeof(path), "maps\\graphs\\%s.ain", m_mapName);
  66. OnResourcePrecached(path);
  67. }
  68. Q_snprintf(path, sizeof(path), "maps\\%s.nav", m_mapName);
  69. OnResourcePrecached(path);
  70. char resfilename[MAX_OSPATH];
  71. Q_snprintf( resfilename, sizeof( resfilename), "maps/%s.res", m_mapName );
  72. KeyValues::AutoDelete resfilekeys( "resourcefiles" );
  73. if ( resfilekeys->LoadFromFile( g_pFileSystem, resfilename, "GAME" ) )
  74. {
  75. for ( KeyValues *pKey = resfilekeys->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() )
  76. {
  77. OnResourcePrecached( pKey->GetName() );
  78. }
  79. }
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: call to mark level load/end
  83. //-----------------------------------------------------------------------------
  84. void CDownloadListGenerator::OnLevelLoadStart(const char *levelName)
  85. {
  86. if ( IsX360() )
  87. {
  88. // not supporting
  89. return;
  90. }
  91. // close the previous level reslist, if any
  92. if (m_hReslistFile != FILESYSTEM_INVALID_HANDLE)
  93. {
  94. g_pFileSystem->Close(m_hReslistFile);
  95. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  96. }
  97. // reset the duplication list
  98. m_AlreadyWrittenFileNames.RemoveAll();
  99. if ( sv_logdownloadlist.GetBool() )
  100. {
  101. // open the new level reslist
  102. char path[MAX_OSPATH];
  103. g_pFileSystem->CreateDirHierarchy( "DownloadLists", "MOD" );
  104. Q_snprintf(path, sizeof(path), "DownloadLists/%s.lst", levelName);
  105. m_hReslistFile = g_pFileSystem->Open(path, "wt", "GAME");
  106. }
  107. // add a slash to the end of com_gamedir, so we can only deal with files for this mod
  108. Q_snprintf( m_gameDir, sizeof(m_gameDir), "%s/", com_gamedir );
  109. V_FixSlashes( m_gameDir, '/' );
  110. // save off the map name
  111. V_strcpy_safe( m_mapName, levelName );
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: call to mark level load/end
  115. //-----------------------------------------------------------------------------
  116. void CDownloadListGenerator::OnLevelLoadEnd()
  117. {
  118. if ( IsX360() )
  119. {
  120. // not supporting
  121. return;
  122. }
  123. if ( m_hReslistFile != FILESYSTEM_INVALID_HANDLE )
  124. {
  125. g_pFileSystem->Close(m_hReslistFile);
  126. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  127. }
  128. m_pStringTable = NULL;
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose: logs and handles mdl files being precaches
  132. //-----------------------------------------------------------------------------
  133. void CDownloadListGenerator::OnModelPrecached(const char *relativePathFileName)
  134. {
  135. if ( IsX360() )
  136. {
  137. // not supporting
  138. return;
  139. }
  140. if (Q_strstr(relativePathFileName, ".vmt"))
  141. {
  142. // it's a materials file, make sure that it starts in the materials directory, and we get the .vtf
  143. char file[_MAX_PATH];
  144. if ( StringHasPrefix( relativePathFileName, "materials" ) )
  145. {
  146. Q_strncpy(file, relativePathFileName, sizeof(file));
  147. }
  148. else
  149. {
  150. // prepend the materials directory
  151. Q_snprintf(file, sizeof(file), "materials\\%s", relativePathFileName);
  152. }
  153. OnResourcePrecached(file);
  154. // get the matching vtf file
  155. char *ext = Q_strstr(file, ".vmt");
  156. if (ext)
  157. {
  158. Q_strncpy(ext, ".vtf", 5);
  159. OnResourcePrecached(file);
  160. }
  161. }
  162. else
  163. {
  164. OnResourcePrecached(relativePathFileName);
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose: logs sound file access
  169. //-----------------------------------------------------------------------------
  170. void CDownloadListGenerator::OnSoundPrecached(const char *relativePathFileName)
  171. {
  172. if ( IsX360() )
  173. {
  174. // not supporting
  175. return;
  176. }
  177. // skip any special characters
  178. if (!V_isalnum(relativePathFileName[0]))
  179. {
  180. ++relativePathFileName;
  181. }
  182. // prepend the sound/ directory if necessary
  183. char file[_MAX_PATH];
  184. if ( StringHasPrefix( relativePathFileName, "sound" ) )
  185. {
  186. Q_strncpy(file, relativePathFileName, sizeof(file));
  187. }
  188. else
  189. {
  190. // prepend the materials directory
  191. Q_snprintf(file, sizeof(file), "sound\\%s", relativePathFileName);
  192. }
  193. OnResourcePrecached(file);
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose: logs the precache as a file access
  197. //-----------------------------------------------------------------------------
  198. void CDownloadListGenerator::OnResourcePrecached( const char *pRelativePathFileName )
  199. {
  200. // not supporting
  201. if ( IsX360() )
  202. return;
  203. // ignore empty string
  204. if ( pRelativePathFileName[0] == 0 )
  205. return;
  206. // ignore files that start with '*' since they signify special models
  207. if ( pRelativePathFileName[0] == '*' )
  208. return;
  209. if ( Q_IsAbsolutePath( pRelativePathFileName ) )
  210. {
  211. Warning( "*** CDownloadListGenerator::OnResourcePrecached: Encountered full path %s!\n", pRelativePathFileName );
  212. return;
  213. }
  214. char pRelativePath[MAX_PATH];
  215. Q_strncpy( pRelativePath, pRelativePathFileName, sizeof(pRelativePath) );
  216. Q_FixSlashes( pRelativePath, '/' );
  217. // make sure the filename hasn't already been written
  218. UtlSymId_t filename = m_AlreadyWrittenFileNames.Find( pRelativePath );
  219. if ( filename != UTL_INVAL_SYMBOL )
  220. return;
  221. // record in list, so we don't write it again
  222. m_AlreadyWrittenFileNames.AddString( pRelativePath );
  223. // don't allow files for download the server doesn't have
  224. if ( !g_pFileSystem->FileExists( pRelativePath, "GAME" ) )
  225. return;
  226. // add extras for mdl's
  227. char *pExt = const_cast< char* >( Q_GetFileExtension( pRelativePath ) );
  228. if ( !Q_stricmp( pExt, "mdl" ) )
  229. {
  230. Q_strncpy(pExt, "vvd", 10);
  231. OnResourcePrecached(pRelativePath);
  232. Q_strncpy(pExt, "ani", 10);
  233. OnResourcePrecached(pRelativePath);
  234. Q_strncpy(pExt, "dx90.vtx", 10);
  235. OnResourcePrecached(pRelativePath);
  236. Q_strncpy(pExt, "phy", 10);
  237. OnResourcePrecached(pRelativePath);
  238. Q_strncpy(pExt, "jpg", 10);
  239. OnResourcePrecached(pRelativePath);
  240. }
  241. FileHandle_t handle = m_hReslistFile;
  242. if ( handle != FILESYSTEM_INVALID_HANDLE )
  243. {
  244. g_pFileSystem->Write("\"", 1, handle);
  245. g_pFileSystem->Write( pRelativePath, Q_strlen(pRelativePath), handle );
  246. g_pFileSystem->Write("\"\n", 2, handle);
  247. }
  248. if ( m_pStringTable )
  249. {
  250. m_pStringTable->AddString( true, pRelativePath );
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: marks a precached file as needing a specific CRC on the client
  255. //-----------------------------------------------------------------------------
  256. void CDownloadListGenerator::ForceExactFile( const char *relativePathFileName, ConsistencyType consistency )
  257. {
  258. if ( IsX360() )
  259. {
  260. // not supporting
  261. return;
  262. }
  263. if ( !m_pStringTable )
  264. return;
  265. if ( consistency != CONSISTENCY_EXACT && consistency != CONSISTENCY_SIMPLE_MATERIAL )
  266. {
  267. consistency = CONSISTENCY_EXACT;
  268. }
  269. CRC32_t crc;
  270. bool error = true;
  271. char file[_MAX_PATH];
  272. const char *filePtr = relativePathFileName;
  273. if (Q_strstr(relativePathFileName, ".vmt") || Q_strstr(relativePathFileName, ".vtf"))
  274. {
  275. // it's a materials file, make sure that it starts in the materials directory, and we get the .vtf
  276. if ( StringHasPrefix(relativePathFileName, "materials" ) )
  277. {
  278. Q_strncpy(file, relativePathFileName, sizeof(file));
  279. }
  280. else
  281. {
  282. // prepend the materials directory
  283. Q_snprintf(file, sizeof(file), "materials\\%s", relativePathFileName);
  284. }
  285. error = !CRC_File( &crc, file );
  286. filePtr = file;
  287. }
  288. else
  289. {
  290. error = !CRC_File( &crc, relativePathFileName );
  291. }
  292. if ( error )
  293. {
  294. DevWarning( "Failed to CRC %s\n", relativePathFileName );
  295. }
  296. else
  297. {
  298. char relativeFileName[_MAX_PATH];
  299. Q_strncpy( relativeFileName, filePtr, sizeof( relativeFileName ) );
  300. V_FixSlashes( relativeFileName, '/' );
  301. ExactFileUserData userData;
  302. userData.consistencyType = consistency;
  303. userData.crc = crc;
  304. int index = m_pStringTable->FindStringIndex( relativeFileName );
  305. if ( index != INVALID_STRING_INDEX )
  306. {
  307. m_pStringTable->SetStringUserData( index, sizeof( ExactFileUserData ), &userData );
  308. }
  309. else
  310. {
  311. m_pStringTable->AddString( true, relativeFileName, sizeof( ExactFileUserData ), &userData );
  312. }
  313. }
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: marks a precached model as having a maximum size on the client
  317. //-----------------------------------------------------------------------------
  318. void CDownloadListGenerator::ForceModelBounds( const char *relativePathFileName, const Vector &mins, const Vector &maxs )
  319. {
  320. if ( IsX360() )
  321. {
  322. // not supporting
  323. return;
  324. }
  325. if ( !m_pStringTable )
  326. return;
  327. if ( !relativePathFileName )
  328. relativePathFileName = "";
  329. if (!Q_stristr(relativePathFileName, ".mdl"))
  330. {
  331. DevWarning( "Warning - trying to enforce model bounds on %s\n", relativePathFileName );
  332. return;
  333. }
  334. char relativeFileName[_MAX_PATH];
  335. Q_strncpy( relativeFileName, relativePathFileName, sizeof( relativeFileName ) );
  336. V_FixSlashes( relativeFileName, '/' );
  337. ModelBoundsUserData userData;
  338. userData.consistencyType = CONSISTENCY_BOUNDS;
  339. userData.mins = mins;
  340. userData.maxs = maxs;
  341. int index = m_pStringTable->FindStringIndex( relativeFileName );
  342. if ( index != INVALID_STRING_INDEX )
  343. {
  344. m_pStringTable->SetStringUserData( index, sizeof( ModelBoundsUserData ), &userData );
  345. }
  346. else
  347. {
  348. m_pStringTable->AddString( true, relativeFileName, sizeof( ModelBoundsUserData ), &userData );
  349. }
  350. }