tools/gdc/GameConfigs.cpp
author Peace-Maker <januk@freenet.de>
Sat May 10 05:42:33 2014 -0700 (2014-05-10)
changeset 4354 7011587e0021
parent 3129 76208f2e5ef0
permissions -rw-r--r--
Remove LogMessageEx definition from logging.inc (bug 5897, r=kyles).
     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 #define PSTATE_NONE						0
    51 #define PSTATE_GAMES					1
    52 #define PSTATE_GAMEDEFS					2
    53 #define PSTATE_GAMEDEFS_OFFSETS			3
    54 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET	4
    55 #define PSTATE_GAMEDEFS_KEYS			5
    56 #define PSTATE_GAMEDEFS_SUPPORTED		6
    57 #define PSTATE_GAMEDEFS_SIGNATURES		7
    58 #define PSTATE_GAMEDEFS_SIGNATURES_SIG	8
    59 #define PSTATE_GAMEDEFS_CRC				9
    60 #define PSTATE_GAMEDEFS_CRC_BINARY		10
    61 #define PSTATE_GAMEDEFS_CUSTOM			11
    62 
    63 #define WIN 0
    64 #define LIN 1
    65 
    66 #define PLATFORM_NAME				"linux"
    67 #define PLATFORM_SERVER_BINARY		"server_i486.so"
    68 
    69 Offset *tempOffset;
    70 Sig *tempSig;
    71 
    72 unsigned int s_ServerBinCRC;
    73 bool s_ServerBinCRC_Ok = false;
    74 
    75 bool /*CGameConfig::*/DoesGameMatch(const char *value)
    76 {
    77 	if (strcmp(value, /*m_gdcG*/game) == 0)
    78 	{
    79 		return true;
    80 	}
    81 	return false;
    82 }
    83 
    84 bool /*CGameConfig::*/DoesEngineMatch(const char *value)
    85 {
    86 	if (strcmp(value, /*m_gdcE*/engine) == 0)
    87 	{
    88 		return true;
    89 	}
    90 	return false;
    91 }
    92 
    93 CGameConfig::CGameConfig()//const char *file)//, const char *game, const char *engine)
    94 {
    95 //	strncopy(m_File, file, sizeof(m_File));
    96 //	strncopy(m_gdcGame, game, sizeof(m_gdcGame));
    97 //	strncopy(m_gdcEngine, engine, sizeof(m_gdcEngine));
    98 	//m_pStrings = new BaseStringTable(512);
    99 	m_RefCount = 0;
   100 
   101 	m_CustomLevel = 0;
   102 	m_CustomHandler = NULL;
   103 }
   104 
   105 CGameConfig::~CGameConfig()
   106 {
   107 //	delete m_pStrings;
   108 }
   109 
   110 SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
   111 {
   112 	if (m_IgnoreLevel)
   113 	{
   114 		m_IgnoreLevel++;
   115 		return SMCResult_Continue;
   116 	}
   117 
   118 	switch (m_ParseState)
   119 	{
   120 	case PSTATE_NONE:
   121 		{
   122 			if (strcmp(name, "Games") == 0)
   123 			{
   124 				m_ParseState = PSTATE_GAMES;
   125 			} else {
   126 				m_IgnoreLevel++;
   127 			}
   128 			break;
   129 		}
   130 	case PSTATE_GAMES:
   131 		{
   132 			if (strcmp(name, "*") == 0 ||
   133 				strcmp(name, "#default") == 0 ||
   134 				DoesGameMatch(name))
   135 			{
   136 				bShouldBeReadingDefault = true;
   137 				m_ParseState = PSTATE_GAMEDEFS;
   138 				strncopy(m_Game, name, sizeof(m_Game));
   139 			} else {
   140 				m_IgnoreLevel++;
   141 			}
   142 			break;
   143 		}
   144 	case PSTATE_GAMEDEFS:
   145 		{
   146 			if (strcmp(name, "Offsets") == 0)
   147 			{
   148 				m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   149 			}
   150 			else if (strcmp(name, "Keys") == 0)
   151 			{
   152 				m_ParseState = PSTATE_GAMEDEFS_KEYS;
   153 			}
   154 			else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
   155 			{
   156 				m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
   157 				/* Ignore this section unless we get a game. */
   158 				bShouldBeReadingDefault = false;
   159 				had_game = false;
   160 				matched_game = false;
   161 				had_engine = false;
   162 				matched_engine = false;
   163 			}
   164 			else if (strcmp(name, "Signatures") == 0)
   165 			{
   166 				m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   167 			}
   168 			else if (strcmp(name, "CRC") == 0)
   169 			{
   170 #if 0
   171 				m_ParseState = PSTATE_GAMEDEFS_CRC;
   172 				bShouldBeReadingDefault = false;
   173 #endif
   174 			}
   175 			else
   176 			{
   177 #if 0
   178 				ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name);
   179 
   180 				if (pListen != NULL)
   181 				{
   182 					m_CustomLevel = 0;
   183 					m_ParseState = PSTATE_GAMEDEFS_CUSTOM;
   184 					m_CustomHandler = *pListen;
   185 					m_CustomHandler->ReadSMC_ParseStart();
   186 					break;
   187 				}
   188 #endif
   189 			}
   190 			break;
   191 		}
   192 	case PSTATE_GAMEDEFS_OFFSETS:
   193 		{
   194 			m_Prop[0] = '\0';
   195 			m_Class[0] = '\0';
   196 			tempOffset = new Offset();
   197 			tempOffset->setName(name);
   198 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
   199 			break;
   200 		}
   201 	case PSTATE_GAMEDEFS_SIGNATURES:
   202 		{
   203 			tempSig = new Sig();
   204 			tempSig->setName(name);
   205 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
   206 			break;
   207 		}
   208 #if 0
   209 	case PSTATE_GAMEDEFS_CRC:
   210 		{
   211 			char error[255];
   212 			error[0] = '\0';
   213 			if (strcmp(name, "server") != 0)
   214 			{
   215 				UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name);
   216 			} 
   217 			else if (!s_ServerBinCRC_Ok)
   218 			{
   219 				FILE *fp;
   220 				char path[PLATFORM_MAX_PATH];
   221 
   222 				g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
   223 				if ((fp = fopen(path, "rb")) == NULL)
   224 				{
   225 					UTIL_Format(error, sizeof(error), "Could not open binary: %s", path);
   226 				} else {
   227 					size_t size;
   228 					void *buffer;
   229 
   230 					fseek(fp, 0, SEEK_END);
   231 					size = ftell(fp);
   232 					fseek(fp, 0, SEEK_SET);
   233 
   234 					buffer = malloc(size);
   235 					fread(buffer, size, 1, fp);
   236 					s_ServerBinCRC = UTIL_CRC32(buffer, size);
   237 					free(buffer);
   238 					s_ServerBinCRC_Ok = true;
   239 					fclose(fp);
   240 				}
   241 			}
   242 			if (error[0] != '\0')
   243 			{
   244 				m_IgnoreLevel = 1;
   245 				g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
   246 				g_Logger.LogError("[SM] %s", error);
   247 			} else {
   248 				m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
   249 			}
   250 			break;
   251 		}
   252 	case PSTATE_GAMEDEFS_CUSTOM:
   253 		{
   254 			m_CustomLevel++;
   255 			return m_CustomHandler->ReadSMC_NewSection(states, name);
   256 			break;
   257 		}
   258 #endif
   259 	/* No sub-sections allowed:
   260 	 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   261 	 case PSTATE_GAMEDEFS_KEYS:
   262 	 case PSTATE_GAMEDEFS_SUPPORTED:
   263 	 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   264 	 case PSTATE_GAMEDEFS_CRC_BINARY:
   265 	 */
   266 	default:
   267 		{
   268 			/* If we don't know what we got, start ignoring */
   269 			m_IgnoreLevel++;
   270 			break;
   271 		}
   272 	}
   273 
   274 	return SMCResult_Continue;
   275 }
   276 
   277 SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   278 {
   279 	if (m_IgnoreLevel)
   280 	{
   281 		return SMCResult_Continue;
   282 	}
   283 
   284 	if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
   285 	{
   286 		if (strcmp(key, "class") == 0)
   287 		{
   288 			strncopy(m_Class, value, sizeof(m_Class));
   289 		} else if (strcmp(key, "prop") == 0) {
   290 			strncopy(m_Prop, value, sizeof(m_Prop));
   291 		} else /*if (strcmp(key, PLATFORM_NAME) == 0) */ {
   292 			int val = atoi(value);
   293 //			sm_trie_replace(m_pOffsets, m_offset, (void *)val);
   294 			if (strcmp(key, "windows") == 0) tempOffset->setWin(val);
   295 			else if (strcmp(key, "linux") == 0) tempOffset->setLin(val);
   296 		}
   297 	} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
   298 		m_Keys[key] = value;
   299 //		printf("Inserting %s - %s\n", key, value);
   300 //		int id = m_pStrings->AddString(value);
   301 //		sm_trie_replace(m_pKeys, key, (void *)id);
   302 	} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
   303 		if (strcmp(key, "game") == 0)
   304 		{
   305 			had_game = true;
   306 			if (DoesGameMatch(value))
   307 			{
   308 				matched_game = true;
   309 			}
   310 			if ((!had_engine && matched_game) || (matched_engine && matched_game))
   311 			{
   312 				bShouldBeReadingDefault = true;
   313 			}
   314 		}
   315 		else if (strcmp(key, "engine") == 0)
   316 		{
   317 			had_engine = true;
   318 			if (DoesEngineMatch(value))
   319 			{
   320 				matched_engine = true;
   321 			}
   322 			if ((!had_game && matched_engine) || (matched_game && matched_engine))
   323 			{
   324 				bShouldBeReadingDefault = true;
   325 			}
   326 		}
   327 	} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
   328 		if (strcmp(key, "windows") == 0) tempSig->setWin(value);
   329 		else if (strcmp(key, "linux") == 0) tempSig->setLin(value);
   330 		else if (strcmp(key, "library") == 0) tempSig->setLib(value);
   331 	} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
   332 		if (strcmp(key, PLATFORM_NAME) == 0 
   333 			&& s_ServerBinCRC_Ok
   334 			&& !bShouldBeReadingDefault)
   335 		{
   336 			unsigned int crc = 0;
   337 			sscanf(value, "%08X", &crc);
   338 			if (s_ServerBinCRC == crc)
   339 			{
   340 				bShouldBeReadingDefault = true;
   341 			}
   342 		}
   343 	} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
   344 		return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
   345 	}
   346 
   347 	return SMCResult_Continue;
   348 }
   349 
   350 SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
   351 {
   352 	if (m_IgnoreLevel)
   353 	{
   354 		m_IgnoreLevel--;
   355 		return SMCResult_Continue;
   356 	}
   357 
   358 	if (m_CustomLevel)
   359 	{
   360 		m_CustomLevel--;
   361 		m_CustomHandler->ReadSMC_LeavingSection(states);
   362 		return SMCResult_Continue;
   363 	}
   364 
   365 	switch (m_ParseState)
   366 	{
   367 	case PSTATE_GAMES:
   368 		{
   369 			m_ParseState = PSTATE_NONE;
   370 			break;
   371 		}
   372 	case PSTATE_GAMEDEFS:
   373 		{
   374 			m_ParseState = PSTATE_GAMES;
   375 			break;
   376 		}
   377 	case PSTATE_GAMEDEFS_CUSTOM:
   378 		{
   379 			m_ParseState = PSTATE_GAMEDEFS;
   380 			m_CustomHandler->ReadSMC_ParseEnd(false, false);
   381 			break;
   382 		}
   383 	case PSTATE_GAMEDEFS_KEYS:
   384 	case PSTATE_GAMEDEFS_OFFSETS:
   385 		{
   386 			m_ParseState = PSTATE_GAMEDEFS;
   387 			break;
   388 		}
   389 	case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   390 		{
   391 			m_Offsets.push_back(*tempOffset);
   392 #if 0
   393 			/* Parse the offset... */
   394 			if (m_Class[0] != '\0'
   395 				&& m_Prop[0] != '\0')
   396 			{
   397 				SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop);
   398 				if (pProp)
   399 				{
   400 					int val = pProp->GetOffset();
   401 					sm_trie_replace(m_pOffsets, m_offset, (void *)val);
   402 					sm_trie_replace(m_pProps, m_offset, pProp);
   403 				} else {
   404 					/* Check if it's a non-default game and no offsets exist */
   405 					if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
   406 						&& (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
   407 					{
   408 						g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", 
   409 							m_Class,
   410 							m_Prop,
   411 							m_CurFile,
   412 							m_Game);
   413 					}
   414 				}
   415 			}
   416 #endif
   417 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   418 			break;
   419 		}
   420 	case PSTATE_GAMEDEFS_CRC:
   421 	case PSTATE_GAMEDEFS_SUPPORTED:
   422 		{
   423 			if (!bShouldBeReadingDefault)
   424 			{
   425 				/* If we shouldn't read the rest of this section, set the ignore level. */
   426 				m_IgnoreLevel = 1;
   427 				m_ParseState = PSTATE_GAMES;
   428 			} else {
   429 				m_ParseState = PSTATE_GAMEDEFS;
   430 			}
   431 			break;
   432 		}
   433 	case PSTATE_GAMEDEFS_CRC_BINARY:
   434 		{
   435 			m_ParseState = PSTATE_GAMEDEFS_CRC;
   436 			break;
   437 		}
   438 	case PSTATE_GAMEDEFS_SIGNATURES:
   439 		{
   440 			m_ParseState = PSTATE_GAMEDEFS;
   441 			break;
   442 		}
   443 	case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   444 		{
   445 			m_Sigs.push_back(*tempSig);
   446 #if 0
   447 			if (s_TempSig.library[0] == '\0')
   448 			{
   449 				/* assume server */
   450 				strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
   451 			}
   452 			void *addrInBase = NULL;
   453 			if (strcmp(s_TempSig.library, "server") == 0)
   454 			{
   455 				addrInBase = (void *)g_SMAPI->GetServerFactory(false);
   456 			} else if (strcmp(s_TempSig.library, "engine") == 0) {
   457 				addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
   458 			}
   459 			void *final_addr = NULL;
   460 			if (addrInBase == NULL)
   461 			{
   462 				g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")", 
   463 					s_TempSig.library, 
   464 					m_CurFile);
   465 			} else {
   466 #if defined PLATFORM_LINUX
   467 				if (s_TempSig.sig[0] == '@')
   468 				{
   469 					Dl_info info;
   470 					/* GNU only: returns 0 on error, inconsistent! >:[ */
   471 					if (dladdr(addrInBase, &info) != 0)
   472 					{
   473 						void *handle = dlopen(info.dli_fname, RTLD_NOW);
   474 						if (handle)
   475 						{
   476 							final_addr = dlsym(handle, &s_TempSig.sig[1]);
   477 							dlclose(handle);
   478 						} else {
   479 							g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
   480 								s_TempSig.library,
   481 								m_File);
   482 						}
   483 					} else {
   484 						g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
   485 							s_TempSig.library,
   486 							m_File);
   487 					}
   488 				}
   489 				if (final_addr)
   490 				{
   491 					goto skip_find;
   492 				}
   493 #endif
   494 				/* First, preprocess the signature */
   495 				char real_sig[511];
   496 				size_t real_bytes;
   497 				size_t length;
   498 
   499 				real_bytes = 0;
   500 				length = strlen(s_TempSig.sig);
   501 
   502 				for (size_t i=0; i<length; i++)
   503 				{
   504 					if (real_bytes >= sizeof(real_sig))
   505 					{
   506 						break;
   507 					}
   508 					real_sig[real_bytes++] = s_TempSig.sig[i];
   509 					if (s_TempSig.sig[i] == '\\'
   510 						&& s_TempSig.sig[i+1] == 'x')
   511 					{
   512 						if (i + 3 >= length)
   513 						{
   514 							continue;
   515 						}
   516 						/* Get the hex part */
   517 						char s_byte[3];
   518 						int r_byte;
   519 						s_byte[0] = s_TempSig.sig[i+2];
   520 						s_byte[1] = s_TempSig.sig[i+3];
   521 						s_byte[2] = '\0';
   522 						/* Read it as an integer */
   523 						sscanf(s_byte, "%x", &r_byte);
   524 						/* Save the value */
   525 						real_sig[real_bytes-1] = r_byte;
   526 						/* Adjust index */
   527 						i += 3;
   528 					}
   529 				}
   530 
   531 				if (real_bytes >= 1)
   532 				{
   533 					final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes);
   534 				}
   535 			}
   536 
   537 #if defined PLATFORM_LINUX
   538 skip_find:
   539 #endif
   540 			sm_trie_replace(m_pSigs, m_offset, final_addr);
   541 #endif
   542 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   543 
   544 			break;
   545 		}
   546 	}
   547 
   548 	return SMCResult_Continue;
   549 }
   550 
   551 #define MSTATE_NONE		0
   552 #define MSTATE_MAIN		1
   553 #define MSTATE_FILE		2
   554 
   555 class MasterReader : public ITextListener_SMC
   556 {
   557 public:
   558 	virtual void ReadSMC_ParseStart()
   559 	{
   560 		state = MSTATE_NONE;
   561 		ignoreLevel = 0;
   562 	}
   563 
   564 	virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   565 	{
   566 		if (ignoreLevel)
   567 		{
   568 			return SMCResult_Continue;
   569 		}
   570 
   571 		if (state == MSTATE_NONE)
   572 		{
   573 			if (strcmp(name, "Game Master") == 0)
   574 			{
   575 				state = MSTATE_MAIN;
   576 			}
   577 			else
   578 			{
   579 				ignoreLevel++;
   580 			}
   581 		}
   582 		else if (state == MSTATE_MAIN)
   583 		{
   584 			strncopy(cur_file, name, sizeof(cur_file));
   585 			had_engine = false;
   586 			matched_engine = false;
   587 			had_game = false;
   588 			matched_game = false;
   589 			state = MSTATE_FILE;
   590 		}
   591 		else if (state == MSTATE_FILE)
   592 		{
   593 			ignoreLevel++;
   594 		}
   595 
   596 		return SMCResult_Continue;
   597 	}
   598 
   599 	virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   600 	{
   601 		if (ignoreLevel || state != MSTATE_FILE)
   602 		{
   603 			return SMCResult_Continue;
   604 		}
   605 
   606 		if (strcmp(key, "engine") == 0)
   607 		{
   608 			had_engine = true;
   609 			if (DoesEngineMatch(value))
   610 			{
   611 				matched_engine = true;
   612 			}
   613 		}
   614 		else if (strcmp(key, "game") == 0)
   615 		{
   616 			had_game = true;
   617 			if (DoesGameMatch(value))
   618 			{
   619 				matched_game = true;
   620 			}
   621 		}
   622 
   623 		return SMCResult_Continue;
   624 	}
   625 
   626 	virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   627 	{
   628 		if (ignoreLevel)
   629 		{
   630 			ignoreLevel--;
   631 			return SMCResult_Continue;
   632 		}
   633 
   634 		if (state == MSTATE_FILE)
   635 		{
   636 			/* The four success conditions:
   637 			 * 1. Needed nothing.
   638 			 * 2. Needed game only.
   639 			 * 3. Needed engine only.
   640 			 * 4. Needed both engine and game.
   641 			 * Final result is minimized via k-map.
   642 			 */
   643 #if 0
   644 			if ((!had_engine && !had_game) ||
   645 				(!had_engine && (had_game && matched_game)) ||
   646 				(!had_game && (had_engine && matched_engine)) ||
   647 				((had_game && had_engine) && (matched_game && matched_engine)))
   648 #endif
   649 			if ((!had_engine && !had_game) ||
   650 				(!had_engine && matched_game) ||
   651 				(!had_game && matched_engine) ||
   652 				(matched_engine && matched_game))
   653 			{
   654 				fileList->push_back(cur_file);
   655 			}
   656 			state = MSTATE_MAIN;
   657 		}
   658 		else if (state == MSTATE_MAIN)
   659 		{
   660 			state = MSTATE_NONE;
   661 		}
   662 
   663 		return SMCResult_Continue;
   664 	}
   665 public:
   666 	list<char*> *fileList;
   667 	unsigned int state;
   668 	unsigned int ignoreLevel;
   669 	char cur_file[PLATFORM_MAX_PATH];
   670 	bool had_engine;
   671 	bool matched_engine;
   672 	bool had_game;
   673 	bool matched_game;
   674 };
   675 
   676 static MasterReader master_reader;
   677 
   678 bool CGameConfig::Reparse(char *error, size_t maxlength)
   679 {
   680 	/* Reset cached data */
   681 //	m_pStrings->Reset();
   682 	m_Offsets.clear();
   683 	m_Sigs.clear();
   684 	
   685 
   686 	char path[PLATFORM_MAX_PATH];
   687 
   688 	/* See if we can use the extended gamedata format. */
   689 	//TODO pass in path to gamedata somehow
   690 //	g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
   691 
   692 	/* Otherwise, it's time to parse the master. */
   693 	SMCError err;
   694 	SMCStates state = {0, 0};
   695 	list<char*> fileList;
   696 	master_reader.fileList = &fileList;
   697 
   698 	err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
   699 	if (err != SMCError_Okay)
   700 	{
   701 		const char *msg = textparsers->GetSMCErrorString(err);
   702 
   703 		printf("[SM] Error parsing master gameconf file \"%s\":", path);
   704 		printf("[SM] Error %d on line %d, col %d: %s", 
   705 			err,
   706 			state.line,
   707 			state.col,
   708 			msg ? msg : "Unknown error");
   709 		exit(PARSE_ERROR);
   710 	}
   711 
   712 	/* Go through each file we found and parse it. */
   713 	list<char*>::iterator iter;
   714 	for (iter = fileList.begin(); iter != fileList.end(); iter++)
   715 	{
   716 		UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter);
   717 		if (!EnterFile(path, error, maxlength))
   718 		{
   719 			return false;
   720 		}
   721 	}
   722 
   723 	return true;
   724 }
   725 
   726 bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
   727 {
   728 	SMCError err;
   729 	SMCStates state = {0, 0};
   730 
   731 	/* Initialize parse states */
   732 	m_IgnoreLevel = 0;
   733 	bShouldBeReadingDefault = true;
   734 	m_ParseState = PSTATE_NONE;
   735 
   736 	if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength))
   737 		!= SMCError_Okay)
   738 	{
   739 		const char *msg;
   740 
   741 		msg = textparsers->GetSMCErrorString(err);
   742 
   743 		printf("[SM] Error parsing gameconfig file \"%s\":\n", file);
   744 		printf("[SM] Error %d on line %d, col %d: %s\n", 
   745 			err,
   746 			state.line,
   747 			state.col,
   748 			msg ? msg : "Unknown error");
   749 
   750 		if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
   751 		{
   752 			//error occurred while parsing a custom section
   753 			m_CustomHandler->ReadSMC_ParseEnd(true, true);
   754 			m_CustomHandler = NULL;
   755 			m_CustomLevel = 0;
   756 		}
   757 
   758 		exit(PARSE_ERROR);
   759 	}
   760 
   761 	return true;
   762 }
   763 
   764 #if 0
   765 bool CGameConfig::GetOffset(const char *key, int *value)
   766 {
   767 	void *obj;
   768 
   769 	if (!sm_trie_retrieve(m_pOffsets, key, &obj))
   770 	{
   771 		return false;
   772 	}
   773 
   774 	*value = (int)obj;
   775 	
   776 	return true;
   777 }
   778 
   779 const char *CGameConfig::GetKeyValue(const char *key)
   780 {
   781 	void *obj;
   782 	if (!sm_trie_retrieve(m_pKeys, key, &obj))
   783 	{
   784 		return NULL;
   785 	}
   786 	return m_pStrings->GetString((int)obj);
   787 }
   788 
   789 SendProp *CGameConfig::GetSendProp(const char *key)
   790 {
   791 	SendProp *pProp;
   792 
   793 	if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
   794 	{
   795 		return NULL;
   796 	}
   797 
   798 	return pProp;
   799 }
   800 
   801 bool CGameConfig::GetMemSig(const char *key, void **addr)
   802 {
   803 	return sm_trie_retrieve(m_pSigs, key, addr);
   804 }
   805 
   806 void CGameConfig::IncRefCount()
   807 {
   808 	m_RefCount++;
   809 }
   810 
   811 unsigned int CGameConfig::DecRefCount()
   812 {
   813 	m_RefCount--;
   814 	return m_RefCount;
   815 }
   816 #endif
   817 
   818 const char *CGameConfig::GetKeyValue(const char *key)
   819 {
   820 	map<const char*,const char*,cmp_str>::iterator it = m_Keys.find(key);
   821 	if (it == m_Keys.end()) return NULL;
   822 
   823 	return it->second;
   824 }
   825 
   826 list<Offset> CGameConfig::GetOffsets() { return m_Offsets; }
   827 list<Sig> CGameConfig::GetSigs() { return m_Sigs; }
   828