Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright 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", IsX360() ? "0" : "1" );
  30. extern int GetSvPureMode();
  31. //-----------------------------------------------------------------------------
  32. // Purpose: Constructor
  33. //-----------------------------------------------------------------------------
  34. CDownloadListGenerator::CDownloadListGenerator()
  35. : m_AlreadyWrittenFileNames( 0, 0, true )
  36. {
  37. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  38. m_pStringTable = NULL;
  39. m_mapName[0] = 0;
  40. }
  41. //-----------------------------------------------------------------------------
  42. //-----------------------------------------------------------------------------
  43. void CDownloadListGenerator::SetStringTable( INetworkStringTable *pStringTable )
  44. {
  45. if ( IsX360() )
  46. {
  47. // not supporting
  48. return;
  49. }
  50. m_pStringTable = pStringTable;
  51. // reset the duplication list
  52. m_AlreadyWrittenFileNames.RemoveAll();
  53. // add in the bsp file to the list, and its node graph and nav mesh
  54. char path[_MAX_PATH];
  55. Q_snprintf(path, sizeof(path), "maps\\%s.bsp", m_mapName);
  56. OnResourcePrecached(path);
  57. bool useNodeGraph = true;
  58. KeyValues *modinfo = new KeyValues("ModInfo");
  59. if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
  60. {
  61. useNodeGraph = modinfo->GetInt( "nodegraph", 1 ) != 0;
  62. }
  63. modinfo->deleteThis();
  64. if ( useNodeGraph )
  65. {
  66. Q_snprintf(path, sizeof(path), "maps\\graphs\\%s.ain", m_mapName);
  67. OnResourcePrecached(path);
  68. }
  69. Q_snprintf(path, sizeof(path), "maps\\%s.nav", m_mapName);
  70. OnResourcePrecached(path);
  71. char resfilename[MAX_OSPATH];
  72. KeyValues *resfilekeys = new KeyValues( "resourefiles" );
  73. Q_snprintf( resfilename, sizeof( resfilename), "maps/%s.res", m_mapName );
  74. if ( resfilekeys->LoadFromFile( g_pFileSystem, resfilename, "GAME" ) )
  75. {
  76. KeyValues *entry = resfilekeys->GetFirstSubKey();
  77. while ( entry )
  78. {
  79. OnResourcePrecached( entry->GetName() );
  80. entry = entry->GetNextKey();
  81. }
  82. }
  83. resfilekeys->deleteThis();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: call to mark level load/end
  87. //-----------------------------------------------------------------------------
  88. void CDownloadListGenerator::OnLevelLoadStart(const char *levelName)
  89. {
  90. if ( IsX360() )
  91. {
  92. // not supporting
  93. return;
  94. }
  95. // close the previous level reslist, if any
  96. if (m_hReslistFile != FILESYSTEM_INVALID_HANDLE)
  97. {
  98. g_pFileSystem->Close(m_hReslistFile);
  99. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  100. }
  101. // reset the duplication list
  102. m_AlreadyWrittenFileNames.RemoveAll();
  103. if ( sv_logdownloadlist.GetBool() )
  104. {
  105. // open the new level reslist
  106. char path[MAX_OSPATH];
  107. g_pFileSystem->CreateDirHierarchy( "DownloadLists", "MOD" );
  108. Q_snprintf(path, sizeof(path), "DownloadLists/%s.lst", levelName);
  109. m_hReslistFile = g_pFileSystem->Open(path, "wt", "GAME");
  110. }
  111. // add a slash to the end of com_gamedir, so we can only deal with files for this mod
  112. Q_snprintf( m_gameDir, sizeof(m_gameDir), "%s/", com_gamedir );
  113. Q_FixSlashes( m_gameDir );
  114. // save off the map name
  115. Q_snprintf( m_mapName, sizeof( m_mapName ), "%s", levelName );
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose: call to mark level load/end
  119. //-----------------------------------------------------------------------------
  120. void CDownloadListGenerator::OnLevelLoadEnd()
  121. {
  122. if ( IsX360() )
  123. {
  124. // not supporting
  125. return;
  126. }
  127. if ( m_hReslistFile != FILESYSTEM_INVALID_HANDLE )
  128. {
  129. g_pFileSystem->Close(m_hReslistFile);
  130. m_hReslistFile = FILESYSTEM_INVALID_HANDLE;
  131. }
  132. m_pStringTable = NULL;
  133. }
  134. //-----------------------------------------------------------------------------
  135. // Purpose: logs and handles mdl files being precaches
  136. //-----------------------------------------------------------------------------
  137. void CDownloadListGenerator::OnModelPrecached(const char *relativePathFileName)
  138. {
  139. if ( IsX360() )
  140. {
  141. // not supporting
  142. return;
  143. }
  144. if (Q_strstr(relativePathFileName, ".vmt"))
  145. {
  146. // it's a materials file, make sure that it starts in the materials directory, and we get the .vtf
  147. char file[_MAX_PATH];
  148. if (!Q_strnicmp(relativePathFileName, "materials", strlen("materials")))
  149. {
  150. Q_strncpy(file, relativePathFileName, sizeof(file));
  151. }
  152. else
  153. {
  154. // prepend the materials directory
  155. Q_snprintf(file, sizeof(file), "materials\\%s", relativePathFileName);
  156. }
  157. OnResourcePrecached(file);
  158. // get the matching vtf file
  159. char *ext = Q_strstr(file, ".vmt");
  160. if (ext)
  161. {
  162. Q_strncpy(ext, ".vtf", 5);
  163. OnResourcePrecached(file);
  164. }
  165. }
  166. else
  167. {
  168. OnResourcePrecached(relativePathFileName);
  169. }
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: logs sound file access
  173. //-----------------------------------------------------------------------------
  174. void CDownloadListGenerator::OnSoundPrecached(const char *relativePathFileName)
  175. {
  176. if ( IsX360() )
  177. {
  178. // not supporting
  179. return;
  180. }
  181. // skip any special characters
  182. if (!V_isalnum(relativePathFileName[0]))
  183. {
  184. ++relativePathFileName;
  185. }
  186. // prepend the sound/ directory if necessary
  187. char file[_MAX_PATH];
  188. if (!Q_strnicmp(relativePathFileName, "sound", strlen("sound")))
  189. {
  190. Q_strncpy(file, relativePathFileName, sizeof(file));
  191. }
  192. else
  193. {
  194. // prepend the materials directory
  195. Q_snprintf(file, sizeof(file), "sound\\%s", relativePathFileName);
  196. }
  197. OnResourcePrecached(file);
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose: logs the precache as a file access
  201. //-----------------------------------------------------------------------------
  202. void CDownloadListGenerator::OnResourcePrecached(const char *relativePathFileName)
  203. {
  204. if ( IsX360() )
  205. {
  206. // not supporting
  207. return;
  208. }
  209. // ignore empty string
  210. if (relativePathFileName[0] == 0)
  211. {
  212. return;
  213. }
  214. // ignore files that start with '*' since they signify special models
  215. if (relativePathFileName[0] == '*')
  216. {
  217. return;
  218. }
  219. char fullPath[_MAX_PATH];
  220. if (g_pFileSystem->GetLocalPath(relativePathFileName, fullPath, sizeof(fullPath)))
  221. {
  222. OnResourcePrecachedFullPath( fullPath, relativePathFileName);
  223. }
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose: marks a precached file as needing a specific CRC on the client
  227. //-----------------------------------------------------------------------------
  228. void CDownloadListGenerator::ForceSimpleMaterial( const char *relativePathFileName )
  229. {
  230. if ( IsX360() )
  231. {
  232. // not supporting
  233. return;
  234. }
  235. if ( !m_pStringTable )
  236. return;
  237. if ( !Q_stristr(relativePathFileName, ".vmt") && !Q_stristr(relativePathFileName, ".vtf"))
  238. {
  239. DevMsg( "Tried to enforce simple material on %s\n", relativePathFileName );
  240. return;
  241. }
  242. // it's a materials file, make sure that it starts in the materials directory, and we get the .vtf
  243. char szFixedFilename[_MAX_PATH];
  244. if (!Q_strnicmp(relativePathFileName, "materials", strlen("materials")))
  245. {
  246. V_strcpy_safe( szFixedFilename, relativePathFileName );
  247. }
  248. else
  249. {
  250. // prepend the materials directory
  251. V_sprintf_safe( szFixedFilename, "materials\\%s", relativePathFileName );
  252. }
  253. V_FixSlashes( szFixedFilename );
  254. if ( !g_pFullFileSystem->FileExists( szFixedFilename, "game" ) )
  255. {
  256. DevMsg( "Cannot force simple material on %s; file not found\n", szFixedFilename );
  257. return;
  258. }
  259. ExactFileUserData userData;
  260. userData.consistencyType = CONSISTENCY_SIMPLE_MATERIAL;
  261. userData.crc = 0;
  262. // Only set consistency data if pure, otherwise just create entry in download list
  263. if ( GetSvPureMode() < 0 )
  264. {
  265. m_pStringTable->AddString( true, szFixedFilename );
  266. }
  267. else
  268. {
  269. int index = m_pStringTable->FindStringIndex( szFixedFilename );
  270. if ( index != INVALID_STRING_INDEX )
  271. {
  272. m_pStringTable->SetStringUserData( index, sizeof( ExactFileUserData ), &userData );
  273. }
  274. else
  275. {
  276. m_pStringTable->AddString( true, szFixedFilename, sizeof( ExactFileUserData ), &userData );
  277. }
  278. }
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose: marks a precached model as having a maximum size on the client
  282. //-----------------------------------------------------------------------------
  283. void CDownloadListGenerator::ForceModelBounds( const char *relativePathFileName, const Vector &mins, const Vector &maxs )
  284. {
  285. if ( IsX360() )
  286. {
  287. // not supporting
  288. return;
  289. }
  290. if ( !m_pStringTable )
  291. return;
  292. if ( !relativePathFileName )
  293. relativePathFileName = "";
  294. if (!Q_stristr(relativePathFileName, ".mdl"))
  295. {
  296. DevMsg( "Warning - trying to enforce model bounds on %s\n", relativePathFileName );
  297. return;
  298. }
  299. char relativeFileName[_MAX_PATH];
  300. Q_strncpy( relativeFileName, relativePathFileName, sizeof( relativeFileName ) );
  301. Q_FixSlashes( relativeFileName );
  302. // Only set consistency data if pure, otherwise just create entry in download list
  303. if ( GetSvPureMode() < 0 )
  304. {
  305. m_pStringTable->AddString( true, relativePathFileName );
  306. }
  307. else
  308. {
  309. ModelBoundsUserData userData;
  310. userData.consistencyType = CONSISTENCY_BOUNDS;
  311. userData.mins = mins;
  312. userData.maxs = maxs;
  313. int index = m_pStringTable->FindStringIndex( relativeFileName );
  314. if ( index != INVALID_STRING_INDEX )
  315. {
  316. m_pStringTable->SetStringUserData( index, sizeof( ModelBoundsUserData ), &userData );
  317. }
  318. else
  319. {
  320. m_pStringTable->AddString( true, relativeFileName, sizeof( ModelBoundsUserData ), &userData );
  321. }
  322. }
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Logs out file access to a file
  326. //-----------------------------------------------------------------------------
  327. void CDownloadListGenerator::OnResourcePrecachedFullPath( char *fullPathFileName, const char *relativeFileName )
  328. {
  329. if ( IsX360() )
  330. {
  331. // not supporting
  332. return;
  333. }
  334. Q_FixSlashes( fullPathFileName );
  335. if ( !g_pFileSystem->FileExists( fullPathFileName ) )
  336. {
  337. return; // don't allow files for download the server doesn't have
  338. }
  339. if ( Q_strncasecmp( m_gameDir, fullPathFileName, Q_strlen( m_gameDir ) ) )
  340. {
  341. return; // the game dir must be part of the full name
  342. }
  343. // make sure the filename hasn't already been written
  344. UtlSymId_t filename = m_AlreadyWrittenFileNames.Find( fullPathFileName );
  345. if ( filename != UTL_INVAL_SYMBOL )
  346. {
  347. return;
  348. }
  349. // record in list, so we don't write it again
  350. m_AlreadyWrittenFileNames.AddString( fullPathFileName );
  351. // add extras for mdl's
  352. if (Q_strstr(relativeFileName, ".mdl"))
  353. {
  354. // it's a model, get it's other files as well
  355. char file[_MAX_PATH];
  356. Q_strncpy(file, relativeFileName, sizeof(file) - 10);
  357. char *ext = Q_strstr(file, ".mdl");
  358. Q_strncpy(ext, ".vvd", 10);
  359. OnResourcePrecached(file);
  360. Q_strncpy(ext, ".ani", 10);
  361. OnResourcePrecached(file);
  362. Q_strncpy(ext, ".dx80.vtx", 10);
  363. OnResourcePrecached(file);
  364. Q_strncpy(ext, ".dx90.vtx", 10);
  365. OnResourcePrecached(file);
  366. Q_strncpy(ext, ".sw.vtx", 10);
  367. OnResourcePrecached(file);
  368. Q_strncpy(ext, ".phy", 10);
  369. OnResourcePrecached(file);
  370. Q_strncpy(ext, ".jpg", 10);
  371. OnResourcePrecached(file);
  372. }
  373. FileHandle_t handle = m_hReslistFile;
  374. if ( handle != FILESYSTEM_INVALID_HANDLE )
  375. {
  376. g_pFileSystem->Write("\"", 1, handle);
  377. g_pFileSystem->Write( relativeFileName, Q_strlen(relativeFileName), handle );
  378. g_pFileSystem->Write("\"\n", 2, handle);
  379. }
  380. if ( m_pStringTable )
  381. {
  382. m_pStringTable->AddString( true, relativeFileName );
  383. }
  384. }