tools/gdc-psyfork/GameConfigs.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sat Jun 30 09:08:06 2012 -0400 (2012-06-30)
changeset 3568 f697142dc892
parent 3513 50b5b98b3400
child 3574 b51285ca203f
permissions -rw-r--r--
Updated GMod gamedata.
     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 
    36 #define PSTATE_NONE						0
    37 #define PSTATE_GAMES					1
    38 #define PSTATE_GAMEDEFS					2
    39 #define PSTATE_GAMEDEFS_OFFSETS			3
    40 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET	4
    41 #define PSTATE_GAMEDEFS_KEYS			5
    42 #define PSTATE_GAMEDEFS_SUPPORTED		6
    43 #define PSTATE_GAMEDEFS_SIGNATURES		7
    44 #define PSTATE_GAMEDEFS_SIGNATURES_SIG	8
    45 #define PSTATE_GAMEDEFS_CRC				9
    46 #define PSTATE_GAMEDEFS_CRC_BINARY		10
    47 #define PSTATE_GAMEDEFS_CUSTOM			11
    48 
    49 #define WIN 0
    50 #define LIN 1
    51 
    52 #define PLATFORM_NAME				"linux"
    53 #define PLATFORM_SERVER_BINARY		"server_i486.so"
    54 
    55 Offset *tempOffset;
    56 Sig *tempSig;
    57 
    58 unsigned int s_ServerBinCRC;
    59 bool s_ServerBinCRC_Ok = false;
    60 
    61 bool DoesGameMatch(const char *value)
    62 {
    63 	if (strcmp(value, game) == 0)
    64 	{
    65 		return true;
    66 	}
    67 	return false;
    68 }
    69 
    70 bool DoesEngineMatch(const char *value)
    71 {
    72 	if (strcmp(value, engine) == 0)
    73 	{
    74 		return true;
    75 	}
    76 	return false;
    77 }
    78 
    79 CGameConfig::CGameConfig()
    80 {
    81 	m_pStrings = new BaseStringTable(512);
    82 	m_RefCount = 0;
    83 
    84 	m_CustomLevel = 0;
    85 	m_CustomHandler = NULL;
    86 }
    87 
    88 CGameConfig::~CGameConfig()
    89 {
    90 	delete m_pStrings;
    91 }
    92 
    93 SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
    94 {
    95 	if (m_IgnoreLevel)
    96 	{
    97 		m_IgnoreLevel++;
    98 		return SMCResult_Continue;
    99 	}
   100 
   101 	switch (m_ParseState)
   102 	{
   103 	case PSTATE_NONE:
   104 		{
   105 			if (strcmp(name, "Games") == 0)
   106 			{
   107 				m_ParseState = PSTATE_GAMES;
   108 			} else {
   109 				m_IgnoreLevel++;
   110 			}
   111 			break;
   112 		}
   113 	case PSTATE_GAMES:
   114 		{
   115 			if (strcmp(name, "*") == 0 ||
   116 				strcmp(name, "#default") == 0 ||
   117 				DoesGameMatch(name))
   118 			{
   119 				bShouldBeReadingDefault = true;
   120 				m_ParseState = PSTATE_GAMEDEFS;
   121 				strncopy(m_Game, name, sizeof(m_Game));
   122 			} else {
   123 				m_IgnoreLevel++;
   124 			}
   125 			break;
   126 		}
   127 	case PSTATE_GAMEDEFS:
   128 		{
   129 			if (strcmp(name, "Offsets") == 0)
   130 			{
   131 				m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   132 			}
   133 			else if (strcmp(name, "Keys") == 0)
   134 			{
   135 				m_ParseState = PSTATE_GAMEDEFS_KEYS;
   136 			}
   137 			else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
   138 			{
   139 				m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
   140 				/* Ignore this section unless we get a game. */
   141 				bShouldBeReadingDefault = false;
   142 				had_game = false;
   143 				matched_game = false;
   144 				had_engine = false;
   145 				matched_engine = false;
   146 			}
   147 			else if (strcmp(name, "Signatures") == 0)
   148 			{
   149 				m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   150 			}
   151 
   152 			break;
   153 		}
   154 	case PSTATE_GAMEDEFS_OFFSETS:
   155 		{
   156 			m_Prop[0] = '\0';
   157 			m_Class[0] = '\0';
   158 			tempOffset = new Offset();
   159 			tempOffset->setName(name);
   160 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
   161 			break;
   162 		}
   163 	case PSTATE_GAMEDEFS_SIGNATURES:
   164 		{
   165 			tempSig = new Sig();
   166 			tempSig->setName(name);
   167 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
   168 			break;
   169 		}
   170 	/* No sub-sections allowed:
   171 	 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   172 	 case PSTATE_GAMEDEFS_KEYS:
   173 	 case PSTATE_GAMEDEFS_SUPPORTED:
   174 	 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   175 	 case PSTATE_GAMEDEFS_CRC_BINARY:
   176 	 */
   177 	default:
   178 		{
   179 			/* If we don't know what we got, start ignoring */
   180 			m_IgnoreLevel++;
   181 			break;
   182 		}
   183 	}
   184 
   185 	return SMCResult_Continue;
   186 }
   187 
   188 SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   189 {
   190 	if (m_IgnoreLevel)
   191 	{
   192 		return SMCResult_Continue;
   193 	}
   194 
   195 	if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
   196 	{
   197 		if (strcmp(key, "class") == 0)
   198 		{
   199 			strncopy(m_Class, value, sizeof(m_Class));
   200 		} else if (strcmp(key, "prop") == 0) {
   201 			strncopy(m_Prop, value, sizeof(m_Prop));
   202 		} else {
   203 			int val = atoi(value);
   204 			if (strcmp(key, "windows") == 0) tempOffset->setWin(val);
   205 			else if (strcmp(key, "linux") == 0) tempOffset->setLin(val);
   206 		}
   207 	} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
   208 		m_Keys.replace(key, m_pStrings->AddString(value));
   209 	} else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
   210 		if (strcmp(key, "game") == 0)
   211 		{
   212 			had_game = true;
   213 			if (DoesGameMatch(value))
   214 			{
   215 				matched_game = true;
   216 			}
   217 			if ((!had_engine && matched_game) || (matched_engine && matched_game))
   218 			{
   219 				bShouldBeReadingDefault = true;
   220 			}
   221 		}
   222 		else if (strcmp(key, "engine") == 0)
   223 		{
   224 			had_engine = true;
   225 			if (DoesEngineMatch(value))
   226 			{
   227 				matched_engine = true;
   228 			}
   229 			if ((!had_game && matched_engine) || (matched_game && matched_engine))
   230 			{
   231 				bShouldBeReadingDefault = true;
   232 			}
   233 		}
   234 	} else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
   235 		if (strcmp(key, "windows") == 0) tempSig->setWin(value);
   236 		else if (strcmp(key, "linux") == 0) tempSig->setLin(value);
   237 		else if (strcmp(key, "library") == 0) tempSig->setLib(value);
   238 	} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
   239 		if (strcmp(key, PLATFORM_NAME) == 0 
   240 			&& s_ServerBinCRC_Ok
   241 			&& !bShouldBeReadingDefault)
   242 		{
   243 			unsigned int crc = 0;
   244 			sscanf(value, "%08X", &crc);
   245 			if (s_ServerBinCRC == crc)
   246 			{
   247 				bShouldBeReadingDefault = true;
   248 			}
   249 		}
   250 	} else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
   251 		return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
   252 	}
   253 
   254 	return SMCResult_Continue;
   255 }
   256 
   257 SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
   258 {
   259 	if (m_IgnoreLevel)
   260 	{
   261 		m_IgnoreLevel--;
   262 		return SMCResult_Continue;
   263 	}
   264 
   265 	if (m_CustomLevel)
   266 	{
   267 		m_CustomLevel--;
   268 		m_CustomHandler->ReadSMC_LeavingSection(states);
   269 		return SMCResult_Continue;
   270 	}
   271 
   272 	switch (m_ParseState)
   273 	{
   274 	case PSTATE_GAMES:
   275 		{
   276 			m_ParseState = PSTATE_NONE;
   277 			break;
   278 		}
   279 	case PSTATE_GAMEDEFS:
   280 		{
   281 			m_ParseState = PSTATE_GAMES;
   282 			break;
   283 		}
   284 	case PSTATE_GAMEDEFS_CUSTOM:
   285 		{
   286 			m_ParseState = PSTATE_GAMEDEFS;
   287 			m_CustomHandler->ReadSMC_ParseEnd(false, false);
   288 			break;
   289 		}
   290 	case PSTATE_GAMEDEFS_KEYS:
   291 	case PSTATE_GAMEDEFS_OFFSETS:
   292 		{
   293 			m_ParseState = PSTATE_GAMEDEFS;
   294 			break;
   295 		}
   296 	case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
   297 		{
   298 			for (list<Offset>::iterator it = m_Offsets.begin(); it != m_Offsets.end(); it++)
   299 			{
   300 				if (strcmp(it->name, tempOffset->name) == 0)
   301 				{
   302 					m_Offsets.erase(it);
   303 					break;
   304 				}
   305 			}
   306 			m_Offsets.push_back(*tempOffset);
   307 			m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
   308 			break;
   309 		}
   310 	case PSTATE_GAMEDEFS_CRC:
   311 	case PSTATE_GAMEDEFS_SUPPORTED:
   312 		{
   313 			if (!bShouldBeReadingDefault)
   314 			{
   315 				/* If we shouldn't read the rest of this section, set the ignore level. */
   316 				m_IgnoreLevel = 1;
   317 				m_ParseState = PSTATE_GAMES;
   318 			} else {
   319 				m_ParseState = PSTATE_GAMEDEFS;
   320 			}
   321 			break;
   322 		}
   323 	case PSTATE_GAMEDEFS_CRC_BINARY:
   324 		{
   325 			m_ParseState = PSTATE_GAMEDEFS_CRC;
   326 			break;
   327 		}
   328 	case PSTATE_GAMEDEFS_SIGNATURES:
   329 		{
   330 			m_ParseState = PSTATE_GAMEDEFS;
   331 			break;
   332 		}
   333 	case PSTATE_GAMEDEFS_SIGNATURES_SIG:
   334 		{
   335 			m_Sigs.push_back(*tempSig);
   336 			m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
   337 
   338 			break;
   339 		}
   340 	}
   341 
   342 	return SMCResult_Continue;
   343 }
   344 
   345 #define MSTATE_NONE		0
   346 #define MSTATE_MAIN		1
   347 #define MSTATE_FILE		2
   348 
   349 class MasterReader : public ITextListener_SMC
   350 {
   351 public:
   352 	virtual void ReadSMC_ParseStart()
   353 	{
   354 		state = MSTATE_NONE;
   355 		ignoreLevel = 0;
   356 	}
   357 
   358 	virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
   359 	{
   360 		if (ignoreLevel)
   361 		{
   362 			return SMCResult_Continue;
   363 		}
   364 
   365 		if (state == MSTATE_NONE)
   366 		{
   367 			if (strcmp(name, "Game Master") == 0)
   368 			{
   369 				state = MSTATE_MAIN;
   370 			}
   371 			else
   372 			{
   373 				ignoreLevel++;
   374 			}
   375 		}
   376 		else if (state == MSTATE_MAIN)
   377 		{
   378 			strncopy(cur_file, name, sizeof(cur_file));
   379 			had_engine = false;
   380 			matched_engine = false;
   381 			had_game = false;
   382 			matched_game = false;
   383 			state = MSTATE_FILE;
   384 		}
   385 		else if (state == MSTATE_FILE)
   386 		{
   387 			ignoreLevel++;
   388 		}
   389 
   390 		return SMCResult_Continue;
   391 	}
   392 
   393 	virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   394 	{
   395 		if (ignoreLevel || state != MSTATE_FILE)
   396 		{
   397 			return SMCResult_Continue;
   398 		}
   399 
   400 		if (strcmp(key, "engine") == 0)
   401 		{
   402 			had_engine = true;
   403 			if (DoesEngineMatch(value))
   404 			{
   405 				matched_engine = true;
   406 			}
   407 		}
   408 		else if (strcmp(key, "game") == 0)
   409 		{
   410 			had_game = true;
   411 			if (DoesGameMatch(value))
   412 			{
   413 				matched_game = true;
   414 			}
   415 		}
   416 
   417 		return SMCResult_Continue;
   418 	}
   419 
   420 	virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
   421 	{
   422 		if (ignoreLevel)
   423 		{
   424 			ignoreLevel--;
   425 			return SMCResult_Continue;
   426 		}
   427 
   428 		if (state == MSTATE_FILE)
   429 		{
   430 			/* The four success conditions:
   431 			 * 1. Needed nothing.
   432 			 * 2. Needed game only.
   433 			 * 3. Needed engine only.
   434 			 * 4. Needed both engine and game.
   435 			 * Final result is minimized via k-map.
   436 			 */
   437 
   438 			if ((!had_engine && !had_game) ||
   439 				(!had_engine && matched_game) ||
   440 				(!had_game && matched_engine) ||
   441 				(matched_engine && matched_game))
   442 			{
   443 				fileList->push_back(cur_file);
   444 			}
   445 			state = MSTATE_MAIN;
   446 		}
   447 		else if (state == MSTATE_MAIN)
   448 		{
   449 			state = MSTATE_NONE;
   450 		}
   451 
   452 		return SMCResult_Continue;
   453 	}
   454 public:
   455 	list<char*> *fileList;
   456 	unsigned int state;
   457 	unsigned int ignoreLevel;
   458 	char cur_file[PLATFORM_MAX_PATH];
   459 	bool had_engine;
   460 	bool matched_engine;
   461 	bool had_game;
   462 	bool matched_game;
   463 };
   464 
   465 static MasterReader master_reader;
   466 
   467 bool CGameConfig::Reparse(char *error, size_t maxlength)
   468 {
   469 	/* Reset cached data */
   470 	m_pStrings->Reset();
   471 	m_Offsets.clear();
   472 	m_Sigs.clear();
   473 	m_Keys.clear();
   474 
   475 	char path[PLATFORM_MAX_PATH];
   476 
   477 	/* See if we can use the extended gamedata format. */
   478 	//TODO pass in path to gamedata somehow
   479 
   480 	/* Otherwise, it's time to parse the master. */
   481 	SMCError err;
   482 	SMCStates state = {0, 0};
   483 	list<char*> fileList;
   484 	master_reader.fileList = &fileList;
   485 
   486 	err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
   487 	if (err != SMCError_Okay)
   488 	{
   489 		const char *msg = textparsers->GetSMCErrorString(err);
   490 
   491 		printf("[SM] Error parsing master gameconf file \"%s\":", path);
   492 		printf("[SM] Error %d on line %d, col %d: %s", 
   493 			err,
   494 			state.line,
   495 			state.col,
   496 			msg ? msg : "Unknown error");
   497 		exit(PARSE_ERROR);
   498 	}
   499 
   500 	/* Go through each file we found and parse it. */
   501 	list<char*>::iterator iter;
   502 	for (iter = fileList.begin(); iter != fileList.end(); iter++)
   503 	{
   504 		UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter);
   505 		if (!EnterFile(path, error, maxlength))
   506 		{
   507 			return false;
   508 		}
   509 	}
   510 
   511 	return true;
   512 }
   513 
   514 bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
   515 {
   516 	SMCError err;
   517 	SMCStates state = {0, 0};
   518 
   519 	/* Initialize parse states */
   520 	m_IgnoreLevel = 0;
   521 	bShouldBeReadingDefault = true;
   522 	m_ParseState = PSTATE_NONE;
   523 
   524 	if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength))
   525 		!= SMCError_Okay)
   526 	{
   527 		const char *msg;
   528 
   529 		msg = textparsers->GetSMCErrorString(err);
   530 
   531 		printf("[SM] Error parsing gameconfig file \"%s\":\n", file);
   532 		printf("[SM] Error %d on line %d, col %d: %s\n", 
   533 			err,
   534 			state.line,
   535 			state.col,
   536 			msg ? msg : "Unknown error");
   537 
   538 		if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
   539 		{
   540 			//error occurred while parsing a custom section
   541 			m_CustomHandler->ReadSMC_ParseEnd(true, true);
   542 			m_CustomHandler = NULL;
   543 			m_CustomLevel = 0;
   544 		}
   545 
   546 		exit(PARSE_ERROR);
   547 	}
   548 
   549 	return true;
   550 }
   551 
   552 const char *CGameConfig::GetKeyValue(const char *key)
   553 {
   554 	int *pkey;
   555 	if ((pkey = m_Keys.retrieve(key)) == NULL)
   556 		return NULL;
   557 	return m_pStrings->GetString(*pkey);
   558 }
   559 
   560 list<Offset> CGameConfig::GetOffsets() { return m_Offsets; }
   561 list<Sig> CGameConfig::GetSigs() { return m_Sigs; }
   562