source-engine/hammer/gameconfig.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

623 lines
17 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include <direct.h>
#include <io.h>
#include <WorldSize.h>
#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; i<CMapDoc::GetDocumentCount(); i++ )
{
CMapDoc *pDoc = CMapDoc::GetDocument(i);
if (pDoc->GetGame() == 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 <mod dir>\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;
}