core/logic/GameConfigs.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Aug 14 14:20:57 2013 -0400 (2013-08-14)
changeset 4008 490b01aa0683
parent 4007 d6f1e42f0263
child 4038 92fba8db1d33
permissions -rw-r--r--
Updated some CS:GO gamedata.
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2009 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 
    30 #include "common_logic.h"
    31 #include <string.h>
    32 #include <stdlib.h>
    33 #include <sh_list.h>
    34 #include <sh_string.h>
    35 #include "GameConfigs.h"
    36 #include "stringutil.h"
    37 #include <IGameHelpers.h>
    38 #include <ILibrarySys.h>
    39 #include <IHandleSys.h>
    40 #include <IMemoryUtils.h>
    41 #include <ISourceMod.h>
    42 #include "common_logic.h"
    43 #include "sm_crc32.h"
    44 #include "MemoryUtils.h"
    45 
    46 #if defined PLATFORM_POSIX
    47 #include <dlfcn.h>
    48 #endif
    49 
    50 using namespace SourceHook;
    51 
    52 GameConfigManager g_GameConfigs;
    53 IGameConfig *g_pGameConf = NULL;
    54 static char g_Game[256];
    55 static char g_GameDesc[256] = {'!', '\0'};
    56 static char g_GameName[256] = {'$', '\0'};
    57 static const char *g_pParseEngine = NULL;
    58 
    59 #define PSTATE_NONE						0
    60 #define PSTATE_GAMES					1
    61 #define PSTATE_GAMEDEFS					2
    62 #define PSTATE_GAMEDEFS_OFFSETS			3
    63 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET	4
    64 #define PSTATE_GAMEDEFS_KEYS			5
    65 #define PSTATE_GAMEDEFS_SUPPORTED		6
    66 #define PSTATE_GAMEDEFS_SIGNATURES		7
    67 #define PSTATE_GAMEDEFS_SIGNATURES_SIG	8
    68 #define PSTATE_GAMEDEFS_CRC				9
    69 #define PSTATE_GAMEDEFS_CRC_BINARY		10
    70 #define PSTATE_GAMEDEFS_CUSTOM			11
    71 #define PSTATE_GAMEDEFS_ADDRESSES		12
    72 #define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS	13
    73 #define PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ	14
    74 
    75 #if defined PLATFORM_WINDOWS
    76 #define PLATFORM_NAME				"windows"
    77 #define PLATFORM_SERVER_BINARY		"server.dll"
    78 #elif defined PLATFORM_LINUX
    79 #define PLATFORM_NAME				"linux"
    80 #define PLATFORM_SERVER_BINARY		"server_i486.so"
    81 #elif defined PLATFORM_APPLE
    82 #define PLATFORM_NAME               "mac"
    83 #define PLATFORM_SERVER_BINARY      "server.dylib"
    84 #endif
    85 
    86 struct TempSigInfo
    87 {
    88 	void Reset()
    89 	{
    90 		library[0] = '\0';
    91 		sig[0] = '\0';
    92 	}
    93 	char sig[512];
    94 	char library[64];
    95 } s_TempSig;
    96 unsigned int s_ServerBinCRC;
    97 bool s_ServerBinCRC_Ok = false;
    98 
    99 static bool DoesGameMatch(const char *value)
   100 {
   101 #if defined PLATFORM_WINDOWS
   102 	if (strcasecmp(value, g_Game) == 0 ||
   103 #else
   104 	if (strcmp(value, g_Game) == 0 ||
   105 #endif
   106 		strcmp(value, g_GameDesc) == 0 ||
   107 		strcmp(value, g_GameName) == 0)
   108 	{
   109 		return true;
   110 	}
   111 	return false;
   112 }
   113 
   114 static bool DoesEngineMatch(const char *value)
   115 {
   116 	return strcmp(value, g_pParseEngine) == 0;
   117 }
   118 
   119 CGameConfig::CGameConfig(const char *file, const char *engine)
   120 {
   121 	strncopy(m_File, file, sizeof(m_File));
   122 	m_pAddresses = new KTrie<AddressConf>();
   123 	m_pStrings = new BaseStringTable(512);
   124 	m_RefCount = 0;
   125 
   126 	m_CustomLevel = 0;
   127 	m_CustomHandler = NULL;
   128 
   129 	if (!engine)
   130 		m_pEngine = smcore.GetSourceEngineName();
   131 	else
   132 		m_pEngine = engine;
   133 
   134 	if (strcmp(m_pEngine, "css") == 0 || strcmp(m_pEngine, "dods") == 0 || strcmp(m_pEngine, "hl2dm") == 0 || strcmp(m_pEngine, "tf2") == 0)
   135 		this->SetBaseEngine("orangebox_valve");
   136 	else if (strcmp(m_pEngine, "nucleardawn") == 0)
   137 		this->SetBaseEngine("left4dead2");
   138 	else
   139 		this->SetBaseEngine(NULL);
   140 }
   141 
   142 CGameConfig::~CGameConfig()
   143 {
   144 	delete m_pAddresses;
   145 	delete m_pStrings;
   146 }
   147 
   148 SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
   149 {
   150 	if (m_IgnoreLevel)
   151 	{
   152 		m_IgnoreLevel++;
   153 		return SMCResult_Continue;
   154 	}
   155 
   156 	switch (m_ParseState)
   157 	{
   158 	case PSTATE_NONE:
   159 		{
   160 			if (strcmp(name, "Games") == 0)
   161 			{
   162 				m_ParseState = PSTATE_GAMES;
   163 			} else {
   164 				m_IgnoreLevel++;
   165 			}
   166 			break;
   167 		}
   168 	case PSTATE_GAMES:
   169 		{
   170 			if (strcmp(name, "*") == 0 ||
   171 				strcmp(name, "#default") == 0 ||
   172 				DoesGameMatch(name))
   173 			{
   174 				bShouldBeReadingDefault = true;
   175 				m_ParseState = PSTATE_GAMEDEFS;
   176 				strncopy(m_Game, name, sizeof(m_Game));
   177 			} else {
   178 				m_IgnoreLevel++;
   179 			}
   180 			break;
   181 		}
   182 	case PSTATE_GAMEDEFS:
   183 		{
   184 			if (strcmp(name, "Offsets") == 0)
   185 			{
   186 				m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   187 			}
   188 			else if (strcmp(name, "Keys") == 0)
   189 			{
   190 				m_ParseState = PSTATE_GAMEDEFS_KEYS;
   191 			}
   192 			else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
   193 			{
   194 				m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
   195 				/* Ignore this section unless we get a game. */
   196 				bShouldBeReadingDefault = false;
   197 				had_game = false;
   198 				matched_game = false;
   199 				had_engine = false;
   200 				matched_engine = false;
   201 			}
   202 			else if (strcmp(name, "Signatures") == 0)
   203 			{
   204 				m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   205 			}
   206 			else if (strcmp(name, "CRC") == 0)
   207 			{
   208 				m_ParseState = PSTATE_GAMEDEFS_CRC;
   209 				bShouldBeReadingDefault = false;
   210 			}
   211 			else if (strcmp(name, "Addresses") == 0)
   212 			{
   213 				m_ParseState = PSTATE_GAMEDEFS_ADDRESSES;
   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 				smcore.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_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
   261 				if ((fp = fopen(path, "rb")) == NULL)
   262 				{
   263 					smcore.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 				smcore.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
   284 				smcore.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 	case PSTATE_GAMEDEFS_ADDRESSES:
   297 		{
   298 			m_Address[0] = '\0';
   299 			m_AddressSignature[0] = '\0';
   300 			m_AddressReadCount = 0;
   301 
   302 			strncopy(m_Address, name, sizeof(m_Address));
   303 			m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS;
   304 
   305 			break;
   306 		}
   307 	case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS:
   308 		{
   309 			if (strcmp(name, PLATFORM_NAME) == 0)
   310 			{
   311 				m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ;
   312 			}
   313 			else
   314 			{
   315 				if (strcmp(name, "linux") != 0 && strcmp(name, "windows") != 0 && strcmp(name, "mac") != 0)
   316 				{
   317 					smcore.LogError("[SM] Error while parsing Address section for \"%s\" (%s):", m_Address, m_CurFile);
   318 					smcore.LogError("[SM] Unrecognized platform \"%s\"", name);
   319 				}
   320 				m_IgnoreLevel = 1;
   321 			}
   322 			break;
   323 		}
   324 	/* No sub-sections allowed:
   325 	 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   326 	 case PSTATE_GAMEDEFS_KEYS:
   327 	 case PSTATE_GAMEDEFS_SUPPORTED:
   328 	 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   329 	 case PSTATE_GAMEDEFS_CRC_BINARY:
   330 	 case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ:
   331 	 */
   332 	default:
   333 		{
   334 			/* If we don't know what we got, start ignoring */
   335 			m_IgnoreLevel++;
   336 			break;
   337 		}
   338 	}
   339 
   340 	return SMCResult_Continue;
   341 }
   342 
   343 SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   344 {
   345 	if (m_IgnoreLevel)
   346 	{
   347 		return SMCResult_Continue;
   348 	}
   349 
   350 	if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
   351 	{
   352 		if (strcmp(key, "class") == 0)
   353 		{
   354 			strncopy(m_Class, value, sizeof(m_Class));
   355 		} else if (strcmp(key, "prop") == 0) {
   356 			strncopy(m_Prop, value, sizeof(m_Prop));
   357 		} else if (strcmp(key, PLATFORM_NAME) == 0) {
   358 			m_Offsets.replace(m_offset, atoi(value));
   359 		}
   360 	} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
   361 		m_Keys.replace(key, m_pStrings->AddString(value));
   362 	} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
   363 		if (strcmp(key, "game") == 0)
   364 		{
   365 			had_game = true;
   366 			if (DoesGameMatch(value))
   367 			{
   368 				matched_game = true;
   369 			}
   370 			if ((!had_engine && matched_game) || (matched_engine && matched_game))
   371 			{
   372 				bShouldBeReadingDefault = true;
   373 			}
   374 		}
   375 		else if (strcmp(key, "engine") == 0)
   376 		{
   377 			had_engine = true;
   378 			if (DoesEngineMatch(value))
   379 			{
   380 				matched_engine = true;
   381 			}
   382 			if ((!had_game && matched_engine) || (matched_game && matched_engine))
   383 			{
   384 				bShouldBeReadingDefault = true;
   385 			}
   386 		}
   387 	} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
   388 		if (strcmp(key, PLATFORM_NAME) == 0)
   389 		{
   390 			strncopy(s_TempSig.sig, value, sizeof(s_TempSig.sig));
   391 		} else if (strcmp(key, "library") == 0) {
   392 			strncopy(s_TempSig.library, value, sizeof(s_TempSig.library));
   393 		}
   394 	} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
   395 		if (strcmp(key, PLATFORM_NAME) == 0 
   396 			&& s_ServerBinCRC_Ok
   397 			&& !bShouldBeReadingDefault)
   398 		{
   399 			unsigned int crc = 0;
   400 			sscanf(value, "%08X", &crc);
   401 			if (s_ServerBinCRC == crc)
   402 			{
   403 				bShouldBeReadingDefault = true;
   404 			}
   405 		}
   406 	} else if (m_ParseState == PSTATE_GAMEDEFS_ADDRESSES_ADDRESS || m_ParseState == PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ) {
   407 		if (strcmp(key, "read") == 0) {
   408 			int limit = sizeof(m_AddressRead)/sizeof(m_AddressRead[0]);
   409 			if (m_AddressReadCount < limit)
   410 			{
   411 				m_AddressRead[m_AddressReadCount] = atoi(value);
   412 				m_AddressReadCount++;
   413 			}
   414 			else
   415 			{
   416 				smcore.LogError("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", m_Address, limit, m_CurFile);
   417 			}
   418 		} else if (strcmp(key, "signature") == 0) {
   419 			strncopy(m_AddressSignature, value, sizeof(m_AddressSignature));
   420 		}
   421 	} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
   422 		return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
   423 	}
   424 
   425 	return SMCResult_Continue;
   426 }
   427 
   428 SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
   429 {
   430 	if (m_IgnoreLevel)
   431 	{
   432 		m_IgnoreLevel--;
   433 		return SMCResult_Continue;
   434 	}
   435 
   436 	if (m_CustomLevel)
   437 	{
   438 		m_CustomLevel--;
   439 		m_CustomHandler->ReadSMC_LeavingSection(states);
   440 		return SMCResult_Continue;
   441 	}
   442 
   443 	switch (m_ParseState)
   444 	{
   445 	case PSTATE_GAMES:
   446 		{
   447 			m_ParseState = PSTATE_NONE;
   448 			break;
   449 		}
   450 	case PSTATE_GAMEDEFS:
   451 		{
   452 			m_ParseState = PSTATE_GAMES;
   453 			break;
   454 		}
   455 	case PSTATE_GAMEDEFS_CUSTOM:
   456 		{
   457 			m_ParseState = PSTATE_GAMEDEFS;
   458 			m_CustomHandler->ReadSMC_ParseEnd(false, false);
   459 			break;
   460 		}
   461 	case PSTATE_GAMEDEFS_KEYS:
   462 	case PSTATE_GAMEDEFS_OFFSETS:
   463 		{
   464 			m_ParseState = PSTATE_GAMEDEFS;
   465 			break;
   466 		}
   467 	case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   468 		{
   469 			/* Parse the offset... */
   470 			if (m_Class[0] != '\0'
   471 				&& m_Prop[0] != '\0')
   472 			{
   473 				SendProp *pProp = gamehelpers->FindInSendTable(m_Class, m_Prop);
   474 				if (pProp)
   475 				{
   476 					int val = gamehelpers->GetSendPropOffset(pProp);
   477 					m_Offsets.replace(m_offset, val);
   478 					m_Props.replace(m_offset, pProp);
   479 				} else {
   480 					/* Check if it's a non-default game and no offsets exist */
   481 					if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
   482 						&& (!m_Offsets.retrieve(m_offset)))
   483 					{
   484 						smcore.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", 
   485 							m_Class,
   486 							m_Prop,
   487 							m_CurFile,
   488 							m_Game);
   489 					}
   490 				}
   491 			}
   492 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   493 			break;
   494 		}
   495 	case PSTATE_GAMEDEFS_CRC:
   496 	case PSTATE_GAMEDEFS_SUPPORTED:
   497 		{
   498 			if (!bShouldBeReadingDefault)
   499 			{
   500 				/* If we shouldn't read the rest of this section, set the ignore level. */
   501 				m_IgnoreLevel = 1;
   502 				m_ParseState = PSTATE_GAMES;
   503 			} else {
   504 				m_ParseState = PSTATE_GAMEDEFS;
   505 			}
   506 			break;
   507 		}
   508 	case PSTATE_GAMEDEFS_CRC_BINARY:
   509 		{
   510 			m_ParseState = PSTATE_GAMEDEFS_CRC;
   511 			break;
   512 		}
   513 	case PSTATE_GAMEDEFS_SIGNATURES:
   514 		{
   515 			m_ParseState = PSTATE_GAMEDEFS;
   516 			break;
   517 		}
   518 	case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   519 		{
   520 			if (s_TempSig.library[0] == '\0')
   521 			{
   522 				/* assume server */
   523 				strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
   524 			}
   525 			void *addrInBase = NULL;
   526 			if (strcmp(s_TempSig.library, "server") == 0)
   527 			{
   528 				addrInBase = smcore.serverFactory;
   529 			} else if (strcmp(s_TempSig.library, "engine") == 0) {
   530 				addrInBase = smcore.engineFactory;
   531 			} else if (strcmp(s_TempSig.library, "matchmaking_ds") == 0) {
   532 				addrInBase = smcore.matchmakingDSFactory;
   533 			}
   534 			void *final_addr = NULL;
   535 			if (addrInBase == NULL)
   536 			{
   537 				smcore.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")", 
   538 					s_TempSig.library, 
   539 					m_CurFile);
   540 			} else {
   541 				if (s_TempSig.sig[0] == '@')
   542 				{
   543 #if defined PLATFORM_WINDOWS
   544 					MEMORY_BASIC_INFORMATION mem;
   545 
   546 					if (VirtualQuery(addrInBase, &mem, sizeof(mem)))
   547 						final_addr = g_MemUtils.ResolveSymbol(mem.AllocationBase, &s_TempSig.sig[1]);
   548 					else
   549 						smcore.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")", s_TempSig.library, m_File);
   550 #elif defined PLATFORM_POSIX
   551 					Dl_info info;
   552 					/* GNU only: returns 0 on error, inconsistent! >:[ */
   553 					if (dladdr(addrInBase, &info) != 0)
   554 					{
   555 						void *handle = dlopen(info.dli_fname, RTLD_NOW);
   556 						if (handle)
   557 						{
   558 							if (smcore.SymbolsAreHidden())
   559 								final_addr = g_MemUtils.ResolveSymbol(handle, &s_TempSig.sig[1]);
   560 							else
   561 								final_addr = dlsym(handle, &s_TempSig.sig[1]);
   562 							dlclose(handle);
   563 						} else {
   564 							smcore.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
   565 								s_TempSig.library,
   566 								m_File);
   567 						}
   568 					} else {
   569 						smcore.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
   570 							s_TempSig.library,
   571 							m_File);
   572 					}
   573 #endif
   574 				}
   575 				if (final_addr)
   576 				{
   577 					goto skip_find;
   578 				}
   579 				/* First, preprocess the signature */
   580 				unsigned char real_sig[511];
   581 				size_t real_bytes;
   582 				size_t length;
   583 
   584 				real_bytes = 0;
   585 				length = strlen(s_TempSig.sig);
   586 
   587 				real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), s_TempSig.sig);
   588 
   589 				if (real_bytes >= 1)
   590 				{
   591 					final_addr = g_MemUtils.FindPattern(addrInBase, (char*)real_sig, real_bytes);
   592 				}
   593 			}
   594 
   595 skip_find:
   596 			m_Sigs.replace(m_offset, final_addr);
   597 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   598 
   599 			break;
   600 		}
   601 	case PSTATE_GAMEDEFS_ADDRESSES:
   602 		{
   603 			m_ParseState = PSTATE_GAMEDEFS;
   604 			break;
   605 		}
   606 	case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS:
   607 		{
   608 			m_ParseState = PSTATE_GAMEDEFS_ADDRESSES;
   609 
   610 			if (m_Address[0] == '\0')
   611 			{
   612 				smcore.LogError("[SM] Address sections must have names (gameconf \"%s\")", m_CurFile);
   613 				break;
   614 			}
   615 			if (m_AddressSignature[0] == '\0')
   616 			{
   617 				smcore.LogError("[SM] Address section for \"%s\" did not specify a signature (gameconf \"%s\")", m_Address, m_CurFile);
   618 				break;
   619 			}
   620 
   621 			AddressConf addrConf(m_AddressSignature, sizeof(m_AddressSignature), m_AddressReadCount, m_AddressRead);
   622 			m_pAddresses->replace(m_Address, addrConf);
   623 
   624 			break;
   625 		}
   626 	case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS_READ:
   627 		{
   628 			m_ParseState = PSTATE_GAMEDEFS_ADDRESSES_ADDRESS;
   629 			break;
   630 		}
   631 	}
   632 
   633 	return SMCResult_Continue;
   634 }
   635 
   636 #define MSTATE_NONE		0
   637 #define MSTATE_MAIN		1
   638 #define MSTATE_FILE		2
   639 
   640 class MasterReader : public ITextListener_SMC
   641 {
   642 public:
   643 	virtual void ReadSMC_ParseStart()
   644 	{
   645 		state = MSTATE_NONE;
   646 		ignoreLevel = 0;
   647 	}
   648 
   649 	virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   650 	{
   651 		if (ignoreLevel)
   652 		{
   653 			return SMCResult_Continue;
   654 		}
   655 
   656 		if (state == MSTATE_NONE)
   657 		{
   658 			if (strcmp(name, "Game Master") == 0)
   659 			{
   660 				state = MSTATE_MAIN;
   661 			}
   662 			else
   663 			{
   664 				ignoreLevel++;
   665 			}
   666 		}
   667 		else if (state == MSTATE_MAIN)
   668 		{
   669 			strncopy(cur_file, name, sizeof(cur_file));
   670 			had_engine = false;
   671 			matched_engine = false;
   672 			had_game = false;
   673 			matched_game = false;
   674 			state = MSTATE_FILE;
   675 		}
   676 		else if (state == MSTATE_FILE)
   677 		{
   678 			ignoreLevel++;
   679 		}
   680 
   681 		return SMCResult_Continue;
   682 	}
   683 
   684 	virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   685 	{
   686 		if (ignoreLevel || state != MSTATE_FILE)
   687 		{
   688 			return SMCResult_Continue;
   689 		}
   690 
   691 		if (strcmp(key, "engine") == 0)
   692 		{
   693 			had_engine = true;
   694 			if (DoesEngineMatch(value))
   695 			{
   696 				matched_engine = true;
   697 			}
   698 		}
   699 		else if (strcmp(key, "game") == 0)
   700 		{
   701 			had_game = true;
   702 			if (DoesGameMatch(value))
   703 			{
   704 				matched_game = true;
   705 			}
   706 		}
   707 
   708 		return SMCResult_Continue;
   709 	}
   710 
   711 	virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   712 	{
   713 		if (ignoreLevel)
   714 		{
   715 			ignoreLevel--;
   716 			return SMCResult_Continue;
   717 		}
   718 
   719 		if (state == MSTATE_FILE)
   720 		{
   721 			/* The four success conditions:
   722 			 * 1. Needed nothing.
   723 			 * 2. Needed game only.
   724 			 * 3. Needed engine only.
   725 			 * 4. Needed both engine and game.
   726 			 * Final result is minimized via k-map.
   727 			 */
   728 #if 0
   729 			if ((!had_engine && !had_game) ||
   730 				(!had_engine && (had_game && matched_game)) ||
   731 				(!had_game && (had_engine && matched_engine)) ||
   732 				((had_game && had_engine) && (matched_game && matched_engine)))
   733 #endif
   734 			if ((!had_engine && !had_game) ||
   735 				(!had_engine && matched_game) ||
   736 				(!had_game && matched_engine) ||
   737 				(matched_engine && matched_game))
   738 			{
   739 				fileList->push_back(cur_file);
   740 			}
   741 			state = MSTATE_MAIN;
   742 		}
   743 		else if (state == MSTATE_MAIN)
   744 		{
   745 			state = MSTATE_NONE;
   746 		}
   747 
   748 		return SMCResult_Continue;
   749 	}
   750 public:
   751 	List<String> *fileList;
   752 	unsigned int state;
   753 	unsigned int ignoreLevel;
   754 	char cur_file[PLATFORM_MAX_PATH];
   755 	bool had_engine;
   756 	bool matched_engine;
   757 	bool had_game;
   758 	bool matched_game;
   759 };
   760 
   761 static MasterReader master_reader;
   762 
   763 bool CGameConfig::Reparse(char *error, size_t maxlength)
   764 {
   765 	/* Reset cached data */
   766 	m_pStrings->Reset();
   767 	m_Offsets.clear();
   768 	m_Props.clear();
   769 	m_Keys.clear();
   770 	m_pAddresses->clear();
   771 
   772 	char path[PLATFORM_MAX_PATH];
   773 
   774 	/* See if we can use the extended gamedata format. */
   775 	g_pSM->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
   776 	if (!libsys->PathExists(path))
   777 	{
   778 		/* Nope, use the old mechanism. */
   779 		smcore.Format(path, sizeof(path), "%s.txt", m_File);
   780 		return EnterFile(path, error, maxlength);
   781 	}
   782 
   783 	/* Otherwise, it's time to parse the master. */
   784 	SMCError err;
   785 	SMCStates state = {0, 0};
   786 	List<String> fileList;
   787 	master_reader.fileList = &fileList;
   788 	const char *pEngine[2] = { m_pBaseEngine, m_pEngine  };
   789 
   790 	for (unsigned char iter = 0; iter < SM_ARRAYSIZE(pEngine); ++iter)
   791 	{
   792 		if (pEngine[iter] == NULL)
   793 		{
   794 			continue;
   795 		}
   796 
   797 		this->SetParseEngine(pEngine[iter]);
   798 		err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
   799 		if (err != SMCError_Okay)
   800 		{
   801 			const char *msg = textparsers->GetSMCErrorString(err);
   802 
   803 			smcore.LogError("[SM] Error parsing master gameconf file \"%s\":", path);
   804 			smcore.LogError("[SM] Error %d on line %d, col %d: %s", 
   805 				err,
   806 				state.line,
   807 				state.col,
   808 				msg ? msg : "Unknown error");
   809 			return false;
   810 		}
   811 	}
   812 
   813 	/* Go through each file we found and parse it. */
   814 	List<String>::iterator iter;
   815 	for (iter = fileList.begin(); iter != fileList.end(); iter++)
   816 	{
   817 		smcore.Format(path, sizeof(path), "%s/%s", m_File, (*iter).c_str());
   818 		if (!EnterFile(path, error, maxlength))
   819 		{
   820 			return false;
   821 		}
   822 	}
   823 
   824 	/* Parse the contents of the 'custom' directory */
   825 	g_pSM->BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/custom", m_File);
   826 	IDirectory *customDir = libsys->OpenDirectory(path);
   827 
   828 	if (!customDir)
   829 	{
   830 		return true;
   831 	}
   832 
   833 	while (customDir->MoreFiles())
   834 	{
   835 		if (!customDir->IsEntryFile())
   836 		{
   837 			customDir->NextEntry();
   838 			continue;
   839 		}
   840 		
   841 		const char *curFile = customDir->GetEntryName();
   842 
   843 		/* Only allow .txt files */
   844 		int len = strlen(curFile);
   845 		if (len > 4 && strcmp(&curFile[len-4], ".txt") != 0)
   846 		{
   847 			customDir->NextEntry();
   848 			continue;	
   849 		}
   850 
   851 		smcore.Format(path, sizeof(path), "%s/custom/%s", m_File, curFile);
   852 		if (!EnterFile(path, error, maxlength))
   853 		{
   854 			libsys->CloseDirectory(customDir);
   855 			return false;
   856 		}
   857 
   858 		customDir->NextEntry();
   859 	}
   860 
   861 	libsys->CloseDirectory(customDir);
   862 	return true;
   863 }
   864 
   865 bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
   866 {
   867 	SMCError err;
   868 	SMCStates state = {0, 0};
   869 
   870 	g_pSM->BuildPath(Path_SM, m_CurFile, sizeof(m_CurFile), "gamedata/%s", file);
   871 
   872 	/* Initialize parse states */
   873 	m_IgnoreLevel = 0;
   874 	bShouldBeReadingDefault = true;
   875 	m_ParseState = PSTATE_NONE;
   876 	const char *pEngine[2] = { m_pBaseEngine, m_pEngine };
   877 
   878 	for (unsigned char iter = 0; iter < SM_ARRAYSIZE(pEngine); ++iter)
   879 	{
   880 		if (pEngine[iter] == NULL)
   881 		{
   882 			continue;
   883 		}
   884 
   885 		this->SetParseEngine(pEngine[iter]);
   886 		if ((err=textparsers->ParseSMCFile(m_CurFile, this, &state, error, maxlength))
   887 			!= SMCError_Okay)
   888 		{
   889 			const char *msg = textparsers->GetSMCErrorString(err);
   890 
   891 			smcore.LogError("[SM] Error parsing gameconfig file \"%s\":", m_CurFile);
   892 			smcore.LogError("[SM] Error %d on line %d, col %d: %s", 
   893 				err,
   894 				state.line,
   895 				state.col,
   896 				msg ? msg : "Unknown error");
   897 
   898 			if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
   899 			{
   900 				//error occurred while parsing a custom section
   901 				m_CustomHandler->ReadSMC_ParseEnd(true, true);
   902 				m_CustomHandler = NULL;
   903 				m_CustomLevel = 0;
   904 			}
   905 
   906 			return false;
   907 		}
   908 	}
   909 
   910 	return true;
   911 }
   912 
   913 void CGameConfig::SetBaseEngine(const char *engine)
   914 {
   915 	m_pBaseEngine = engine;
   916 }
   917 
   918 void CGameConfig::SetParseEngine(const char *engine)
   919 {
   920 	g_pParseEngine = engine;
   921 }
   922 
   923 bool CGameConfig::GetOffset(const char *key, int *value)
   924 {
   925 	int *pvalue;
   926 	if ((pvalue = m_Offsets.retrieve(key)) == NULL)
   927 		return false;
   928 
   929 	*value = *pvalue;
   930 	return true;
   931 }
   932 
   933 const char *CGameConfig::GetKeyValue(const char *key)
   934 {
   935 	int *pkey;
   936 	if ((pkey = m_Keys.retrieve(key)) == NULL)
   937 		return NULL;
   938 	return m_pStrings->GetString(*pkey);
   939 }
   940 
   941 //memory addresses below 0x10000 are automatically considered invalid for dereferencing
   942 #define VALID_MINIMUM_MEMORY_ADDRESS 0x10000
   943 
   944 bool CGameConfig::GetAddress(const char *key, void **retaddr)
   945 {
   946 	AddressConf *addrConf;
   947 
   948 	addrConf = m_pAddresses->retrieve(key);
   949 	if (!addrConf)
   950 	{
   951 		*retaddr = NULL;
   952 		return false;
   953 	}
   954 
   955 	void *addr;
   956 	if (!GetMemSig(addrConf->signatureName, &addr))
   957 	{
   958 		*retaddr = NULL;
   959 		return false;
   960 	}
   961 
   962 	for (int i = 0; i < addrConf->readCount; ++i)
   963 	{
   964 		int offset = addrConf->read[i];
   965 
   966 		//NULLs in the middle of an indirection chain are bad, end NULL is ok
   967 		if (addr ==  NULL || reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
   968 		{
   969 			*retaddr = NULL;
   970 			return false;
   971 		}
   972 		addr = *(reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(addr) + offset));
   973 	}
   974 
   975 	*retaddr = addr;
   976 	return true;
   977 }
   978 
   979 static inline unsigned minOf(unsigned a, unsigned b)
   980 {
   981 	return a <= b ? a : b;
   982 }
   983 
   984 CGameConfig::AddressConf::AddressConf(char *sigName, unsigned sigLength, unsigned readCount, int *read)
   985 {
   986 	unsigned readLimit = minOf(readCount, sizeof(this->read) / sizeof(this->read[0]));
   987 
   988 	strncopy(signatureName, sigName, sizeof(signatureName) / sizeof(signatureName[0]));
   989 	this->readCount = readLimit;
   990 	memcpy(&this->read[0], read, sizeof(this->read[0])*readLimit);
   991 }
   992 
   993 SendProp *CGameConfig::GetSendProp(const char *key)
   994 {
   995 	SendProp **pProp;
   996 
   997 	if ((pProp = m_Props.retrieve(key)) == NULL)
   998 		return NULL;
   999 
  1000 	return *pProp;
  1001 }
  1002 
  1003 bool CGameConfig::GetMemSig(const char *key, void **addr)
  1004 {
  1005 	void **paddr;
  1006 	if ((paddr = m_Sigs.retrieve(key)) == NULL)
  1007 		return false;
  1008 	*addr = *paddr;
  1009 	return true;
  1010 }
  1011 
  1012 void CGameConfig::IncRefCount()
  1013 {
  1014 	m_RefCount++;
  1015 }
  1016 
  1017 unsigned int CGameConfig::DecRefCount()
  1018 {
  1019 	m_RefCount--;
  1020 	return m_RefCount;
  1021 }
  1022 
  1023 GameConfigManager::GameConfigManager()
  1024 {
  1025 }
  1026 
  1027 GameConfigManager::~GameConfigManager()
  1028 {
  1029 }
  1030 
  1031 void GameConfigManager::OnSourceModStartup(bool late)
  1032 {
  1033 	LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
  1034 
  1035 	strncopy(g_Game, g_pSM->GetGameFolderName(), sizeof(g_Game));
  1036 	strncopy(g_GameDesc + 1, smcore.GetGameDescription(), sizeof(g_GameDesc) - 1);
  1037 	smcore.GetGameName(g_GameName + 1, sizeof(g_GameName) - 1);
  1038 }
  1039 
  1040 void GameConfigManager::OnSourceModAllInitialized()
  1041 {
  1042 	/* NOW initialize the game file */
  1043 	CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
  1044 	
  1045 	char error[255];
  1046 	if (!pGameConf->Reparse(error, sizeof(error)))
  1047 	{
  1048 		/* :TODO: log */
  1049 	}
  1050 
  1051 	sharesys->AddInterface(NULL, this);
  1052 }
  1053 
  1054 void GameConfigManager::OnSourceModAllShutdown()
  1055 {
  1056 	CloseGameConfigFile(g_pGameConf);
  1057 }
  1058 
  1059 bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
  1060 {
  1061 #if 0
  1062 	/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
  1063 	if (g_blockGameDataLoad)
  1064 	{
  1065 		UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
  1066 		return false;
  1067 	}
  1068 #endif
  1069 
  1070 	CGameConfig *pConfig;
  1071 	CGameConfig **ppConfig;
  1072 
  1073 	if ((ppConfig = m_Lookup.retrieve(file)) != NULL)
  1074 	{
  1075 		pConfig = *ppConfig;
  1076 		pConfig->IncRefCount();
  1077 		*_pConfig = pConfig;
  1078 		return true;
  1079 	}
  1080 
  1081 	pConfig = new CGameConfig(file);
  1082 
  1083 	/* :HACKHACK: Don't parse the main config file */
  1084 	bool retval = true;
  1085 	if (_pConfig != &g_pGameConf)
  1086 	{
  1087 		retval = pConfig->Reparse(error, maxlength);
  1088 	}
  1089 
  1090 	m_cfgs.push_back(pConfig);
  1091 	m_Lookup.insert(file, pConfig);
  1092 
  1093 	pConfig->IncRefCount();
  1094 
  1095 	*_pConfig = pConfig;
  1096 
  1097 	return retval;
  1098 }
  1099 
  1100 void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
  1101 {
  1102 	CGameConfig *pConfig = (CGameConfig *)cfg;
  1103 
  1104 	if (pConfig->DecRefCount() == 0)
  1105 	{
  1106 		m_Lookup.remove(pConfig->m_File);
  1107 		delete pConfig;
  1108 	}
  1109 }
  1110 
  1111 extern Handle_t g_GameConfigsType;
  1112 
  1113 IGameConfig *GameConfigManager::ReadHandle(Handle_t hndl, IdentityToken_t *ident, HandleError *err)
  1114 {
  1115 	HandleSecurity sec(ident, g_pCoreIdent);
  1116 	IGameConfig *conf = NULL;
  1117 	HandleError _err = handlesys->ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&conf);
  1118 
  1119 	if (err)
  1120 	{
  1121 		*err = _err;
  1122 	}
  1123 
  1124 	return conf;
  1125 }
  1126 
  1127 void GameConfigManager::AddUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
  1128 {
  1129 	m_customHandlers.insert(sectionname, listener);
  1130 }
  1131 
  1132 void GameConfigManager::RemoveUserConfigHook(const char *sectionname, ITextListener_SMC *listener)
  1133 {
  1134 	ITextListener_SMC **pListener = m_customHandlers.retrieve(sectionname);
  1135 
  1136 	if (pListener == NULL)
  1137 	{
  1138 		return;
  1139 	}
  1140 
  1141 	if (*pListener != listener)
  1142 	{
  1143 		return;
  1144 	}
  1145 
  1146 	m_customHandlers.remove(sectionname);
  1147 	
  1148 	return;
  1149 }
  1150 
  1151 void GameConfigManager::AcquireLock()
  1152 {
  1153 }
  1154 
  1155 void GameConfigManager::ReleaseLock()
  1156 {
  1157 }