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.

622 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include <direct.h>
  8. #include <io.h>
  9. #include <WorldSize.h>
  10. #include "Gameconfig.h"
  11. #include "GlobalFunctions.h"
  12. #include "fgdlib/HelperInfo.h"
  13. #include "hammer.h"
  14. #include "KeyValues.h"
  15. #include "MapDoc.h"
  16. #include "MapDoc.h"
  17. #include "MapEntity.h"
  18. #include "MapInstance.h"
  19. #include "MapWorld.h"
  20. #include "filesystem_tools.h"
  21. #include "TextureSystem.h"
  22. #include "tier1/strtools.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #pragma warning(disable:4244)
  26. const int MAX_ERRORS = 5;
  27. GameData *pGD;
  28. CGameConfig g_DefaultGameConfig;
  29. CGameConfig *g_pGameConfig = &g_DefaultGameConfig;
  30. float g_MAX_MAP_COORD = 4096;
  31. float g_MIN_MAP_COORD = -4096;
  32. //-----------------------------------------------------------------------------
  33. // Purpose:
  34. //-----------------------------------------------------------------------------
  35. CGameConfig *CGameConfig::GetActiveGame(void)
  36. {
  37. return g_pGameConfig;
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose:
  41. // Input : pGame -
  42. //-----------------------------------------------------------------------------
  43. void CGameConfig::SetActiveGame(CGameConfig *pGame)
  44. {
  45. if (pGame != NULL)
  46. {
  47. g_pGameConfig = pGame;
  48. pGD = &pGame->GD;
  49. if (pGame->mapformat == mfHalfLife)
  50. {
  51. g_MAX_MAP_COORD = 4096;
  52. g_MIN_MAP_COORD = -4096;
  53. }
  54. else
  55. {
  56. g_MAX_MAP_COORD = pGD->GetMaxMapCoord();
  57. g_MIN_MAP_COORD = pGD->GetMinMapCoord();
  58. }
  59. }
  60. else
  61. {
  62. g_pGameConfig = &g_DefaultGameConfig;
  63. pGD = NULL;
  64. g_MAX_MAP_COORD = 4096;
  65. g_MIN_MAP_COORD = -4096;
  66. }
  67. }
  68. //-----------------------------------------------------------------------------
  69. // Purpose: Constructor. Maintains a static counter uniquely identifying each
  70. // game configuration.
  71. //-----------------------------------------------------------------------------
  72. CGameConfig::CGameConfig(void)
  73. {
  74. nGDFiles = 0;
  75. textureformat = tfNone;
  76. m_fDefaultTextureScale = DEFAULT_TEXTURE_SCALE;
  77. m_nDefaultLightmapScale = DEFAULT_LIGHTMAP_SCALE;
  78. m_MaterialExcludeCount = 0;
  79. memset(szName, 0, sizeof(szName));
  80. memset(szExecutable, 0, sizeof(szExecutable));
  81. memset(szDefaultPoint, 0, sizeof(szDefaultPoint));
  82. memset(szDefaultSolid, 0, sizeof(szDefaultSolid));
  83. memset(szBSP, 0, sizeof(szBSP));
  84. memset(szLIGHT, 0, sizeof(szLIGHT));
  85. memset(szVIS, 0, sizeof(szVIS));
  86. memset(szMapDir, 0, sizeof(szMapDir));
  87. memset(m_szGameExeDir, 0, sizeof(m_szGameExeDir));
  88. memset(szBSPDir, 0, sizeof(szBSPDir));
  89. memset(m_szModDir, 0, sizeof(m_szModDir));
  90. strcpy(m_szCordonTexture, "BLACK");
  91. m_szSteamDir[0] = '\0';
  92. m_szSteamAppID[0] = '\0';
  93. static DWORD __dwID = 0;
  94. dwID = __dwID++;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Imports an old binary GameCfg.wc file.
  98. // Input : file -
  99. // fVersion -
  100. // Output : Returns TRUE on success, FALSE on failure.
  101. //-----------------------------------------------------------------------------
  102. BOOL CGameConfig::Import(std::fstream& file, float fVersion)
  103. {
  104. file.read(szName, sizeof szName);
  105. file.read((char*)&nGDFiles, sizeof nGDFiles);
  106. file.read((char*)&textureformat, sizeof textureformat);
  107. if (fVersion >= 1.1f)
  108. {
  109. file.read((char*)&mapformat, sizeof mapformat);
  110. }
  111. else
  112. {
  113. mapformat = mfQuake;
  114. }
  115. //
  116. // If reading an old (pre 1.4) format file, skip past the obselete palette
  117. // file path.
  118. //
  119. if (fVersion < 1.4f)
  120. {
  121. char szPalette[128];
  122. file.read(szPalette, sizeof szPalette);
  123. }
  124. file.read(szExecutable, sizeof szExecutable);
  125. file.read(szDefaultSolid, sizeof szDefaultSolid);
  126. file.read(szDefaultPoint, sizeof szDefaultPoint);
  127. if (fVersion >= 1.2f)
  128. {
  129. file.read(szBSP, sizeof szBSP);
  130. file.read(szLIGHT, sizeof szLIGHT);
  131. file.read(szVIS, sizeof szVIS);
  132. file.read(m_szGameExeDir, sizeof m_szGameExeDir);
  133. file.read(szMapDir, sizeof szMapDir);
  134. }
  135. if (fVersion >= 1.3f)
  136. {
  137. file.read(szBSPDir, sizeof(szBSPDir));
  138. }
  139. if (fVersion >= 1.4f)
  140. {
  141. // CSG setting is gone now.
  142. char szTempCSG[128];
  143. file.read(szTempCSG, sizeof(szTempCSG));
  144. file.read(m_szModDir, sizeof(m_szModDir));
  145. // gamedir is gone now.
  146. char tempGameDir[128];
  147. file.read(tempGameDir, sizeof(tempGameDir));
  148. }
  149. // read game data files
  150. char szBuf[128];
  151. for(int i = 0; i < nGDFiles; i++)
  152. {
  153. file.read(szBuf, sizeof szBuf);
  154. GDFiles.Add(CString(szBuf));
  155. }
  156. LoadGDFiles();
  157. return TRUE;
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: Loads this game configuration from a keyvalue block.
  161. // Output : Returns true on success, false on failure.
  162. //-----------------------------------------------------------------------------
  163. bool CGameConfig::Load(KeyValues *pkv)
  164. {
  165. char szKey[MAX_PATH];
  166. // We should at least be able to get the game name and the game dir.
  167. Q_strncpy(szName, pkv->GetName(), sizeof(szName));
  168. Q_strncpy(m_szModDir, pkv->GetString("GameDir"), sizeof(m_szModDir));
  169. // Try to get the Hammer settings.
  170. KeyValues *pkvHammer = pkv->FindKey("Hammer");
  171. if (!pkvHammer)
  172. return true;
  173. //
  174. // Load the game data filenames from the "GameData0..GameDataN" keys.
  175. //
  176. nGDFiles = 0;
  177. bool bAdded = true;
  178. do
  179. {
  180. sprintf(szKey, "GameData%d", nGDFiles);
  181. const char *pszGameData = pkvHammer->GetString(szKey);
  182. if (pszGameData[0] != '\0')
  183. {
  184. GDFiles.Add(pszGameData);
  185. nGDFiles++;
  186. }
  187. else
  188. {
  189. bAdded = false;
  190. }
  191. } while (bAdded);
  192. textureformat = (TEXTUREFORMAT)pkvHammer->GetInt("TextureFormat", tfVMT);
  193. mapformat = (MAPFORMAT)pkvHammer->GetInt("MapFormat", mfHalfLife2);
  194. m_fDefaultTextureScale = pkvHammer->GetFloat("DefaultTextureScale", DEFAULT_TEXTURE_SCALE);
  195. if (m_fDefaultTextureScale == 0)
  196. {
  197. m_fDefaultTextureScale = DEFAULT_TEXTURE_SCALE;
  198. }
  199. m_nDefaultLightmapScale = pkvHammer->GetInt("DefaultLightmapScale", DEFAULT_LIGHTMAP_SCALE);
  200. Q_strncpy(szExecutable, pkvHammer->GetString("GameExe"), sizeof(szExecutable));
  201. Q_strncpy(szDefaultSolid, pkvHammer->GetString("DefaultSolidEntity"), sizeof(szDefaultSolid));
  202. Q_strncpy(szDefaultPoint, pkvHammer->GetString("DefaultPointEntity"), sizeof(szDefaultPoint));
  203. Q_strncpy(szBSP, pkvHammer->GetString("BSP"), sizeof(szBSP));
  204. Q_strncpy(szVIS, pkvHammer->GetString("Vis"), sizeof(szVIS));
  205. Q_strncpy(szLIGHT, pkvHammer->GetString("Light"), sizeof(szLIGHT));
  206. Q_strncpy(m_szGameExeDir, pkvHammer->GetString("GameExeDir"), sizeof(m_szGameExeDir));
  207. Q_strncpy(szMapDir, pkvHammer->GetString("MapDir"), sizeof(szMapDir));
  208. Q_strncpy(szBSPDir, pkvHammer->GetString("BSPDir"), sizeof(szBSPDir));
  209. SetCordonTexture( pkvHammer->GetString("CordonTexture", "BLACK") );
  210. char szExcludeDir[MAX_PATH];
  211. m_MaterialExcludeCount = pkvHammer->GetInt( "MaterialExcludeCount" );
  212. for ( int i = 0; i < m_MaterialExcludeCount; i++ )
  213. {
  214. sprintf( szExcludeDir, "-MaterialExcludeDir%d", i );
  215. int index = m_MaterialExclusions.AddToTail();
  216. Q_strncpy( m_MaterialExclusions[index].szDirectory, pkvHammer->GetString( szExcludeDir ), sizeof( m_MaterialExclusions[index].szDirectory ) );
  217. Q_StripTrailingSlash( m_MaterialExclusions[index].szDirectory );
  218. m_MaterialExclusions[index].bUserGenerated = true;
  219. }
  220. LoadGDFiles();
  221. return(true);
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose: Saves this config's data into a keyvalues object.
  225. // Output : Returns true on success, false on failure.
  226. //-----------------------------------------------------------------------------
  227. bool CGameConfig::Save(KeyValues *pkv)
  228. {
  229. pkv->SetName(szName);
  230. pkv->SetString("GameDir", m_szModDir);
  231. // Try to get the Hammer settings.
  232. KeyValues *pkvHammer = pkv->FindKey("Hammer");
  233. if (pkvHammer)
  234. {
  235. pkv->RemoveSubKey(pkvHammer);
  236. pkvHammer->deleteThis();
  237. }
  238. pkvHammer = pkv->CreateNewKey();
  239. if (!pkvHammer)
  240. return false;
  241. pkvHammer->SetName("Hammer");
  242. //
  243. // Load the game data filenames from the "GameData0..GameDataN" keys.
  244. //
  245. for (int i = 0; i < nGDFiles; i++)
  246. {
  247. char szKey[MAX_PATH];
  248. sprintf(szKey, "GameData%d", i);
  249. pkvHammer->SetString(szKey, GDFiles.GetAt(i));
  250. }
  251. pkvHammer->SetInt("TextureFormat", textureformat);
  252. pkvHammer->SetInt("MapFormat", mapformat);
  253. pkvHammer->SetFloat("DefaultTextureScale", m_fDefaultTextureScale);
  254. pkvHammer->SetInt("DefaultLightmapScale", m_nDefaultLightmapScale);
  255. pkvHammer->SetString("GameExe", szExecutable);
  256. pkvHammer->SetString("DefaultSolidEntity", szDefaultSolid);
  257. pkvHammer->SetString("DefaultPointEntity", szDefaultPoint);
  258. pkvHammer->SetString("BSP", szBSP);
  259. pkvHammer->SetString("Vis", szVIS);
  260. pkvHammer->SetString("Light", szLIGHT);
  261. pkvHammer->SetString("GameExeDir", m_szGameExeDir);
  262. pkvHammer->SetString("MapDir", szMapDir);
  263. pkvHammer->SetString("BSPDir", szBSPDir);
  264. pkvHammer->SetString("CordonTexture", m_szCordonTexture);
  265. char szExcludeDir[MAX_PATH];
  266. pkvHammer->SetInt("MaterialExcludeCount", m_MaterialExcludeCount);
  267. for (int i = 0; i < m_MaterialExcludeCount; i++)
  268. {
  269. sprintf(szExcludeDir, "-MaterialExcludeDir%d", i );
  270. pkvHammer->SetString(szExcludeDir, m_MaterialExclusions[i].szDirectory);
  271. }
  272. return true;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. // Input : file -
  277. //-----------------------------------------------------------------------------
  278. void CGameConfig::Save(std::fstream &file)
  279. {
  280. file.write(szName, sizeof szName);
  281. file.write((char*)&nGDFiles, sizeof nGDFiles);
  282. file.write((char*)&textureformat, sizeof textureformat);
  283. file.write((char*)&mapformat, sizeof mapformat);
  284. file.write(szExecutable, sizeof szExecutable);
  285. file.write(szDefaultSolid, sizeof szDefaultSolid);
  286. file.write(szDefaultPoint, sizeof szDefaultPoint);
  287. // 1.2
  288. file.write(szBSP, sizeof szBSP);
  289. file.write(szLIGHT, sizeof szLIGHT);
  290. file.write(szVIS, sizeof szVIS);
  291. file.write(m_szGameExeDir, sizeof(m_szGameExeDir));
  292. file.write(szMapDir, sizeof szMapDir);
  293. // 1.3
  294. file.write(szBSPDir, sizeof szBSPDir);
  295. // 1.4
  296. char tempCSG[128] = "";
  297. file.write(tempCSG, sizeof(tempCSG));
  298. file.write(m_szModDir, sizeof(m_szModDir));
  299. char tempGameDir[128] = "";
  300. file.write(tempGameDir, sizeof(tempGameDir));
  301. // write game data files
  302. char szBuf[128];
  303. for(int i = 0; i < nGDFiles; i++)
  304. {
  305. strcpy(szBuf, GDFiles[i]);
  306. file.write(szBuf, sizeof szBuf);
  307. }
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. // Input : *pConfig -
  312. //-----------------------------------------------------------------------------
  313. void CGameConfig::CopyFrom(CGameConfig *pConfig)
  314. {
  315. nGDFiles = pConfig->nGDFiles;
  316. GDFiles.RemoveAll();
  317. GDFiles.Append(pConfig->GDFiles);
  318. strcpy(szName, pConfig->szName);
  319. strcpy(szExecutable, pConfig->szExecutable);
  320. strcpy(szDefaultPoint, pConfig->szDefaultPoint);
  321. strcpy(szDefaultSolid, pConfig->szDefaultSolid);
  322. strcpy(szBSP, pConfig->szBSP);
  323. strcpy(szLIGHT, pConfig->szLIGHT);
  324. strcpy(szVIS, pConfig->szVIS);
  325. strcpy(szMapDir, pConfig->szMapDir);
  326. strcpy(m_szGameExeDir, pConfig->m_szGameExeDir);
  327. strcpy(szBSPDir, pConfig->szBSPDir);
  328. strcpy(m_szModDir, pConfig->m_szModDir);
  329. pConfig->m_MaterialExcludeCount = m_MaterialExcludeCount;
  330. for( int i = 0; i < m_MaterialExcludeCount; i++ )
  331. {
  332. strcpy( m_MaterialExclusions[i].szDirectory, pConfig->m_MaterialExclusions[i].szDirectory );
  333. }
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose:
  337. // Input : pEntity -
  338. // pGD -
  339. // Output : Returns TRUE to keep enumerating.
  340. //-----------------------------------------------------------------------------
  341. static BOOL UpdateClassPointer(CMapEntity *pEntity, GameData *pGDIn)
  342. {
  343. GDclass *pClass = pGDIn->ClassForName(pEntity->GetClassName());
  344. pEntity->SetClass(pClass);
  345. return(TRUE);
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose:
  349. //-----------------------------------------------------------------------------
  350. void CGameConfig::LoadGDFiles(void)
  351. {
  352. GD.ClearData();
  353. // Save the old working directory
  354. char szOldDir[MAX_PATH];
  355. _getcwd( szOldDir, sizeof(szOldDir) );
  356. // Set our working directory properly
  357. char szAppDir[MAX_PATH];
  358. APP()->GetDirectory( DIR_PROGRAM, szAppDir );
  359. _chdir( szAppDir );
  360. for (int i = 0; i < nGDFiles; i++)
  361. {
  362. GD.Load(GDFiles[i]);
  363. }
  364. // Reset our old working directory
  365. _chdir( szOldDir );
  366. // All the class pointers have changed - now we have to
  367. // reset all the class pointers in each map doc that
  368. // uses this game.
  369. for ( int i=0; i<CMapDoc::GetDocumentCount(); i++ )
  370. {
  371. CMapDoc *pDoc = CMapDoc::GetDocument(i);
  372. if (pDoc->GetGame() == this)
  373. {
  374. CMapWorld *pWorld = pDoc->GetMapWorld();
  375. pWorld->SetClass(GD.ClassForName(pWorld->GetClassName()));
  376. pWorld->EnumChildren((ENUMMAPCHILDRENPROC)UpdateClassPointer, (DWORD)&GD, MAPCLASS_TYPE(CMapEntity));
  377. }
  378. }
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose: Searches for the given filename, starting in szStartDir and looking
  382. // up the directory tree.
  383. // Input: szFile - the name of the file to search for.
  384. // szStartDir - the folder to start searching from, towards the root.
  385. // szFoundPath - receives the full path of the FOLDER where szFile was found.
  386. // Output : Returns true if the file was found, false if not. If the file was
  387. // found the full path (not including the filename) is returned in szFoundPath.
  388. //-----------------------------------------------------------------------------
  389. bool FindFileInTree(const char *szFile, const char *szStartDir, char *szFoundPath)
  390. {
  391. if ((szFile == NULL) || (szStartDir == NULL) || (szFoundPath == NULL))
  392. {
  393. return false;
  394. }
  395. char szRoot[MAX_PATH];
  396. strcpy(szRoot, szStartDir);
  397. Q_AppendSlash(szRoot, sizeof(szRoot));
  398. char szTemp[MAX_PATH];
  399. do
  400. {
  401. strcpy(szTemp, szRoot);
  402. strcat(szTemp, szFile);
  403. if (!_access(szTemp, 0))
  404. {
  405. strcpy(szFoundPath, szRoot);
  406. Q_StripTrailingSlash(szFoundPath);
  407. return true;
  408. }
  409. } while (Q_StripLastDir(szRoot, sizeof(szRoot)));
  410. return false;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose:
  414. // Input : *szDir -
  415. // *szSteamDir -
  416. // *szSteamUserDir -
  417. // Output : Returns true on success, false on failure.
  418. //-----------------------------------------------------------------------------
  419. bool FindSteamUserDir(const char *szAppDir, const char *szSteamDir, char *szSteamUserDir)
  420. {
  421. if ((szAppDir == NULL) || (szSteamDir == NULL) || (szSteamUserDir == NULL))
  422. {
  423. return false;
  424. }
  425. // If the szAppDir was run from within the steam tree, we should be able to find the steam user dir.
  426. int nSteamDirLen = strlen(szSteamDir);
  427. if (!Q_strnicmp(szAppDir, szSteamDir, nSteamDirLen ) && (szAppDir[nSteamDirLen] == '\\'))
  428. {
  429. strcpy(szSteamUserDir, szAppDir);
  430. char *pszSlash = strchr(&szSteamUserDir[nSteamDirLen + 1], '\\');
  431. if (pszSlash)
  432. {
  433. pszSlash++;
  434. pszSlash = strchr(pszSlash, '\\');
  435. if (pszSlash)
  436. {
  437. *pszSlash = '\0';
  438. return true;
  439. }
  440. }
  441. }
  442. szSteamUserDir[0] = '\0';
  443. return false;
  444. }
  445. // memdbgon must be the last include file in a .cpp file!!!
  446. #include "tier0/memdbgoff.h"
  447. //-----------------------------------------------------------------------------
  448. // Purpose: Loads the settings from <mod dir>\gameinfo.txt into data members.
  449. //-----------------------------------------------------------------------------
  450. void CGameConfig::ParseGameInfo()
  451. {
  452. KeyValues *pkv = new KeyValues("gameinfo.txt");
  453. if (!pkv->LoadFromFile(g_pFileSystem, "gameinfo.txt", "GAME"))
  454. {
  455. pkv->deleteThis();
  456. return;
  457. }
  458. KeyValues *pKey = pkv->FindKey("FileSystem");
  459. if (pKey)
  460. {
  461. V_strcpy_safe( m_szSteamAppID, pKey->GetString( "SteamAppId", "" ) );
  462. }
  463. const char *InstancePath = pkv->GetString( "InstancePath", NULL );
  464. if ( InstancePath )
  465. {
  466. CMapInstance::SetInstancePath( InstancePath );
  467. }
  468. pkv->deleteThis();
  469. char szAppDir[MAX_PATH];
  470. APP()->GetDirectory(DIR_PROGRAM, szAppDir);
  471. if (!FindFileInTree("steam.exe", szAppDir, m_szSteamDir))
  472. {
  473. // Couldn't find steam.exe in the hammer tree
  474. m_szSteamDir[0] = '\0';
  475. }
  476. if (!FindSteamUserDir(szAppDir, m_szSteamDir, m_szSteamUserDir))
  477. {
  478. m_szSteamUserDir[0] = '\0';
  479. }
  480. }
  481. //-----------------------------------------------------------------------------
  482. // Accessor methods to get at the mod + the game (*not* full paths)
  483. //-----------------------------------------------------------------------------
  484. const char *CGameConfig::GetMod()
  485. {
  486. // Strip path from modDir
  487. char szModPath[MAX_PATH];
  488. static char szMod[MAX_PATH];
  489. Q_strncpy( szModPath, m_szModDir, MAX_PATH );
  490. Q_StripTrailingSlash( szModPath );
  491. if ( !szModPath[0] )
  492. {
  493. Q_strcpy( szModPath, "hl2" );
  494. }
  495. Q_FileBase( szModPath, szMod, MAX_PATH );
  496. return szMod;
  497. }
  498. const char *CGameConfig::GetGame()
  499. {
  500. return "hl2";
  501. // // Strip path from modDir
  502. // char szGamePath[MAX_PATH];
  503. // static char szGame[MAX_PATH];
  504. // Q_strncpy( szGamePath, m_szGameDir, MAX_PATH );
  505. // Q_StripTrailingSlash( szGamePath );
  506. // Q_FileBase( szGamePath, szGame, MAX_PATH );
  507. // return szGame;
  508. }