core/GameConfigs.cpp
author David Anderson <dvander@alliedmods.net>
Mon Oct 26 23:51:39 2009 -0700 (2009-10-26)
changeset 2832 bf5d37088b6d
parent 2828 74059a80142c
child 2847 1daf209d9dd2
permissions -rw-r--r--
Don't pass unconnected clients into OnClientCommand (bug 3732, r=fyren).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <stdlib.h>
    34 #include "GameConfigs.h"
    35 #include "sm_stringutil.h"
    36 #include "sourcemod.h"
    37 #include "sourcemm_api.h"
    38 #include "HalfLife2.h"
    39 #include "Logger.h"
    40 #include "ShareSys.h"
    41 #include "MemoryUtils.h"
    42 #include "LibrarySys.h"
    43 #include "HandleSys.h"
    44 #include "sm_crc32.h"
    45 
    46 #if defined PLATFORM_LINUX
    47 #include <dlfcn.h>
    48 #endif
    49 
    50 GameConfigManager g_GameConfigs;
    51 IGameConfig *g_pGameConf = NULL;
    52 static char g_Game[256];
    53 static char g_GameDesc[256] = {'!', '\0'};
    54 static char g_GameName[256] = {'$', '\0'};
    55 
    56 #define PSTATE_NONE						0
    57 #define PSTATE_GAMES					1
    58 #define PSTATE_GAMEDEFS					2
    59 #define PSTATE_GAMEDEFS_OFFSETS			3
    60 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET	4
    61 #define PSTATE_GAMEDEFS_KEYS			5
    62 #define PSTATE_GAMEDEFS_SUPPORTED		6
    63 #define PSTATE_GAMEDEFS_SIGNATURES		7
    64 #define PSTATE_GAMEDEFS_SIGNATURES_SIG	8
    65 #define PSTATE_GAMEDEFS_CRC				9
    66 #define PSTATE_GAMEDEFS_CRC_BINARY		10
    67 #define PSTATE_GAMEDEFS_CUSTOM			11
    68 
    69 #if defined PLATFORM_WINDOWS
    70 #define PLATFORM_NAME				"windows"
    71 #define PLATFORM_SERVER_BINARY		"server.dll"
    72 #elif defined PLATFORM_LINUX
    73 #define PLATFORM_NAME				"linux"
    74 #define PLATFORM_SERVER_BINARY		"server_i486.so"
    75 #elif defined PLATFORM_APPLE
    76 #define PLATFORM_NAME               "undef"
    77 #define PLATFORM_SERVER_BINARY      "undef.dylib"
    78 #endif
    79 
    80 struct TempSigInfo
    81 {
    82 	void Reset()
    83 	{
    84 		library[0] = '\0';
    85 		sig[0] = '\0';
    86 	}
    87 	char sig[512];
    88 	char library[64];
    89 } s_TempSig;
    90 unsigned int s_ServerBinCRC;
    91 bool s_ServerBinCRC_Ok = false;
    92 
    93 static bool DoesGameMatch(const char *value)
    94 {
    95 #if defined PLATFORM_WINDOWS
    96 	if (strcasecmp(value, g_Game) == 0 ||
    97 #else
    98 	if (strcmp(value, g_Game) == 0 ||
    99 #endif
   100 		strcmp(value, g_GameDesc) == 0 ||
   101 		strcmp(value, g_GameName) == 0)
   102 	{
   103 		return true;
   104 	}
   105 	return false;
   106 }
   107 
   108 static bool DoesEngineMatch(const char *value)
   109 {
   110 #if SOURCE_ENGINE == SE_EPISODEONE
   111 	if (strcmp(value, "original") == 0)
   112 #elif SOURCE_ENGINE == SE_DARKMESSIAH
   113 	if (strcmp(value, "darkmessiah") == 0)
   114 #elif SOURCE_ENGINE == SE_ORANGEBOX
   115 	if (strcmp(value, "orangebox") == 0)
   116 #elif SOURCE_ENGINE == SE_ORANGEBOXVALVE
   117 	if (strcmp(value, "orangebox_valve") == 0)
   118 #elif SOURCE_ENGINE == SE_LEFT4DEAD
   119 	if (strcmp(value, "left4dead") == 0)
   120 #else
   121 #error "Unknown engine type"
   122 #endif
   123 	{
   124 		return true;
   125 	}
   126 	return false;
   127 }
   128 
   129 CGameConfig::CGameConfig(const char *file)
   130 {
   131 	strncopy(m_File, file, sizeof(m_File));
   132 	m_pOffsets = sm_trie_create();
   133 	m_pProps = sm_trie_create();
   134 	m_pKeys = sm_trie_create();
   135 	m_pSigs = sm_trie_create();
   136 	m_pStrings = new BaseStringTable(512);
   137 	m_RefCount = 0;
   138 
   139 	m_CustomLevel = 0;
   140 	m_CustomHandler = NULL;
   141 }
   142 
   143 CGameConfig::~CGameConfig()
   144 {
   145 	sm_trie_destroy(m_pOffsets);
   146 	sm_trie_destroy(m_pProps);
   147 	sm_trie_destroy(m_pKeys);
   148 	sm_trie_destroy(m_pSigs);
   149 	delete m_pStrings;
   150 }
   151 
   152 SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
   153 {
   154 	if (m_IgnoreLevel)
   155 	{
   156 		m_IgnoreLevel++;
   157 		return SMCResult_Continue;
   158 	}
   159 
   160 	switch (m_ParseState)
   161 	{
   162 	case PSTATE_NONE:
   163 		{
   164 			if (strcmp(name, "Games") == 0)
   165 			{
   166 				m_ParseState = PSTATE_GAMES;
   167 			} else {
   168 				m_IgnoreLevel++;
   169 			}
   170 			break;
   171 		}
   172 	case PSTATE_GAMES:
   173 		{
   174 			if (strcmp(name, "*") == 0 ||
   175 				strcmp(name, "#default") == 0 ||
   176 				DoesGameMatch(name))
   177 			{
   178 				bShouldBeReadingDefault = true;
   179 				m_ParseState = PSTATE_GAMEDEFS;
   180 				strncopy(m_Game, name, sizeof(m_Game));
   181 			} else {
   182 				m_IgnoreLevel++;
   183 			}
   184 			break;
   185 		}
   186 	case PSTATE_GAMEDEFS:
   187 		{
   188 			if (strcmp(name, "Offsets") == 0)
   189 			{
   190 				m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   191 			}
   192 			else if (strcmp(name, "Keys") == 0)
   193 			{
   194 				m_ParseState = PSTATE_GAMEDEFS_KEYS;
   195 			}
   196 			else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
   197 			{
   198 				m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
   199 				/* Ignore this section unless we get a game. */
   200 				bShouldBeReadingDefault = false;
   201 				had_game = false;
   202 				matched_game = false;
   203 				had_engine = false;
   204 				matched_engine = false;
   205 			}
   206 			else if (strcmp(name, "Signatures") == 0)
   207 			{
   208 				m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   209 			}
   210 			else if (strcmp(name, "CRC") == 0)
   211 			{
   212 				m_ParseState = PSTATE_GAMEDEFS_CRC;
   213 				bShouldBeReadingDefault = false;
   214 			}
   215 			else
   216 			{
   217 				ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name);
   218 
   219 				if (pListen != NULL)
   220 				{
   221 					m_CustomLevel = 0;
   222 					m_ParseState = PSTATE_GAMEDEFS_CUSTOM;
   223 					m_CustomHandler = *pListen;
   224 					m_CustomHandler->ReadSMC_ParseStart();
   225 					break;
   226 				}
   227 
   228 				m_IgnoreLevel++;
   229 			}
   230 			break;
   231 		}
   232 	case PSTATE_GAMEDEFS_OFFSETS:
   233 		{
   234 			m_Prop[0] = '\0';
   235 			m_Class[0] = '\0';
   236 			strncopy(m_offset, name, sizeof(m_offset));
   237 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
   238 			break;
   239 		}
   240 	case PSTATE_GAMEDEFS_SIGNATURES:
   241 		{
   242 			strncopy(m_offset, name, sizeof(m_offset));
   243 			s_TempSig.Reset();
   244 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
   245 			break;
   246 		}
   247 	case PSTATE_GAMEDEFS_CRC:
   248 		{
   249 			char error[255];
   250 			error[0] = '\0';
   251 			if (strcmp(name, "server") != 0)
   252 			{
   253 				UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name);
   254 			} 
   255 			else if (!s_ServerBinCRC_Ok)
   256 			{
   257 				FILE *fp;
   258 				char path[PLATFORM_MAX_PATH];
   259 
   260 				g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
   261 				if ((fp = fopen(path, "rb")) == NULL)
   262 				{
   263 					UTIL_Format(error, sizeof(error), "Could not open binary: %s", path);
   264 				} else {
   265 					size_t size;
   266 					void *buffer;
   267 
   268 					fseek(fp, 0, SEEK_END);
   269 					size = ftell(fp);
   270 					fseek(fp, 0, SEEK_SET);
   271 
   272 					buffer = malloc(size);
   273 					fread(buffer, size, 1, fp);
   274 					s_ServerBinCRC = UTIL_CRC32(buffer, size);
   275 					free(buffer);
   276 					s_ServerBinCRC_Ok = true;
   277 					fclose(fp);
   278 				}
   279 			}
   280 			if (error[0] != '\0')
   281 			{
   282 				m_IgnoreLevel = 1;
   283 				g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
   284 				g_Logger.LogError("[SM] %s", error);
   285 			} else {
   286 				m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
   287 			}
   288 			break;
   289 		}
   290 	case PSTATE_GAMEDEFS_CUSTOM:
   291 		{
   292 			m_CustomLevel++;
   293 			return m_CustomHandler->ReadSMC_NewSection(states, name);
   294 			break;
   295 		}
   296 	/* No sub-sections allowed:
   297 	 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   298 	 case PSTATE_GAMEDEFS_KEYS:
   299 	 case PSTATE_GAMEDEFS_SUPPORTED:
   300 	 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   301 	 case PSTATE_GAMEDEFS_CRC_BINARY:
   302 	 */
   303 	default:
   304 		{
   305 			/* If we don't know what we got, start ignoring */
   306 			m_IgnoreLevel++;
   307 			break;
   308 		}
   309 	}
   310 
   311 	return SMCResult_Continue;
   312 }
   313 
   314 SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   315 {
   316 	if (m_IgnoreLevel)
   317 	{
   318 		return SMCResult_Continue;
   319 	}
   320 
   321 	if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
   322 	{
   323 		if (strcmp(key, "class") == 0)
   324 		{
   325 			strncopy(m_Class, value, sizeof(m_Class));
   326 		} else if (strcmp(key, "prop") == 0) {
   327 			strncopy(m_Prop, value, sizeof(m_Prop));
   328 		} else if (strcmp(key, PLATFORM_NAME) == 0) {
   329 			int val = atoi(value);
   330 			sm_trie_replace(m_pOffsets, m_offset, (void *)val);
   331 		}
   332 	} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
   333 		int id = m_pStrings->AddString(value);
   334 		sm_trie_replace(m_pKeys, key, (void *)id);
   335 	} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
   336 		if (strcmp(key, "game") == 0)
   337 		{
   338 			had_game = true;
   339 			if (DoesGameMatch(value))
   340 			{
   341 				matched_game = true;
   342 			}
   343 			if ((!had_engine && matched_game) || (matched_engine && matched_game))
   344 			{
   345 				bShouldBeReadingDefault = true;
   346 			}
   347 		}
   348 		else if (strcmp(key, "engine") == 0)
   349 		{
   350 			had_engine = true;
   351 			if (DoesEngineMatch(value))
   352 			{
   353 				matched_engine = true;
   354 			}
   355 			if ((!had_game && matched_engine) || (matched_game && matched_engine))
   356 			{
   357 				bShouldBeReadingDefault = true;
   358 			}
   359 		}
   360 	} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
   361 		if (strcmp(key, PLATFORM_NAME) == 0)
   362 		{
   363 			strncopy(s_TempSig.sig, value, sizeof(s_TempSig.sig));
   364 		} else if (strcmp(key, "library") == 0) {
   365 			strncopy(s_TempSig.library, value, sizeof(s_TempSig.library));
   366 		}
   367 	} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
   368 		if (strcmp(key, PLATFORM_NAME) == 0 
   369 			&& s_ServerBinCRC_Ok
   370 			&& !bShouldBeReadingDefault)
   371 		{
   372 			unsigned int crc = 0;
   373 			sscanf(value, "%08X", &crc);
   374 			if (s_ServerBinCRC == crc)
   375 			{
   376 				bShouldBeReadingDefault = true;
   377 			}
   378 		}
   379 	} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
   380 		return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
   381 	}
   382 
   383 	return SMCResult_Continue;
   384 }
   385 
   386 SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
   387 {
   388 	if (m_IgnoreLevel)
   389 	{
   390 		m_IgnoreLevel--;
   391 		return SMCResult_Continue;
   392 	}
   393 
   394 	if (m_CustomLevel)
   395 	{
   396 		m_CustomLevel--;
   397 		m_CustomHandler->ReadSMC_LeavingSection(states);
   398 		return SMCResult_Continue;
   399 	}
   400 
   401 	switch (m_ParseState)
   402 	{
   403 	case PSTATE_GAMES:
   404 		{
   405 			m_ParseState = PSTATE_NONE;
   406 			break;
   407 		}
   408 	case PSTATE_GAMEDEFS:
   409 		{
   410 			m_ParseState = PSTATE_GAMES;
   411 			break;
   412 		}
   413 	case PSTATE_GAMEDEFS_CUSTOM:
   414 		{
   415 			m_ParseState = PSTATE_GAMEDEFS;
   416 			m_CustomHandler->ReadSMC_ParseEnd(false, false);
   417 			break;
   418 		}
   419 	case PSTATE_GAMEDEFS_KEYS:
   420 	case PSTATE_GAMEDEFS_OFFSETS:
   421 		{
   422 			m_ParseState = PSTATE_GAMEDEFS;
   423 			break;
   424 		}
   425 	case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   426 		{
   427 			/* Parse the offset... */
   428 			if (m_Class[0] != '\0'
   429 				&& m_Prop[0] != '\0')
   430 			{
   431 				SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop);
   432 				if (pProp)
   433 				{
   434 					int val = pProp->GetOffset();
   435 					sm_trie_replace(m_pOffsets, m_offset, (void *)val);
   436 					sm_trie_replace(m_pProps, m_offset, pProp);
   437 				} else {
   438 					/* Check if it's a non-default game and no offsets exist */
   439 					if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
   440 						&& (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
   441 					{
   442 						g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", 
   443 							m_Class,
   444 							m_Prop,
   445 							m_CurFile,
   446 							m_Game);
   447 					}
   448 				}
   449 			}
   450 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   451 			break;
   452 		}
   453 	case PSTATE_GAMEDEFS_CRC:
   454 	case PSTATE_GAMEDEFS_SUPPORTED:
   455 		{
   456 			if (!bShouldBeReadingDefault)
   457 			{
   458 				/* If we shouldn't read the rest of this section, set the ignore level. */
   459 				m_IgnoreLevel = 1;
   460 				m_ParseState = PSTATE_GAMES;
   461 			} else {
   462 				m_ParseState = PSTATE_GAMEDEFS;
   463 			}
   464 			break;
   465 		}
   466 	case PSTATE_GAMEDEFS_CRC_BINARY:
   467 		{
   468 			m_ParseState = PSTATE_GAMEDEFS_CRC;
   469 			break;
   470 		}
   471 	case PSTATE_GAMEDEFS_SIGNATURES:
   472 		{
   473 			m_ParseState = PSTATE_GAMEDEFS;
   474 			break;
   475 		}
   476 	case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   477 		{
   478 			if (s_TempSig.library[0] == '\0')
   479 			{
   480 				/* assume server */
   481 				strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
   482 			}
   483 			void *addrInBase = NULL;
   484 			if (strcmp(s_TempSig.library, "server") == 0)
   485 			{
   486 				addrInBase = (void *)g_SMAPI->GetServerFactory(false);
   487 			} else if (strcmp(s_TempSig.library, "engine") == 0) {
   488 				addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
   489 			}
   490 			void *final_addr = NULL;
   491 			if (addrInBase == NULL)
   492 			{
   493 				g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")", 
   494 					s_TempSig.library, 
   495 					m_CurFile);
   496 			} else {
   497 #if defined PLATFORM_LINUX
   498 				if (s_TempSig.sig[0] == '@')
   499 				{
   500 					Dl_info info;
   501 					/* GNU only: returns 0 on error, inconsistent! >:[ */
   502 					if (dladdr(addrInBase, &info) != 0)
   503 					{
   504 						void *handle = dlopen(info.dli_fname, RTLD_NOW);
   505 						if (handle)
   506 						{
   507 							final_addr = dlsym(handle, &s_TempSig.sig[1]);
   508 							dlclose(handle);
   509 						} else {
   510 							g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
   511 								s_TempSig.library,
   512 								m_File);
   513 						}
   514 					} else {
   515 						g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
   516 							s_TempSig.library,
   517 							m_File);
   518 					}
   519 				}
   520 				if (final_addr)
   521 				{
   522 					goto skip_find;
   523 				}
   524 #endif
   525 				/* First, preprocess the signature */
   526 				unsigned char real_sig[511];
   527 				size_t real_bytes;
   528 				size_t length;
   529 
   530 				real_bytes = 0;
   531 				length = strlen(s_TempSig.sig);
   532 
   533 				real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), s_TempSig.sig);
   534 
   535 				if (real_bytes >= 1)
   536 				{
   537 					final_addr = g_MemUtils.FindPattern(addrInBase, (char*)real_sig, real_bytes);
   538 				}
   539 			}
   540 
   541 #if defined PLATFORM_LINUX
   542 skip_find:
   543 #endif
   544 			sm_trie_replace(m_pSigs, m_offset, final_addr);
   545 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   546 
   547 			break;
   548 		}
   549 	}
   550 
   551 	return SMCResult_Continue;
   552 }
   553 
   554 #define MSTATE_NONE		0
   555 #define MSTATE_MAIN		1
   556 #define MSTATE_FILE		2
   557 
   558 class MasterReader : public ITextListener_SMC
   559 {
   560 public:
   561 	virtual void ReadSMC_ParseStart()
   562 	{
   563 		state = MSTATE_NONE;
   564 		ignoreLevel = 0;
   565 	}
   566 
   567 	virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   568 	{
   569 		if (ignoreLevel)
   570 		{
   571 			return SMCResult_Continue;
   572 		}
   573 
   574 		if (state == MSTATE_NONE)
   575 		{
   576 			if (strcmp(name, "Game Master") == 0)
   577 			{
   578 				state = MSTATE_MAIN;
   579 			}
   580 			else
   581 			{
   582 				ignoreLevel++;
   583 			}
   584 		}
   585 		else if (state == MSTATE_MAIN)
   586 		{
   587 			strncopy(cur_file, name, sizeof(cur_file));
   588 			had_engine = false;
   589 			matched_engine = false;
   590 			had_game = false;
   591 			matched_game = false;
   592 			state = MSTATE_FILE;
   593 		}
   594 		else if (state == MSTATE_FILE)
   595 		{
   596 			ignoreLevel++;
   597 		}
   598 
   599 		return SMCResult_Continue;
   600 	}
   601 
   602 	virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   603 	{
   604 		if (ignoreLevel || state != MSTATE_FILE)
   605 		{
   606 			return SMCResult_Continue;
   607 		}
   608 
   609 		if (strcmp(key, "engine") == 0)
   610 		{
   611 			had_engine = true;
   612 			if (DoesEngineMatch(value))
   613 			{
   614 				matched_engine = true;
   615 			}
   616 		}
   617 		else if (strcmp(key, "game") == 0)
   618 		{
   619 			had_game = true;
   620 			if (DoesGameMatch(value))
   621 			{
   622 				matched_game = true;
   623 			}
   624 		}
   625 
   626 		return SMCResult_Continue;
   627 	}
   628 
   629 	virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   630 	{
   631 		if (ignoreLevel)
   632 		{
   633 			ignoreLevel--;
   634 			return SMCResult_Continue;
   635 		}
   636 
   637 		if (state == MSTATE_FILE)
   638 		{
   639 			/* The four success conditions:
   640 			 * 1. Needed nothing.
   641 			 * 2. Needed game only.
   642 			 * 3. Needed engine only.
   643 			 * 4. Needed both engine and game.
   644 			 * Final result is minimized via k-map.
   645 			 */
   646 #if 0
   647 			if ((!had_engine && !had_game) ||
   648 				(!had_engine && (had_game && matched_game)) ||
   649 				(!had_game && (had_engine && matched_engine)) ||
   650 				((had_game && had_engine) && (matched_game && matched_engine)))
   651 #endif
   652 			if ((!had_engine && !had_game) ||
   653 				(!had_engine && matched_game) ||
   654 				(!had_game && matched_engine) ||
   655 				(matched_engine && matched_game))
   656 			{
   657 				fileList->push_back(cur_file);
   658 			}
   659 			state = MSTATE_MAIN;
   660 		}
   661 		else if (state == MSTATE_MAIN)
   662 		{
   663 			state = MSTATE_NONE;
   664 		}
   665 
   666 		return SMCResult_Continue;
   667 	}
   668 public:
   669 	List<String> *fileList;
   670 	unsigned int state;
   671 	unsigned int ignoreLevel;
   672 	char cur_file[PLATFORM_MAX_PATH];
   673 	bool had_engine;
   674 	bool matched_engine;
   675 	bool had_game;
   676 	bool matched_game;
   677 };
   678 
   679 static MasterReader master_reader;
   680 
   681 bool CGameConfig::Reparse(char *error, size_t maxlength)
   682 {
   683 	/* Reset cached data */
   684 	m_pStrings->Reset();
   685 	sm_trie_clear(m_pOffsets);
   686 	sm_trie_clear(m_pProps);
   687 	sm_trie_clear(m_pKeys);
   688 
   689 	char path[PLATFORM_MAX_PATH];
   690 
   691 	/* See if we can use the extended gamedata format. */
   692 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
   693 	if (!g_LibSys.PathExists(path))
   694 	{
   695 		/* Nope, use the old mechanism. */
   696 		UTIL_Format(path, sizeof(path), "%s.txt", m_File);
   697 		return EnterFile(path, error, maxlength);
   698 	}
   699 
   700 	/* Otherwise, it's time to parse the master. */
   701 	SMCError err;
   702 	SMCStates state = {0, 0};
   703 	List<String> fileList;
   704 	master_reader.fileList = &fileList;
   705 
   706 	err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
   707 	if (err != SMCError_Okay)
   708 	{
   709 		const char *msg = textparsers->GetSMCErrorString(err);
   710 
   711 		g_Logger.LogError("[SM] Error parsing master gameconf file \"%s\":", path);
   712 		g_Logger.LogError("[SM] Error %d on line %d, col %d: %s", 
   713 			err,
   714 			state.line,
   715 			state.col,
   716 			msg ? msg : "Unknown error");
   717 		return false;
   718 	}
   719 
   720 	/* Go through each file we found and parse it. */
   721 	List<String>::iterator iter;
   722 	for (iter = fileList.begin(); iter != fileList.end(); iter++)
   723 	{
   724 		UTIL_Format(path, sizeof(path), "%s/%s", m_File, (*iter).c_str());
   725 		if (!EnterFile(path, error, maxlength))
   726 		{
   727 			return false;
   728 		}
   729 	}
   730 
   731 	/* Parse the contents of the 'custom' directory */
   732 	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/custom", m_File);
   733 	IDirectory *customDir = g_LibSys.OpenDirectory(path);
   734 
   735 	if (!customDir)
   736 	{
   737 		return true;
   738 	}
   739 
   740 	while (customDir->MoreFiles())
   741 	{
   742 		if (!customDir->IsEntryFile())
   743 		{
   744 			customDir->NextEntry();
   745 			continue;
   746 		}
   747 		
   748 		const char *curFile = customDir->GetEntryName();
   749 
   750 		/* Only allow .txt files */
   751 		int len = strlen(curFile);
   752 		if (len > 4 && strcmp(&curFile[len-4], ".txt") != 0)
   753 		{
   754 			customDir->NextEntry();
   755 			continue;	
   756 		}
   757 
   758 		UTIL_Format(path, sizeof(path), "%s/custom/%s", m_File, curFile);
   759 		if (!EnterFile(path, error, maxlength))
   760 		{
   761 			g_LibSys.CloseDirectory(customDir);
   762 			return false;
   763 		}
   764 
   765 		customDir->NextEntry();
   766 	}
   767 
   768 	g_LibSys.CloseDirectory(customDir);
   769 	return true;
   770 }
   771 
   772 bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
   773 {
   774 	SMCError err;
   775 	SMCStates state = {0, 0};
   776 
   777 	g_SourceMod.BuildPath(Path_SM, m_CurFile, sizeof(m_CurFile), "gamedata/%s", file);
   778 
   779 	/* Initialize parse states */
   780 	m_IgnoreLevel = 0;
   781 	bShouldBeReadingDefault = true;
   782 	m_ParseState = PSTATE_NONE;
   783 
   784 	if ((err=textparsers->ParseSMCFile(m_CurFile, this, &state, error, maxlength))
   785 		!= SMCError_Okay)
   786 	{
   787 		const char *msg;
   788 
   789 		msg = textparsers->GetSMCErrorString(err);
   790 
   791 		g_Logger.LogError("[SM] Error parsing gameconfig file \"%s\":", m_CurFile);
   792 		g_Logger.LogError("[SM] Error %d on line %d, col %d: %s", 
   793 			err,
   794 			state.line,
   795 			state.col,
   796 			msg ? msg : "Unknown error");
   797 
   798 		if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
   799 		{
   800 			//error occurred while parsing a custom section
   801 			m_CustomHandler->ReadSMC_ParseEnd(true, true);
   802 			m_CustomHandler = NULL;
   803 			m_CustomLevel = 0;
   804 		}
   805 
   806 		return false;
   807 	}
   808 
   809 	return true;
   810 }
   811 
   812 bool CGameConfig::GetOffset(const char *key, int *value)
   813 {
   814 	void *obj;
   815 
   816 	if (!sm_trie_retrieve(m_pOffsets, key, &obj))
   817 	{
   818 		return false;
   819 	}
   820 
   821 	*value = (int)obj;
   822 	
   823 	return true;
   824 }
   825 
   826 const char *CGameConfig::GetKeyValue(const char *key)
   827 {
   828 	void *obj;
   829 	if (!sm_trie_retrieve(m_pKeys, key, &obj))
   830 	{
   831 		return NULL;
   832 	}
   833 	return m_pStrings->GetString((int)obj);
   834 }
   835 
   836 SendProp *CGameConfig::GetSendProp(const char *key)
   837 {
   838 	SendProp *pProp;
   839 
   840 	if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
   841 	{
   842 		return NULL;
   843 	}
   844 
   845 	return pProp;
   846 }
   847 
   848 bool CGameConfig::GetMemSig(const char *key, void **addr)
   849 {
   850 	return sm_trie_retrieve(m_pSigs, key, addr);
   851 }
   852 
   853 void CGameConfig::IncRefCount()
   854 {
   855 	m_RefCount++;
   856 }
   857 
   858 unsigned int CGameConfig::DecRefCount()
   859 {
   860 	m_RefCount--;
   861 	return m_RefCount;
   862 }
   863 
   864 GameConfigManager::GameConfigManager()
   865 {
   866 	m_pLookup = sm_trie_create();
   867 }
   868 
   869 GameConfigManager::~GameConfigManager()
   870 {
   871 	sm_trie_destroy(m_pLookup);
   872 }
   873 
   874 void GameConfigManager::OnSourceModStartup(bool late)
   875 {
   876 	LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
   877 
   878 	strncopy(g_Game, g_SourceMod.GetGameFolderName(), sizeof(g_Game));
   879 	strncopy(g_GameDesc + 1, SERVER_CALL(GetGameDescription)(), sizeof(g_GameDesc) - 1);
   880 
   881 	KeyValues *pGameInfo = new KeyValues("GameInfo");
   882 	if (g_HL2.KVLoadFromFile(pGameInfo, basefilesystem, "gameinfo.txt"))
   883 	{
   884 		const char *str;
   885 		if ((str = pGameInfo->GetString("game", NULL)) != NULL)
   886 		{
   887 			strncopy(g_GameName + 1, str, sizeof(g_GameName) - 1);
   888 		}
   889 	}
   890 	pGameInfo->deleteThis();
   891 }
   892 
   893 void GameConfigManager::OnSourceModAllInitialized()
   894 {
   895 	/* NOW initialize the game file */
   896 	CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
   897 	
   898 	char error[255];
   899 	if (!pGameConf->Reparse(error, sizeof(error)))
   900 	{
   901 		/* :TODO: log */
   902 	}
   903 
   904 	g_ShareSys.AddInterface(NULL, this);
   905 }
   906 
   907 void GameConfigManager::OnSourceModAllShutdown()
   908 {
   909 	CloseGameConfigFile(g_pGameConf);
   910 }
   911 
   912 bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
   913 {
   914 #if 0
   915 	/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
   916 	if (g_blockGameDataLoad)
   917 	{
   918 		UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
   919 		return false;
   920 	}
   921 #endif
   922 
   923 	CGameConfig *pConfig;
   924 
   925 	if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
   926 	{
   927 		pConfig->IncRefCount();
   928 		*_pConfig = pConfig;
   929 		return true;
   930 	}
   931 
   932 	pConfig = new CGameConfig(file);
   933 
   934 	/* :HACKHACK: Don't parse the main config file */
   935 	bool retval = true;
   936 	if (_pConfig != &g_pGameConf)
   937 	{
   938 		retval = pConfig->Reparse(error, maxlength);
   939 	}
   940 
   941 	m_cfgs.push_back(pConfig);
   942 	sm_trie_insert(m_pLookup, file, pConfig);
   943 
   944 	pConfig->IncRefCount();
   945 
   946 	*_pConfig = pConfig;
   947 
   948 	return retval;
   949 }
   950 
   951 void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
   952 {
   953 	CGameConfig *pConfig = (CGameConfig *)cfg;
   954 
   955 	if (pConfig->DecRefCount() == 0)
   956 	{
   957 		sm_trie_delete(m_pLookup, pConfig->m_File);
   958 		delete pConfig;
   959 	}
   960 }
   961 
   962 extern HandleType_t g_GameConfigsType;
   963 
   964 IGameConfig *GameConfigManager::ReadHandle(Handle_t hndl, IdentityToken_t *ident, HandleError *err)
   965 {
   966 	HandleSecurity sec(ident, g_pCoreIdent);
   967 	IGameConfig *conf = NULL;
   968 	HandleError _err = g_HandleSys.ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&conf);
   969 
   970 	if (err)
   971 	{
   972 		*err = _err;
   973 	}
   974 
   975 	return conf;
   976 }
   977 
   978 void GameConfigManager::AddUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
   979 {
   980 	m_customHandlers.insert(sectionname, listener);
   981 }
   982 
   983 void GameConfigManager::RemoveUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
   984 {
   985 	ITextListener_SMC **pListener = m_customHandlers.retrieve(sectionname);
   986 
   987 	if (pListener == NULL)
   988 	{
   989 		return;
   990 	}
   991 
   992 	if (*pListener != listener)
   993 	{
   994 		return;
   995 	}
   996 
   997 	m_customHandlers.remove(sectionname);
   998 	
   999 	return;
  1000 }
  1001 
  1002 void GameConfigManager::AcquireLock()
  1003 {
  1004 }
  1005 
  1006 void GameConfigManager::ReleaseLock()
  1007 {
  1008 }