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