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