core/GameConfigs.cpp
changeset 2524 b7d445920806
parent 2517 312e73edd971
child 2562 af4b3f9a8168
     1.1 --- a/core/GameConfigs.cpp	Mon Jan 12 01:32:53 2009 -0500
     1.2 +++ b/core/GameConfigs.cpp	Mon Jan 12 02:28:22 2009 -0500
     1.3 @@ -90,9 +90,37 @@
     1.4  unsigned int s_ServerBinCRC;
     1.5  bool s_ServerBinCRC_Ok = false;
     1.6  
     1.7 +static bool DoesGameMatch(const char *value)
     1.8 +{
     1.9 +	if (strcmp(value, g_Game) == 0 ||
    1.10 +		strcmp(value, g_GameDesc) == 0 ||
    1.11 +		strcmp(value, g_GameName) == 0)
    1.12 +	{
    1.13 +		return true;
    1.14 +	}
    1.15 +	return false;
    1.16 +}
    1.17 +
    1.18 +static bool DoesEngineMatch(const char *value)
    1.19 +{
    1.20 +#if SOURCE_ENGINE == SE_EPISODEONE
    1.21 +	if (strcmp(value, "original") == 0)
    1.22 +#elif SOURCE_ENGINE == SE_ORANGEBOX
    1.23 +	if (strcmp(value, "orangebox") == 0)
    1.24 +#elif SOURCE_ENGINE == SE_LEFT4DEAD
    1.25 +	if (strcmp(value, "left4dead") == 0)
    1.26 +#else
    1.27 +#error "Unknown engine type"
    1.28 +#endif
    1.29 +	{
    1.30 +		return true;
    1.31 +	}
    1.32 +	return false;
    1.33 +}
    1.34 +
    1.35  CGameConfig::CGameConfig(const char *file)
    1.36  {
    1.37 -	m_pFile = sm_strdup(file);
    1.38 +	strncopy(m_File, file, sizeof(m_File));
    1.39  	m_pOffsets = sm_trie_create();
    1.40  	m_pProps = sm_trie_create();
    1.41  	m_pKeys = sm_trie_create();
    1.42 @@ -110,7 +138,6 @@
    1.43  	sm_trie_destroy(m_pProps);
    1.44  	sm_trie_destroy(m_pKeys);
    1.45  	sm_trie_destroy(m_pSigs);
    1.46 -	delete [] m_pFile;
    1.47  	delete m_pStrings;
    1.48  }
    1.49  
    1.50 @@ -136,11 +163,9 @@
    1.51  		}
    1.52  	case PSTATE_GAMES:
    1.53  		{
    1.54 -			if ((strcmp(name, "*") == 0)
    1.55 -				|| (strcmp(name, "#default") == 0)
    1.56 -				|| (strcasecmp(name, g_Game) == 0)
    1.57 -				|| (strcasecmp(name, g_GameDesc) == 0)
    1.58 -				|| (strcasecmp(name, g_GameName) == 0))
    1.59 +			if (strcmp(name, "*") == 0 ||
    1.60 +				strcmp(name, "#default") == 0 ||
    1.61 +				DoesGameMatch(name))
    1.62  			{
    1.63  				bShouldBeReadingDefault = true;
    1.64  				m_ParseState = PSTATE_GAMEDEFS;
    1.65 @@ -160,12 +185,15 @@
    1.66  			{
    1.67  				m_ParseState = PSTATE_GAMEDEFS_KEYS;
    1.68  			}
    1.69 -			else if ((strcmp(name, "#supported") == 0)
    1.70 -					 && (strcmp(m_Game, "#default") == 0))
    1.71 +			else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
    1.72  			{
    1.73  				m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
    1.74  				/* Ignore this section unless we get a game. */
    1.75  				bShouldBeReadingDefault = false;
    1.76 +				had_game = false;
    1.77 +				matched_game = false;
    1.78 +				had_engine = false;
    1.79 +				matched_engine = false;
    1.80  			}
    1.81  			else if (strcmp(name, "Signatures") == 0)
    1.82  			{
    1.83 @@ -244,7 +272,7 @@
    1.84  			if (error[0] != '\0')
    1.85  			{
    1.86  				m_IgnoreLevel = 1;
    1.87 -				g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_pFile);
    1.88 +				g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
    1.89  				g_Logger.LogError("[SM] %s", error);
    1.90  			} else {
    1.91  				m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
    1.92 @@ -297,13 +325,29 @@
    1.93  		int id = m_pStrings->AddString(value);
    1.94  		sm_trie_replace(m_pKeys, key, (void *)id);
    1.95  	} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
    1.96 -		if (strcmp(key, "game") == 0
    1.97 -			&& (strcasecmp(value, g_Game) == 0 
    1.98 -				|| strcasecmp(value, g_GameDesc) == 0
    1.99 -				|| strcasecmp(value, g_GameName) == 0)
   1.100 -				)
   1.101 +		if (strcmp(key, "game") == 0)
   1.102  		{
   1.103 -			bShouldBeReadingDefault = true;
   1.104 +			had_game = true;
   1.105 +			if (DoesGameMatch(value))
   1.106 +			{
   1.107 +				matched_game = true;
   1.108 +			}
   1.109 +			if ((!had_engine && matched_game) || (matched_engine && matched_game))
   1.110 +			{
   1.111 +				bShouldBeReadingDefault = true;
   1.112 +			}
   1.113 +		}
   1.114 +		else if (strcmp(key, "engine") == 0)
   1.115 +		{
   1.116 +			had_engine = true;
   1.117 +			if (DoesEngineMatch(value))
   1.118 +			{
   1.119 +				matched_engine = true;
   1.120 +			}
   1.121 +			if ((!had_game && matched_engine) || (matched_game && matched_engine))
   1.122 +			{
   1.123 +				bShouldBeReadingDefault = true;
   1.124 +			}
   1.125  		}
   1.126  	} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
   1.127  		if (strcmp(key, PLATFORM_NAME) == 0)
   1.128 @@ -390,7 +434,7 @@
   1.129  						g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", 
   1.130  							m_Class,
   1.131  							m_Prop,
   1.132 -							m_pFile,
   1.133 +							m_CurFile,
   1.134  							m_Game);
   1.135  					}
   1.136  				}
   1.137 @@ -440,7 +484,7 @@
   1.138  			{
   1.139  				g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")", 
   1.140  					s_TempSig.library, 
   1.141 -					m_pFile);
   1.142 +					m_CurFile);
   1.143  			} else {
   1.144  #if defined PLATFORM_LINUX
   1.145  				if (s_TempSig.sig[0] == '@')
   1.146 @@ -457,12 +501,12 @@
   1.147  						} else {
   1.148  							g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
   1.149  								s_TempSig.library,
   1.150 -								m_pFile);
   1.151 +								m_File);
   1.152  						}
   1.153  					} else {
   1.154  						g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
   1.155  							s_TempSig.library,
   1.156 -							m_pFile);
   1.157 +							m_File);
   1.158  					}
   1.159  				}
   1.160  				if (final_addr)
   1.161 @@ -526,37 +570,206 @@
   1.162  	return SMCResult_Continue;
   1.163  }
   1.164  
   1.165 +#define MSTATE_NONE		0
   1.166 +#define MSTATE_MAIN		1
   1.167 +#define MSTATE_FILE		2
   1.168 +
   1.169 +class MasterReader : public ITextListener_SMC
   1.170 +{
   1.171 +public:
   1.172 +	virtual void ReadSMC_ParseStart()
   1.173 +	{
   1.174 +		state = MSTATE_NONE;
   1.175 +		ignoreLevel = 0;
   1.176 +	}
   1.177 +
   1.178 +	virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   1.179 +	{
   1.180 +		if (ignoreLevel)
   1.181 +		{
   1.182 +			return SMCResult_Continue;
   1.183 +		}
   1.184 +
   1.185 +		if (state == MSTATE_NONE)
   1.186 +		{
   1.187 +			if (strcmp(name, "Game Master") == 0)
   1.188 +			{
   1.189 +				state = MSTATE_MAIN;
   1.190 +			}
   1.191 +			else
   1.192 +			{
   1.193 +				ignoreLevel++;
   1.194 +			}
   1.195 +		}
   1.196 +		else if (state == MSTATE_MAIN)
   1.197 +		{
   1.198 +			strncopy(cur_file, name, sizeof(cur_file));
   1.199 +			had_engine = false;
   1.200 +			matched_engine = false;
   1.201 +			had_game = false;
   1.202 +			matched_game = false;
   1.203 +			state = MSTATE_FILE;
   1.204 +		}
   1.205 +		else if (state == MSTATE_FILE)
   1.206 +		{
   1.207 +			ignoreLevel++;
   1.208 +		}
   1.209 +
   1.210 +		return SMCResult_Continue;
   1.211 +	}
   1.212 +
   1.213 +	virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   1.214 +	{
   1.215 +		if (ignoreLevel || state != MSTATE_FILE)
   1.216 +		{
   1.217 +			return SMCResult_Continue;
   1.218 +		}
   1.219 +
   1.220 +		if (strcmp(key, "engine") == 0)
   1.221 +		{
   1.222 +			had_engine = true;
   1.223 +			if (DoesEngineMatch(value))
   1.224 +			{
   1.225 +				matched_engine = true;
   1.226 +			}
   1.227 +		}
   1.228 +		else if (strcmp(key, "game") == 0)
   1.229 +		{
   1.230 +			had_game = true;
   1.231 +			if (DoesGameMatch(value))
   1.232 +			{
   1.233 +				matched_game = true;
   1.234 +			}
   1.235 +		}
   1.236 +
   1.237 +		return SMCResult_Continue;
   1.238 +	}
   1.239 +
   1.240 +	virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   1.241 +	{
   1.242 +		if (ignoreLevel)
   1.243 +		{
   1.244 +			ignoreLevel--;
   1.245 +			return SMCResult_Continue;
   1.246 +		}
   1.247 +
   1.248 +		if (state == MSTATE_FILE)
   1.249 +		{
   1.250 +			/* The four success conditions:
   1.251 +			 * 1. Needed nothing.
   1.252 +			 * 2. Needed game only.
   1.253 +			 * 3. Needed engine only.
   1.254 +			 * 4. Needed both engine and game.
   1.255 +			 * Final result is minimized via k-map.
   1.256 +			 */
   1.257 +#if 0
   1.258 +			if ((!had_engine && !had_game) ||
   1.259 +				(!had_engine && (had_game && matched_game)) ||
   1.260 +				(!had_game && (had_engine && matched_engine)) ||
   1.261 +				((had_game && had_engine) && (matched_game && matched_engine)))
   1.262 +#endif
   1.263 +			if ((!had_engine && !had_game) ||
   1.264 +				(!had_engine && matched_game) ||
   1.265 +				(!had_game && matched_engine) ||
   1.266 +				(matched_engine && matched_game))
   1.267 +			{
   1.268 +				fileList->push_back(cur_file);
   1.269 +			}
   1.270 +			state = MSTATE_MAIN;
   1.271 +		}
   1.272 +		else if (state == MSTATE_MAIN)
   1.273 +		{
   1.274 +			state = MSTATE_NONE;
   1.275 +		}
   1.276 +
   1.277 +		return SMCResult_Continue;
   1.278 +	}
   1.279 +public:
   1.280 +	List<String> *fileList;
   1.281 +	unsigned int state;
   1.282 +	unsigned int ignoreLevel;
   1.283 +	char cur_file[PLATFORM_MAX_PATH];
   1.284 +	bool had_engine;
   1.285 +	bool matched_engine;
   1.286 +	bool had_game;
   1.287 +	bool matched_game;
   1.288 +};
   1.289 +
   1.290 +static MasterReader master_reader;
   1.291 +
   1.292  bool CGameConfig::Reparse(char *error, size_t maxlength)
   1.293  {
   1.294 -	SMCError err;
   1.295 -	SMCStates state = {0, 0};
   1.296 -
   1.297 -	char path[PLATFORM_MAX_PATH];
   1.298 -	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile);
   1.299 -	/* Backwards compatibility */
   1.300 -	if (!g_LibSys.PathExists(path))
   1.301 -	{
   1.302 -		g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile);
   1.303 -	}
   1.304 -
   1.305 -	/* Initialize parse states */
   1.306 -	m_IgnoreLevel = 0;
   1.307 -	bShouldBeReadingDefault = true;
   1.308 -	m_ParseState = PSTATE_NONE;
   1.309  	/* Reset cached data */
   1.310  	m_pStrings->Reset();
   1.311  	sm_trie_clear(m_pOffsets);
   1.312  	sm_trie_clear(m_pProps);
   1.313  	sm_trie_clear(m_pKeys);
   1.314  
   1.315 -	if ((err=textparsers->ParseSMCFile(path, this, &state, error, maxlength))
   1.316 +	char path[PLATFORM_MAX_PATH];
   1.317 +
   1.318 +	/* See if we can use the extended gamedata format. */
   1.319 +	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
   1.320 +	if (!g_LibSys.PathExists(path))
   1.321 +	{
   1.322 +		/* Nope, use the old mechanism. */
   1.323 +		UTIL_Format(path, sizeof(path), "%s.txt", m_File);
   1.324 +		return EnterFile(path, error, maxlength);
   1.325 +	}
   1.326 +
   1.327 +	/* Otherwise, it's time to parse the master. */
   1.328 +	SMCError err;
   1.329 +	SMCStates state = {0, 0};
   1.330 +	List<String> fileList;
   1.331 +	master_reader.fileList = &fileList;
   1.332 +
   1.333 +	err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
   1.334 +	if (err != SMCError_Okay)
   1.335 +	{
   1.336 +		const char *msg = textparsers->GetSMCErrorString(err);
   1.337 +
   1.338 +		g_Logger.LogError("[SM] Error parsing master gameconf file \"%s\":", path);
   1.339 +		g_Logger.LogError("[SM] Error %d on line %d, col %d: %s", 
   1.340 +			err,
   1.341 +			state.line,
   1.342 +			state.col,
   1.343 +			msg ? msg : "Unknown error");
   1.344 +		return false;
   1.345 +	}
   1.346 +
   1.347 +	/* Go through each file we found and parse it. */
   1.348 +	List<String>::iterator iter;
   1.349 +	for (iter = fileList.begin(); iter != fileList.end(); iter++)
   1.350 +	{
   1.351 +		UTIL_Format(path, sizeof(path), "%s/%s", m_File, (*iter).c_str());
   1.352 +		if (!EnterFile(path, error, maxlength))
   1.353 +		{
   1.354 +			return false;
   1.355 +		}
   1.356 +	}
   1.357 +
   1.358 +	return true;
   1.359 +}
   1.360 +
   1.361 +bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
   1.362 +{
   1.363 +	SMCError err;
   1.364 +	SMCStates state = {0, 0};
   1.365 +
   1.366 +	g_SourceMod.BuildPath(Path_SM, m_CurFile, sizeof(m_CurFile), "gamedata/%s", file);
   1.367 +
   1.368 +	/* Initialize parse states */
   1.369 +	m_IgnoreLevel = 0;
   1.370 +	bShouldBeReadingDefault = true;
   1.371 +	m_ParseState = PSTATE_NONE;
   1.372 +
   1.373 +	if ((err=textparsers->ParseSMCFile(m_CurFile, this, &state, error, maxlength))
   1.374  		!= SMCError_Okay)
   1.375  	{
   1.376  		const char *msg;
   1.377  
   1.378  		msg = textparsers->GetSMCErrorString(err);
   1.379  
   1.380 -		g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", path);
   1.381 +		g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", m_CurFile);
   1.382  		g_Logger.LogError("[SM] Error %d on line %d, col %d: %s", 
   1.383  			err,
   1.384  			state.line,
   1.385 @@ -722,7 +935,7 @@
   1.386  
   1.387  	if (pConfig->DecRefCount() == 0)
   1.388  	{
   1.389 -		sm_trie_delete(m_pLookup, pConfig->m_pFile);
   1.390 +		sm_trie_delete(m_pLookup, pConfig->m_File);
   1.391  		delete pConfig;
   1.392  	}
   1.393  }