1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/GameConfigs.cpp Sat Mar 10 21:26:04 2007 +0000
1.3 @@ -0,0 +1,383 @@
1.4 +/**
1.5 + * ===============================================================
1.6 + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
1.7 + * ===============================================================
1.8 + *
1.9 + * This file is not open source and may not be copied without explicit
1.10 + * written permission of AlliedModders LLC. This file may not be redistributed
1.11 + * in whole or significant part.
1.12 + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
1.13 + *
1.14 + * Version: $Id$
1.15 + */
1.16 +
1.17 +#include <string.h>
1.18 +#include <stdlib.h>
1.19 +#include "GameConfigs.h"
1.20 +#include "TextParsers.h"
1.21 +#include "sm_stringutil.h"
1.22 +#include "sourcemod.h"
1.23 +#include "sourcemm_api.h"
1.24 +#include "HalfLife2.h"
1.25 +#include "Logger.h"
1.26 +#include "ShareSys.h"
1.27 +
1.28 +CGameConfigManager g_GameConfigs;
1.29 +IGameConfig *g_pGameConf = NULL;
1.30 +char g_mod[255];
1.31 +
1.32 +#define PSTATE_NONE 0
1.33 +#define PSTATE_GAMES 1
1.34 +#define PSTATE_GAMEDEFS 2
1.35 +#define PSTATE_GAMEDEFS_OFFSETS 3
1.36 +#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
1.37 +#define PSTATE_GAMEDEFS_KEYS 5
1.38 +
1.39 +#if defined PLATFORM_WINDOWS
1.40 +#define PLATFORM_NAME "windows"
1.41 +#elif defined PLATFORM_LINUX
1.42 +#define PLATFORM_NAME "linux"
1.43 +#endif
1.44 +
1.45 +CGameConfig::CGameConfig(const char *file)
1.46 +{
1.47 + m_pFile = sm_strdup(file);
1.48 + m_pOffsets = sm_trie_create();
1.49 + m_pProps = sm_trie_create();
1.50 + m_pKeys = sm_trie_create();
1.51 + m_pStrings = new BaseStringTable(512);
1.52 +}
1.53 +
1.54 +CGameConfig::~CGameConfig()
1.55 +{
1.56 + sm_trie_destroy(m_pOffsets);
1.57 + sm_trie_destroy(m_pProps);
1.58 + sm_trie_destroy(m_pKeys);
1.59 + delete [] m_pFile;
1.60 + delete m_pStrings;
1.61 +}
1.62 +
1.63 +SMCParseResult CGameConfig::ReadSMC_NewSection(const char *name, bool opt_quotes)
1.64 +{
1.65 + if (m_IgnoreLevel)
1.66 + {
1.67 + m_IgnoreLevel++;
1.68 + return SMCParse_Continue;
1.69 + }
1.70 + switch (m_ParseState)
1.71 + {
1.72 + case PSTATE_NONE:
1.73 + {
1.74 + if (strcmp(name, "Games") == 0)
1.75 + {
1.76 + m_ParseState = PSTATE_GAMES;
1.77 + } else {
1.78 + m_IgnoreLevel++;
1.79 + }
1.80 + break;
1.81 + }
1.82 + case PSTATE_GAMES:
1.83 + {
1.84 + if ((strcmp(name, "*") == 0)
1.85 + || (strcmp(name, g_mod) == 0))
1.86 + {
1.87 + m_ParseState = PSTATE_GAMEDEFS;
1.88 + strncopy(m_mod, name, sizeof(m_mod));
1.89 + } else {
1.90 + m_IgnoreLevel++;
1.91 + }
1.92 + break;
1.93 + }
1.94 + case PSTATE_GAMEDEFS:
1.95 + {
1.96 + if (strcmp(name, "Offsets") == 0)
1.97 + {
1.98 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
1.99 + } else if (strcmp(name, "Keys") == 0) {
1.100 + m_ParseState = PSTATE_GAMEDEFS_KEYS;
1.101 + } else {
1.102 + m_IgnoreLevel++;
1.103 + }
1.104 + break;
1.105 + }
1.106 + case PSTATE_GAMEDEFS_OFFSETS:
1.107 + {
1.108 + m_prop[0] = '\0';
1.109 + m_class[0] = '\0';
1.110 + strncopy(m_offset, name, sizeof(m_offset));
1.111 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
1.112 + break;
1.113 + }
1.114 + /* No sub-sections allowed:
1.115 + case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
1.116 + case PSTATE_GAMEDEFS_KEYS:
1.117 + */
1.118 + default:
1.119 + {
1.120 + /* If we don't know what we got, start ignoring */
1.121 + m_IgnoreLevel++;
1.122 + break;
1.123 + }
1.124 + }
1.125 +
1.126 + return SMCParse_Continue;
1.127 +}
1.128 +
1.129 +SMCParseResult CGameConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
1.130 +{
1.131 + if (m_IgnoreLevel)
1.132 + {
1.133 + return SMCParse_Continue;
1.134 + }
1.135 +
1.136 + if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
1.137 + {
1.138 + if (strcmp(key, "class") == 0)
1.139 + {
1.140 + strncopy(m_class, value, sizeof(m_class));
1.141 + } else if (strcmp(key, "prop") == 0) {
1.142 + strncopy(m_prop, value, sizeof(m_prop));
1.143 + } else if (strcmp(key, PLATFORM_NAME) == 0) {
1.144 + int val = atoi(value);
1.145 + sm_trie_insert(m_pOffsets, m_offset, (void *)val);
1.146 + }
1.147 + } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
1.148 + int id = m_pStrings->AddString(value);
1.149 + sm_trie_insert(m_pKeys, key, (void *)id);
1.150 + }
1.151 +
1.152 + return SMCParse_Continue;
1.153 +}
1.154 +
1.155 +SMCParseResult CGameConfig::ReadSMC_LeavingSection()
1.156 +{
1.157 + if (m_IgnoreLevel)
1.158 + {
1.159 + m_IgnoreLevel--;
1.160 + return SMCParse_Continue;
1.161 + }
1.162 +
1.163 + switch (m_ParseState)
1.164 + {
1.165 + case PSTATE_GAMES:
1.166 + {
1.167 + m_ParseState = PSTATE_NONE;
1.168 + break;
1.169 + }
1.170 + case PSTATE_GAMEDEFS:
1.171 + {
1.172 + m_ParseState = PSTATE_GAMES;
1.173 + break;
1.174 + }
1.175 + case PSTATE_GAMEDEFS_KEYS:
1.176 + case PSTATE_GAMEDEFS_OFFSETS:
1.177 + {
1.178 + m_ParseState = PSTATE_GAMEDEFS;
1.179 + break;
1.180 + }
1.181 + case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
1.182 + {
1.183 + /* Parse the offset... */
1.184 + if (m_class[0] != '\0'
1.185 + && m_prop[0] != '\0')
1.186 + {
1.187 + SendProp *pProp = g_HL2.FindInSendTable(m_class, m_prop);
1.188 + if (pProp)
1.189 + {
1.190 + int val = pProp->GetOffset();
1.191 + sm_trie_insert(m_pOffsets, m_offset, (void *)val);
1.192 + sm_trie_insert(m_pProps, m_offset, pProp);
1.193 + } else {
1.194 + /* Check if it's a non-default game and no offsets exist */
1.195 + if ((strcmp(m_mod, "*") != 0)
1.196 + && (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
1.197 + {
1.198 + g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
1.199 + m_class,
1.200 + m_prop,
1.201 + m_pFile,
1.202 + m_mod);
1.203 + }
1.204 + }
1.205 + }
1.206 + m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
1.207 + break;
1.208 + }
1.209 + }
1.210 +
1.211 + return SMCParse_Continue;
1.212 +}
1.213 +
1.214 +bool CGameConfig::Reparse(char *error, size_t maxlength)
1.215 +{
1.216 + SMCParseError err;
1.217 +
1.218 + char path[PLATFORM_MAX_PATH];
1.219 + g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile);
1.220 +
1.221 + /* Initialize parse states */
1.222 + m_IgnoreLevel = 0;
1.223 + m_ParseState = PSTATE_NONE;
1.224 + /* Reset cached data */
1.225 + m_pStrings->Reset();
1.226 + sm_trie_clear(m_pOffsets);
1.227 + sm_trie_clear(m_pProps);
1.228 + sm_trie_clear(m_pKeys);
1.229 +
1.230 + if ((err=g_TextParser.ParseFile_SMC(path, this, NULL, NULL))
1.231 + != SMCParse_Okay)
1.232 + {
1.233 + if (error && (err != SMCParse_Custom))
1.234 + {
1.235 + const char *str = g_TextParser.GetSMCErrorString(err);
1.236 + snprintf(error, maxlength, "%s", str);
1.237 + }
1.238 + return false;
1.239 + }
1.240 +
1.241 + return true;
1.242 +}
1.243 +
1.244 +bool CGameConfig::GetOffset(const char *key, int *value)
1.245 +{
1.246 + void *obj;
1.247 +
1.248 + if (!sm_trie_retrieve(m_pOffsets, key, &obj))
1.249 + {
1.250 + return false;
1.251 + }
1.252 +
1.253 + *value = (int)obj;
1.254 +
1.255 + return true;
1.256 +}
1.257 +
1.258 +const char *CGameConfig::GetKeyValue(const char *key)
1.259 +{
1.260 + void *obj;
1.261 + if (!sm_trie_retrieve(m_pKeys, key, &obj))
1.262 + {
1.263 + return NULL;
1.264 + }
1.265 + return m_pStrings->GetString((int)obj);
1.266 +}
1.267 +
1.268 +SendProp *CGameConfig::GetSendProp(const char *key)
1.269 +{
1.270 + SendProp *pProp;
1.271 +
1.272 + if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
1.273 + {
1.274 + return NULL;
1.275 + }
1.276 +
1.277 + return pProp;
1.278 +}
1.279 +
1.280 +void CGameConfig::IncRefCount()
1.281 +{
1.282 + m_RefCount++;
1.283 +}
1.284 +
1.285 +unsigned int CGameConfig::DecRefCount()
1.286 +{
1.287 + m_RefCount--;
1.288 + return m_RefCount;
1.289 +}
1.290 +
1.291 +CGameConfigManager::CGameConfigManager()
1.292 +{
1.293 + m_pLookup = sm_trie_create();
1.294 +}
1.295 +
1.296 +CGameConfigManager::~CGameConfigManager()
1.297 +{
1.298 + sm_trie_destroy(m_pLookup);
1.299 +}
1.300 +
1.301 +void CGameConfigManager::OnSourceModStartup(bool late)
1.302 +{
1.303 + LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
1.304 +
1.305 + char mod[255];
1.306 + engine->GetGameDir(mod, sizeof(mod));
1.307 +
1.308 + g_mod[0] = '\0';
1.309 + size_t len = strlen(mod);
1.310 + for (size_t i=len-1; i>=0 && i<len; i++)
1.311 + {
1.312 + if (mod[i] == '/')
1.313 + {
1.314 + if (i == len-1)
1.315 + {
1.316 + mod[i] = '\0';
1.317 + continue;
1.318 + }
1.319 + strcpy(g_mod, &mod[i]);
1.320 + break;
1.321 + }
1.322 + }
1.323 + if (g_mod[0] != '\0')
1.324 + {
1.325 + strcpy(g_mod, mod);
1.326 + }
1.327 +}
1.328 +
1.329 +void CGameConfigManager::OnSourceModAllInitialized()
1.330 +{
1.331 + /* NOW initialize the game file */
1.332 + CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
1.333 +
1.334 + char error[255];
1.335 + if (!pGameConf->Reparse(error, sizeof(error)))
1.336 + {
1.337 + /* :TODO: log */
1.338 + }
1.339 +
1.340 + g_ShareSys.AddInterface(NULL, this);
1.341 +}
1.342 +
1.343 +void CGameConfigManager::OnSourceModAllShutdown()
1.344 +{
1.345 + CloseGameConfigFile(g_pGameConf);
1.346 +}
1.347 +
1.348 +bool CGameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
1.349 +{
1.350 + CGameConfig *pConfig;
1.351 +
1.352 + if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
1.353 + {
1.354 + pConfig->IncRefCount();
1.355 + *_pConfig = pConfig;
1.356 + return true;
1.357 + }
1.358 +
1.359 + pConfig = new CGameConfig(file);
1.360 +
1.361 + /* :HACKHACK: Don't parse the main config file */
1.362 + bool retval = true;
1.363 + if (_pConfig != &g_pGameConf)
1.364 + {
1.365 + retval = pConfig->Reparse(error, maxlength);
1.366 + }
1.367 +
1.368 + m_cfgs.push_back(pConfig);
1.369 + sm_trie_insert(m_pLookup, file, pConfig);
1.370 +
1.371 + pConfig->IncRefCount();
1.372 +
1.373 + *_pConfig = pConfig;
1.374 +
1.375 + return retval;
1.376 +}
1.377 +
1.378 +void CGameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
1.379 +{
1.380 + CGameConfig *pConfig = (CGameConfig *)cfg;
1.381 +
1.382 + if (pConfig->DecRefCount() == 0)
1.383 + {
1.384 + delete pConfig;
1.385 + }
1.386 +}