1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/tools/gdc-psyfork/GameConfigs.cpp Sun May 27 11:36:49 2012 -0400
1.3 @@ -0,0 +1,562 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include <string.h>
1.36 +#include <stdlib.h>
1.37 +#include "GameConfigs.h"
1.38 +
1.39 +#define PSTATE_NONE 0
1.40 +#define PSTATE_GAMES 1
1.41 +#define PSTATE_GAMEDEFS 2
1.42 +#define PSTATE_GAMEDEFS_OFFSETS 3
1.43 +#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
1.44 +#define PSTATE_GAMEDEFS_KEYS 5
1.45 +#define PSTATE_GAMEDEFS_SUPPORTED 6
1.46 +#define PSTATE_GAMEDEFS_SIGNATURES 7
1.47 +#define PSTATE_GAMEDEFS_SIGNATURES_SIG 8
1.48 +#define PSTATE_GAMEDEFS_CRC 9
1.49 +#define PSTATE_GAMEDEFS_CRC_BINARY 10
1.50 +#define PSTATE_GAMEDEFS_CUSTOM 11
1.51 +
1.52 +#define WIN 0
1.53 +#define LIN 1
1.54 +
1.55 +#define PLATFORM_NAME "linux"
1.56 +#define PLATFORM_SERVER_BINARY "server_i486.so"
1.57 +
1.58 +Offset *tempOffset;
1.59 +Sig *tempSig;
1.60 +
1.61 +unsigned int s_ServerBinCRC;
1.62 +bool s_ServerBinCRC_Ok = false;
1.63 +
1.64 +bool DoesGameMatch(const char *value)
1.65 +{
1.66 + if (strcmp(value, game) == 0)
1.67 + {
1.68 + return true;
1.69 + }
1.70 + return false;
1.71 +}
1.72 +
1.73 +bool DoesEngineMatch(const char *value)
1.74 +{
1.75 + if (strcmp(value, engine) == 0)
1.76 + {
1.77 + return true;
1.78 + }
1.79 + return false;
1.80 +}
1.81 +
1.82 +CGameConfig::CGameConfig()
1.83 +{
1.84 + m_pStrings = new BaseStringTable(512);
1.85 + m_RefCount = 0;
1.86 +
1.87 + m_CustomLevel = 0;
1.88 + m_CustomHandler = NULL;
1.89 +}
1.90 +
1.91 +CGameConfig::~CGameConfig()
1.92 +{
1.93 + delete m_pStrings;
1.94 +}
1.95 +
1.96 +SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
1.97 +{
1.98 + if (m_IgnoreLevel)
1.99 + {
1.100 + m_IgnoreLevel++;
1.101 + return SMCResult_Continue;
1.102 + }
1.103 +
1.104 + switch (m_ParseState)
1.105 + {
1.106 + case PSTATE_NONE:
1.107 + {
1.108 + if (strcmp(name, "Games") == 0)
1.109 + {
1.110 + m_ParseState = PSTATE_GAMES;
1.111 + } else {
1.112 + m_IgnoreLevel++;
1.113 + }
1.114 + break;
1.115 + }
1.116 + case PSTATE_GAMES:
1.117 + {
1.118 + if (strcmp(name, "*") == 0 ||
1.119 + strcmp(name, "#default") == 0 ||
1.120 + DoesGameMatch(name))
1.121 + {
1.122 + bShouldBeReadingDefault = true;
1.123 + m_ParseState = PSTATE_GAMEDEFS;
1.124 + strncopy(m_Game, name, sizeof(m_Game));
1.125 + } else {
1.126 + m_IgnoreLevel++;
1.127 + }
1.128 + break;
1.129 + }
1.130 + case PSTATE_GAMEDEFS:
1.131 + {
1.132 + if (strcmp(name, "Offsets") == 0)
1.133 + {
1.134 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
1.135 + }
1.136 + else if (strcmp(name, "Keys") == 0)
1.137 + {
1.138 + m_ParseState = PSTATE_GAMEDEFS_KEYS;
1.139 + }
1.140 + else if ((strcmp(name, "#supported") == 0) && (strcmp(m_Game, "#default") == 0))
1.141 + {
1.142 + m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
1.143 + /* Ignore this section unless we get a game. */
1.144 + bShouldBeReadingDefault = false;
1.145 + had_game = false;
1.146 + matched_game = false;
1.147 + had_engine = false;
1.148 + matched_engine = false;
1.149 + }
1.150 + else if (strcmp(name, "Signatures") == 0)
1.151 + {
1.152 + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
1.153 + }
1.154 +
1.155 + break;
1.156 + }
1.157 + case PSTATE_GAMEDEFS_OFFSETS:
1.158 + {
1.159 + m_Prop[0] = '\0';
1.160 + m_Class[0] = '\0';
1.161 + tempOffset = new Offset();
1.162 + tempOffset->setName(name);
1.163 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
1.164 + break;
1.165 + }
1.166 + case PSTATE_GAMEDEFS_SIGNATURES:
1.167 + {
1.168 + tempSig = new Sig();
1.169 + tempSig->setName(name);
1.170 + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
1.171 + break;
1.172 + }
1.173 + /* No sub-sections allowed:
1.174 + case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
1.175 + case PSTATE_GAMEDEFS_KEYS:
1.176 + case PSTATE_GAMEDEFS_SUPPORTED:
1.177 + case PSTATE_GAMEDEFS_SIGNATURES_SIG:
1.178 + case PSTATE_GAMEDEFS_CRC_BINARY:
1.179 + */
1.180 + default:
1.181 + {
1.182 + /* If we don't know what we got, start ignoring */
1.183 + m_IgnoreLevel++;
1.184 + break;
1.185 + }
1.186 + }
1.187 +
1.188 + return SMCResult_Continue;
1.189 +}
1.190 +
1.191 +SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.192 +{
1.193 + if (m_IgnoreLevel)
1.194 + {
1.195 + return SMCResult_Continue;
1.196 + }
1.197 +
1.198 + if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
1.199 + {
1.200 + if (strcmp(key, "class") == 0)
1.201 + {
1.202 + strncopy(m_Class, value, sizeof(m_Class));
1.203 + } else if (strcmp(key, "prop") == 0) {
1.204 + strncopy(m_Prop, value, sizeof(m_Prop));
1.205 + } else {
1.206 + int val = atoi(value);
1.207 + if (strcmp(key, "windows") == 0) tempOffset->setWin(val);
1.208 + else if (strcmp(key, "linux") == 0) tempOffset->setLin(val);
1.209 + }
1.210 + } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
1.211 + m_Keys.replace(key, m_pStrings->AddString(value));
1.212 + } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
1.213 + if (strcmp(key, "game") == 0)
1.214 + {
1.215 + had_game = true;
1.216 + if (DoesGameMatch(value))
1.217 + {
1.218 + matched_game = true;
1.219 + }
1.220 + if ((!had_engine && matched_game) || (matched_engine && matched_game))
1.221 + {
1.222 + bShouldBeReadingDefault = true;
1.223 + }
1.224 + }
1.225 + else if (strcmp(key, "engine") == 0)
1.226 + {
1.227 + had_engine = true;
1.228 + if (DoesEngineMatch(value))
1.229 + {
1.230 + matched_engine = true;
1.231 + }
1.232 + if ((!had_game && matched_engine) || (matched_game && matched_engine))
1.233 + {
1.234 + bShouldBeReadingDefault = true;
1.235 + }
1.236 + }
1.237 + } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
1.238 + if (strcmp(key, "windows") == 0) tempSig->setWin(value);
1.239 + else if (strcmp(key, "linux") == 0) tempSig->setLin(value);
1.240 + else if (strcmp(key, "library") == 0) tempSig->setLib(value);
1.241 + } else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
1.242 + if (strcmp(key, PLATFORM_NAME) == 0
1.243 + && s_ServerBinCRC_Ok
1.244 + && !bShouldBeReadingDefault)
1.245 + {
1.246 + unsigned int crc = 0;
1.247 + sscanf(value, "%08X", &crc);
1.248 + if (s_ServerBinCRC == crc)
1.249 + {
1.250 + bShouldBeReadingDefault = true;
1.251 + }
1.252 + }
1.253 + } else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
1.254 + return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
1.255 + }
1.256 +
1.257 + return SMCResult_Continue;
1.258 +}
1.259 +
1.260 +SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
1.261 +{
1.262 + if (m_IgnoreLevel)
1.263 + {
1.264 + m_IgnoreLevel--;
1.265 + return SMCResult_Continue;
1.266 + }
1.267 +
1.268 + if (m_CustomLevel)
1.269 + {
1.270 + m_CustomLevel--;
1.271 + m_CustomHandler->ReadSMC_LeavingSection(states);
1.272 + return SMCResult_Continue;
1.273 + }
1.274 +
1.275 + switch (m_ParseState)
1.276 + {
1.277 + case PSTATE_GAMES:
1.278 + {
1.279 + m_ParseState = PSTATE_NONE;
1.280 + break;
1.281 + }
1.282 + case PSTATE_GAMEDEFS:
1.283 + {
1.284 + m_ParseState = PSTATE_GAMES;
1.285 + break;
1.286 + }
1.287 + case PSTATE_GAMEDEFS_CUSTOM:
1.288 + {
1.289 + m_ParseState = PSTATE_GAMEDEFS;
1.290 + m_CustomHandler->ReadSMC_ParseEnd(false, false);
1.291 + break;
1.292 + }
1.293 + case PSTATE_GAMEDEFS_KEYS:
1.294 + case PSTATE_GAMEDEFS_OFFSETS:
1.295 + {
1.296 + m_ParseState = PSTATE_GAMEDEFS;
1.297 + break;
1.298 + }
1.299 + case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
1.300 + {
1.301 + for (list<Offset>::iterator it = m_Offsets.begin(); it != m_Offsets.end(); it++)
1.302 + {
1.303 + if (strcmp(it->name, tempOffset->name) == 0)
1.304 + {
1.305 + m_Offsets.erase(it);
1.306 + break;
1.307 + }
1.308 + }
1.309 + m_Offsets.push_back(*tempOffset);
1.310 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
1.311 + break;
1.312 + }
1.313 + case PSTATE_GAMEDEFS_CRC:
1.314 + case PSTATE_GAMEDEFS_SUPPORTED:
1.315 + {
1.316 + if (!bShouldBeReadingDefault)
1.317 + {
1.318 + /* If we shouldn't read the rest of this section, set the ignore level. */
1.319 + m_IgnoreLevel = 1;
1.320 + m_ParseState = PSTATE_GAMES;
1.321 + } else {
1.322 + m_ParseState = PSTATE_GAMEDEFS;
1.323 + }
1.324 + break;
1.325 + }
1.326 + case PSTATE_GAMEDEFS_CRC_BINARY:
1.327 + {
1.328 + m_ParseState = PSTATE_GAMEDEFS_CRC;
1.329 + break;
1.330 + }
1.331 + case PSTATE_GAMEDEFS_SIGNATURES:
1.332 + {
1.333 + m_ParseState = PSTATE_GAMEDEFS;
1.334 + break;
1.335 + }
1.336 + case PSTATE_GAMEDEFS_SIGNATURES_SIG:
1.337 + {
1.338 + m_Sigs.push_back(*tempSig);
1.339 + m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
1.340 +
1.341 + break;
1.342 + }
1.343 + }
1.344 +
1.345 + return SMCResult_Continue;
1.346 +}
1.347 +
1.348 +#define MSTATE_NONE 0
1.349 +#define MSTATE_MAIN 1
1.350 +#define MSTATE_FILE 2
1.351 +
1.352 +class MasterReader : public ITextListener_SMC
1.353 +{
1.354 +public:
1.355 + virtual void ReadSMC_ParseStart()
1.356 + {
1.357 + state = MSTATE_NONE;
1.358 + ignoreLevel = 0;
1.359 + }
1.360 +
1.361 + virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
1.362 + {
1.363 + if (ignoreLevel)
1.364 + {
1.365 + return SMCResult_Continue;
1.366 + }
1.367 +
1.368 + if (state == MSTATE_NONE)
1.369 + {
1.370 + if (strcmp(name, "Game Master") == 0)
1.371 + {
1.372 + state = MSTATE_MAIN;
1.373 + }
1.374 + else
1.375 + {
1.376 + ignoreLevel++;
1.377 + }
1.378 + }
1.379 + else if (state == MSTATE_MAIN)
1.380 + {
1.381 + strncopy(cur_file, name, sizeof(cur_file));
1.382 + had_engine = false;
1.383 + matched_engine = false;
1.384 + had_game = false;
1.385 + matched_game = false;
1.386 + state = MSTATE_FILE;
1.387 + }
1.388 + else if (state == MSTATE_FILE)
1.389 + {
1.390 + ignoreLevel++;
1.391 + }
1.392 +
1.393 + return SMCResult_Continue;
1.394 + }
1.395 +
1.396 + virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
1.397 + {
1.398 + if (ignoreLevel || state != MSTATE_FILE)
1.399 + {
1.400 + return SMCResult_Continue;
1.401 + }
1.402 +
1.403 + if (strcmp(key, "engine") == 0)
1.404 + {
1.405 + had_engine = true;
1.406 + if (DoesEngineMatch(value))
1.407 + {
1.408 + matched_engine = true;
1.409 + }
1.410 + }
1.411 + else if (strcmp(key, "game") == 0)
1.412 + {
1.413 + had_game = true;
1.414 + if (DoesGameMatch(value))
1.415 + {
1.416 + matched_game = true;
1.417 + }
1.418 + }
1.419 +
1.420 + return SMCResult_Continue;
1.421 + }
1.422 +
1.423 + virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
1.424 + {
1.425 + if (ignoreLevel)
1.426 + {
1.427 + ignoreLevel--;
1.428 + return SMCResult_Continue;
1.429 + }
1.430 +
1.431 + if (state == MSTATE_FILE)
1.432 + {
1.433 + /* The four success conditions:
1.434 + * 1. Needed nothing.
1.435 + * 2. Needed game only.
1.436 + * 3. Needed engine only.
1.437 + * 4. Needed both engine and game.
1.438 + * Final result is minimized via k-map.
1.439 + */
1.440 +
1.441 + if ((!had_engine && !had_game) ||
1.442 + (!had_engine && matched_game) ||
1.443 + (!had_game && matched_engine) ||
1.444 + (matched_engine && matched_game))
1.445 + {
1.446 + fileList->push_back(cur_file);
1.447 + }
1.448 + state = MSTATE_MAIN;
1.449 + }
1.450 + else if (state == MSTATE_MAIN)
1.451 + {
1.452 + state = MSTATE_NONE;
1.453 + }
1.454 +
1.455 + return SMCResult_Continue;
1.456 + }
1.457 +public:
1.458 + list<char*> *fileList;
1.459 + unsigned int state;
1.460 + unsigned int ignoreLevel;
1.461 + char cur_file[PLATFORM_MAX_PATH];
1.462 + bool had_engine;
1.463 + bool matched_engine;
1.464 + bool had_game;
1.465 + bool matched_game;
1.466 +};
1.467 +
1.468 +static MasterReader master_reader;
1.469 +
1.470 +bool CGameConfig::Reparse(char *error, size_t maxlength)
1.471 +{
1.472 + /* Reset cached data */
1.473 + m_pStrings->Reset();
1.474 + m_Offsets.clear();
1.475 + m_Sigs.clear();
1.476 + m_Keys.clear();
1.477 +
1.478 + char path[PLATFORM_MAX_PATH];
1.479 +
1.480 + /* See if we can use the extended gamedata format. */
1.481 + //TODO pass in path to gamedata somehow
1.482 +
1.483 + /* Otherwise, it's time to parse the master. */
1.484 + SMCError err;
1.485 + SMCStates state = {0, 0};
1.486 + list<char*> fileList;
1.487 + master_reader.fileList = &fileList;
1.488 +
1.489 + err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
1.490 + if (err != SMCError_Okay)
1.491 + {
1.492 + const char *msg = textparsers->GetSMCErrorString(err);
1.493 +
1.494 + printf("[SM] Error parsing master gameconf file \"%s\":", path);
1.495 + printf("[SM] Error %d on line %d, col %d: %s",
1.496 + err,
1.497 + state.line,
1.498 + state.col,
1.499 + msg ? msg : "Unknown error");
1.500 + exit(PARSE_ERROR);
1.501 + }
1.502 +
1.503 + /* Go through each file we found and parse it. */
1.504 + list<char*>::iterator iter;
1.505 + for (iter = fileList.begin(); iter != fileList.end(); iter++)
1.506 + {
1.507 + UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter);
1.508 + if (!EnterFile(path, error, maxlength))
1.509 + {
1.510 + return false;
1.511 + }
1.512 + }
1.513 +
1.514 + return true;
1.515 +}
1.516 +
1.517 +bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
1.518 +{
1.519 + SMCError err;
1.520 + SMCStates state = {0, 0};
1.521 +
1.522 + /* Initialize parse states */
1.523 + m_IgnoreLevel = 0;
1.524 + bShouldBeReadingDefault = true;
1.525 + m_ParseState = PSTATE_NONE;
1.526 +
1.527 + if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength))
1.528 + != SMCError_Okay)
1.529 + {
1.530 + const char *msg;
1.531 +
1.532 + msg = textparsers->GetSMCErrorString(err);
1.533 +
1.534 + printf("[SM] Error parsing gameconfig file \"%s\":\n", file);
1.535 + printf("[SM] Error %d on line %d, col %d: %s\n",
1.536 + err,
1.537 + state.line,
1.538 + state.col,
1.539 + msg ? msg : "Unknown error");
1.540 +
1.541 + if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
1.542 + {
1.543 + //error occurred while parsing a custom section
1.544 + m_CustomHandler->ReadSMC_ParseEnd(true, true);
1.545 + m_CustomHandler = NULL;
1.546 + m_CustomLevel = 0;
1.547 + }
1.548 +
1.549 + exit(PARSE_ERROR);
1.550 + }
1.551 +
1.552 + return true;
1.553 +}
1.554 +
1.555 +const char *CGameConfig::GetKeyValue(const char *key)
1.556 +{
1.557 + int *pkey;
1.558 + if ((pkey = m_Keys.retrieve(key)) == NULL)
1.559 + return NULL;
1.560 + return m_pStrings->GetString(*pkey);
1.561 +}
1.562 +
1.563 +list<Offset> CGameConfig::GetOffsets() { return m_Offsets; }
1.564 +list<Sig> CGameConfig::GetSigs() { return m_Sigs; }
1.565 +