//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "stdafx.h" #include #include #include #include "Gameconfig.h" #include "GlobalFunctions.h" #include "fgdlib/HelperInfo.h" #include "hammer.h" #include "KeyValues.h" #include "MapDoc.h" #include "MapDoc.h" #include "MapEntity.h" #include "MapInstance.h" #include "MapWorld.h" #include "filesystem_tools.h" #include "TextureSystem.h" #include "tier1/strtools.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #pragma warning(disable:4244) const int MAX_ERRORS = 5; GameData *pGD; CGameConfig g_DefaultGameConfig; CGameConfig *g_pGameConfig = &g_DefaultGameConfig; float g_MAX_MAP_COORD = 4096; float g_MIN_MAP_COORD = -4096; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CGameConfig *CGameConfig::GetActiveGame(void) { return g_pGameConfig; } //----------------------------------------------------------------------------- // Purpose: // Input : pGame - //----------------------------------------------------------------------------- void CGameConfig::SetActiveGame(CGameConfig *pGame) { if (pGame != NULL) { g_pGameConfig = pGame; pGD = &pGame->GD; if (pGame->mapformat == mfHalfLife) { g_MAX_MAP_COORD = 4096; g_MIN_MAP_COORD = -4096; } else { g_MAX_MAP_COORD = pGD->GetMaxMapCoord(); g_MIN_MAP_COORD = pGD->GetMinMapCoord(); } } else { g_pGameConfig = &g_DefaultGameConfig; pGD = NULL; g_MAX_MAP_COORD = 4096; g_MIN_MAP_COORD = -4096; } } //----------------------------------------------------------------------------- // Purpose: Constructor. Maintains a static counter uniquely identifying each // game configuration. //----------------------------------------------------------------------------- CGameConfig::CGameConfig(void) { nGDFiles = 0; textureformat = tfNone; m_fDefaultTextureScale = DEFAULT_TEXTURE_SCALE; m_nDefaultLightmapScale = DEFAULT_LIGHTMAP_SCALE; m_MaterialExcludeCount = 0; memset(szName, 0, sizeof(szName)); memset(szExecutable, 0, sizeof(szExecutable)); memset(szDefaultPoint, 0, sizeof(szDefaultPoint)); memset(szDefaultSolid, 0, sizeof(szDefaultSolid)); memset(szBSP, 0, sizeof(szBSP)); memset(szLIGHT, 0, sizeof(szLIGHT)); memset(szVIS, 0, sizeof(szVIS)); memset(szMapDir, 0, sizeof(szMapDir)); memset(m_szGameExeDir, 0, sizeof(m_szGameExeDir)); memset(szBSPDir, 0, sizeof(szBSPDir)); memset(m_szModDir, 0, sizeof(m_szModDir)); strcpy(m_szCordonTexture, "BLACK"); m_szSteamDir[0] = '\0'; m_szSteamAppID[0] = '\0'; static DWORD __dwID = 0; dwID = __dwID++; } //----------------------------------------------------------------------------- // Purpose: Imports an old binary GameCfg.wc file. // Input : file - // fVersion - // Output : Returns TRUE on success, FALSE on failure. //----------------------------------------------------------------------------- BOOL CGameConfig::Import(std::fstream& file, float fVersion) { file.read(szName, sizeof szName); file.read((char*)&nGDFiles, sizeof nGDFiles); file.read((char*)&textureformat, sizeof textureformat); if (fVersion >= 1.1f) { file.read((char*)&mapformat, sizeof mapformat); } else { mapformat = mfQuake; } // // If reading an old (pre 1.4) format file, skip past the obselete palette // file path. // if (fVersion < 1.4f) { char szPalette[128]; file.read(szPalette, sizeof szPalette); } file.read(szExecutable, sizeof szExecutable); file.read(szDefaultSolid, sizeof szDefaultSolid); file.read(szDefaultPoint, sizeof szDefaultPoint); if (fVersion >= 1.2f) { file.read(szBSP, sizeof szBSP); file.read(szLIGHT, sizeof szLIGHT); file.read(szVIS, sizeof szVIS); file.read(m_szGameExeDir, sizeof m_szGameExeDir); file.read(szMapDir, sizeof szMapDir); } if (fVersion >= 1.3f) { file.read(szBSPDir, sizeof(szBSPDir)); } if (fVersion >= 1.4f) { // CSG setting is gone now. char szTempCSG[128]; file.read(szTempCSG, sizeof(szTempCSG)); file.read(m_szModDir, sizeof(m_szModDir)); // gamedir is gone now. char tempGameDir[128]; file.read(tempGameDir, sizeof(tempGameDir)); } // read game data files char szBuf[128]; for(int i = 0; i < nGDFiles; i++) { file.read(szBuf, sizeof szBuf); GDFiles.Add(CString(szBuf)); } LoadGDFiles(); return TRUE; } //----------------------------------------------------------------------------- // Purpose: Loads this game configuration from a keyvalue block. // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CGameConfig::Load(KeyValues *pkv) { char szKey[MAX_PATH]; // We should at least be able to get the game name and the game dir. Q_strncpy(szName, pkv->GetName(), sizeof(szName)); Q_strncpy(m_szModDir, pkv->GetString("GameDir"), sizeof(m_szModDir)); // Try to get the Hammer settings. KeyValues *pkvHammer = pkv->FindKey("Hammer"); if (!pkvHammer) return true; // // Load the game data filenames from the "GameData0..GameDataN" keys. // nGDFiles = 0; bool bAdded = true; do { sprintf(szKey, "GameData%d", nGDFiles); const char *pszGameData = pkvHammer->GetString(szKey); if (pszGameData[0] != '\0') { GDFiles.Add(pszGameData); nGDFiles++; } else { bAdded = false; } } while (bAdded); textureformat = (TEXTUREFORMAT)pkvHammer->GetInt("TextureFormat", tfVMT); mapformat = (MAPFORMAT)pkvHammer->GetInt("MapFormat", mfHalfLife2); m_fDefaultTextureScale = pkvHammer->GetFloat("DefaultTextureScale", DEFAULT_TEXTURE_SCALE); if (m_fDefaultTextureScale == 0) { m_fDefaultTextureScale = DEFAULT_TEXTURE_SCALE; } m_nDefaultLightmapScale = pkvHammer->GetInt("DefaultLightmapScale", DEFAULT_LIGHTMAP_SCALE); Q_strncpy(szExecutable, pkvHammer->GetString("GameExe"), sizeof(szExecutable)); Q_strncpy(szDefaultSolid, pkvHammer->GetString("DefaultSolidEntity"), sizeof(szDefaultSolid)); Q_strncpy(szDefaultPoint, pkvHammer->GetString("DefaultPointEntity"), sizeof(szDefaultPoint)); Q_strncpy(szBSP, pkvHammer->GetString("BSP"), sizeof(szBSP)); Q_strncpy(szVIS, pkvHammer->GetString("Vis"), sizeof(szVIS)); Q_strncpy(szLIGHT, pkvHammer->GetString("Light"), sizeof(szLIGHT)); Q_strncpy(m_szGameExeDir, pkvHammer->GetString("GameExeDir"), sizeof(m_szGameExeDir)); Q_strncpy(szMapDir, pkvHammer->GetString("MapDir"), sizeof(szMapDir)); Q_strncpy(szBSPDir, pkvHammer->GetString("BSPDir"), sizeof(szBSPDir)); SetCordonTexture( pkvHammer->GetString("CordonTexture", "BLACK") ); char szExcludeDir[MAX_PATH]; m_MaterialExcludeCount = pkvHammer->GetInt( "MaterialExcludeCount" ); for ( int i = 0; i < m_MaterialExcludeCount; i++ ) { sprintf( szExcludeDir, "-MaterialExcludeDir%d", i ); int index = m_MaterialExclusions.AddToTail(); Q_strncpy( m_MaterialExclusions[index].szDirectory, pkvHammer->GetString( szExcludeDir ), sizeof( m_MaterialExclusions[index].szDirectory ) ); Q_StripTrailingSlash( m_MaterialExclusions[index].szDirectory ); m_MaterialExclusions[index].bUserGenerated = true; } LoadGDFiles(); return(true); } //----------------------------------------------------------------------------- // Purpose: Saves this config's data into a keyvalues object. // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CGameConfig::Save(KeyValues *pkv) { pkv->SetName(szName); pkv->SetString("GameDir", m_szModDir); // Try to get the Hammer settings. KeyValues *pkvHammer = pkv->FindKey("Hammer"); if (pkvHammer) { pkv->RemoveSubKey(pkvHammer); pkvHammer->deleteThis(); } pkvHammer = pkv->CreateNewKey(); if (!pkvHammer) return false; pkvHammer->SetName("Hammer"); // // Load the game data filenames from the "GameData0..GameDataN" keys. // for (int i = 0; i < nGDFiles; i++) { char szKey[MAX_PATH]; sprintf(szKey, "GameData%d", i); pkvHammer->SetString(szKey, GDFiles.GetAt(i)); } pkvHammer->SetInt("TextureFormat", textureformat); pkvHammer->SetInt("MapFormat", mapformat); pkvHammer->SetFloat("DefaultTextureScale", m_fDefaultTextureScale); pkvHammer->SetInt("DefaultLightmapScale", m_nDefaultLightmapScale); pkvHammer->SetString("GameExe", szExecutable); pkvHammer->SetString("DefaultSolidEntity", szDefaultSolid); pkvHammer->SetString("DefaultPointEntity", szDefaultPoint); pkvHammer->SetString("BSP", szBSP); pkvHammer->SetString("Vis", szVIS); pkvHammer->SetString("Light", szLIGHT); pkvHammer->SetString("GameExeDir", m_szGameExeDir); pkvHammer->SetString("MapDir", szMapDir); pkvHammer->SetString("BSPDir", szBSPDir); pkvHammer->SetString("CordonTexture", m_szCordonTexture); char szExcludeDir[MAX_PATH]; pkvHammer->SetInt("MaterialExcludeCount", m_MaterialExcludeCount); for (int i = 0; i < m_MaterialExcludeCount; i++) { sprintf(szExcludeDir, "-MaterialExcludeDir%d", i ); pkvHammer->SetString(szExcludeDir, m_MaterialExclusions[i].szDirectory); } return true; } //----------------------------------------------------------------------------- // Purpose: // Input : file - //----------------------------------------------------------------------------- void CGameConfig::Save(std::fstream &file) { file.write(szName, sizeof szName); file.write((char*)&nGDFiles, sizeof nGDFiles); file.write((char*)&textureformat, sizeof textureformat); file.write((char*)&mapformat, sizeof mapformat); file.write(szExecutable, sizeof szExecutable); file.write(szDefaultSolid, sizeof szDefaultSolid); file.write(szDefaultPoint, sizeof szDefaultPoint); // 1.2 file.write(szBSP, sizeof szBSP); file.write(szLIGHT, sizeof szLIGHT); file.write(szVIS, sizeof szVIS); file.write(m_szGameExeDir, sizeof(m_szGameExeDir)); file.write(szMapDir, sizeof szMapDir); // 1.3 file.write(szBSPDir, sizeof szBSPDir); // 1.4 char tempCSG[128] = ""; file.write(tempCSG, sizeof(tempCSG)); file.write(m_szModDir, sizeof(m_szModDir)); char tempGameDir[128] = ""; file.write(tempGameDir, sizeof(tempGameDir)); // write game data files char szBuf[128]; for(int i = 0; i < nGDFiles; i++) { strcpy(szBuf, GDFiles[i]); file.write(szBuf, sizeof szBuf); } } //----------------------------------------------------------------------------- // Purpose: // Input : *pConfig - //----------------------------------------------------------------------------- void CGameConfig::CopyFrom(CGameConfig *pConfig) { nGDFiles = pConfig->nGDFiles; GDFiles.RemoveAll(); GDFiles.Append(pConfig->GDFiles); strcpy(szName, pConfig->szName); strcpy(szExecutable, pConfig->szExecutable); strcpy(szDefaultPoint, pConfig->szDefaultPoint); strcpy(szDefaultSolid, pConfig->szDefaultSolid); strcpy(szBSP, pConfig->szBSP); strcpy(szLIGHT, pConfig->szLIGHT); strcpy(szVIS, pConfig->szVIS); strcpy(szMapDir, pConfig->szMapDir); strcpy(m_szGameExeDir, pConfig->m_szGameExeDir); strcpy(szBSPDir, pConfig->szBSPDir); strcpy(m_szModDir, pConfig->m_szModDir); pConfig->m_MaterialExcludeCount = m_MaterialExcludeCount; for( int i = 0; i < m_MaterialExcludeCount; i++ ) { strcpy( m_MaterialExclusions[i].szDirectory, pConfig->m_MaterialExclusions[i].szDirectory ); } } //----------------------------------------------------------------------------- // Purpose: // Input : pEntity - // pGD - // Output : Returns TRUE to keep enumerating. //----------------------------------------------------------------------------- static BOOL UpdateClassPointer(CMapEntity *pEntity, GameData *pGDIn) { GDclass *pClass = pGDIn->ClassForName(pEntity->GetClassName()); pEntity->SetClass(pClass); return(TRUE); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGameConfig::LoadGDFiles(void) { GD.ClearData(); // Save the old working directory char szOldDir[MAX_PATH]; _getcwd( szOldDir, sizeof(szOldDir) ); // Set our working directory properly char szAppDir[MAX_PATH]; APP()->GetDirectory( DIR_PROGRAM, szAppDir ); _chdir( szAppDir ); for (int i = 0; i < nGDFiles; i++) { GD.Load(GDFiles[i]); } // Reset our old working directory _chdir( szOldDir ); // All the class pointers have changed - now we have to // reset all the class pointers in each map doc that // uses this game. for ( int i=0; iGetGame() == this) { CMapWorld *pWorld = pDoc->GetMapWorld(); pWorld->SetClass(GD.ClassForName(pWorld->GetClassName())); pWorld->EnumChildren((ENUMMAPCHILDRENPROC)UpdateClassPointer, (DWORD)&GD, MAPCLASS_TYPE(CMapEntity)); } } } //----------------------------------------------------------------------------- // Purpose: Searches for the given filename, starting in szStartDir and looking // up the directory tree. // Input: szFile - the name of the file to search for. // szStartDir - the folder to start searching from, towards the root. // szFoundPath - receives the full path of the FOLDER where szFile was found. // Output : Returns true if the file was found, false if not. If the file was // found the full path (not including the filename) is returned in szFoundPath. //----------------------------------------------------------------------------- bool FindFileInTree(const char *szFile, const char *szStartDir, char *szFoundPath) { if ((szFile == NULL) || (szStartDir == NULL) || (szFoundPath == NULL)) { return false; } char szRoot[MAX_PATH]; strcpy(szRoot, szStartDir); Q_AppendSlash(szRoot, sizeof(szRoot)); char szTemp[MAX_PATH]; do { strcpy(szTemp, szRoot); strcat(szTemp, szFile); if (!_access(szTemp, 0)) { strcpy(szFoundPath, szRoot); Q_StripTrailingSlash(szFoundPath); return true; } } while (Q_StripLastDir(szRoot, sizeof(szRoot))); return false; } //----------------------------------------------------------------------------- // Purpose: // Input : *szDir - // *szSteamDir - // *szSteamUserDir - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool FindSteamUserDir(const char *szAppDir, const char *szSteamDir, char *szSteamUserDir) { if ((szAppDir == NULL) || (szSteamDir == NULL) || (szSteamUserDir == NULL)) { return false; } // If the szAppDir was run from within the steam tree, we should be able to find the steam user dir. int nSteamDirLen = strlen(szSteamDir); if (!Q_strnicmp(szAppDir, szSteamDir, nSteamDirLen ) && (szAppDir[nSteamDirLen] == '\\')) { strcpy(szSteamUserDir, szAppDir); char *pszSlash = strchr(&szSteamUserDir[nSteamDirLen + 1], '\\'); if (pszSlash) { pszSlash++; pszSlash = strchr(pszSlash, '\\'); if (pszSlash) { *pszSlash = '\0'; return true; } } } szSteamUserDir[0] = '\0'; return false; } // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgoff.h" //----------------------------------------------------------------------------- // Purpose: Loads the settings from \gameinfo.txt into data members. //----------------------------------------------------------------------------- void CGameConfig::ParseGameInfo() { KeyValues *pkv = new KeyValues("gameinfo.txt"); if (!pkv->LoadFromFile(g_pFileSystem, "gameinfo.txt", "GAME")) { pkv->deleteThis(); return; } KeyValues *pKey = pkv->FindKey("FileSystem"); if (pKey) { V_strcpy_safe( m_szSteamAppID, pKey->GetString( "SteamAppId", "" ) ); } const char *InstancePath = pkv->GetString( "InstancePath", NULL ); if ( InstancePath ) { CMapInstance::SetInstancePath( InstancePath ); } pkv->deleteThis(); char szAppDir[MAX_PATH]; APP()->GetDirectory(DIR_PROGRAM, szAppDir); if (!FindFileInTree("steam.exe", szAppDir, m_szSteamDir)) { // Couldn't find steam.exe in the hammer tree m_szSteamDir[0] = '\0'; } if (!FindSteamUserDir(szAppDir, m_szSteamDir, m_szSteamUserDir)) { m_szSteamUserDir[0] = '\0'; } } //----------------------------------------------------------------------------- // Accessor methods to get at the mod + the game (*not* full paths) //----------------------------------------------------------------------------- const char *CGameConfig::GetMod() { // Strip path from modDir char szModPath[MAX_PATH]; static char szMod[MAX_PATH]; Q_strncpy( szModPath, m_szModDir, MAX_PATH ); Q_StripTrailingSlash( szModPath ); if ( !szModPath[0] ) { Q_strcpy( szModPath, "hl2" ); } Q_FileBase( szModPath, szMod, MAX_PATH ); return szMod; } const char *CGameConfig::GetGame() { return "hl2"; // // Strip path from modDir // char szGamePath[MAX_PATH]; // static char szGame[MAX_PATH]; // Q_strncpy( szGamePath, m_szGameDir, MAX_PATH ); // Q_StripTrailingSlash( szGamePath ); // Q_FileBase( szGamePath, szGame, MAX_PATH ); // return szGame; }