extensions can now be built for multiple engines, and will autoload from auto.X.Y folders
3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
34 #include "GameConfigs.h"
35 #include "sm_stringutil.h"
36 #include "sourcemod.h"
37 #include "sourcemm_api.h"
38 #include "HalfLife2.h"
41 #include "MemoryUtils.h"
42 #include "LibrarySys.h"
43 #include "HandleSys.h"
46 #if defined PLATFORM_LINUX
50 GameConfigManager g_GameConfigs;
51 IGameConfig *g_pGameConf = NULL;
53 char g_GameDesc[256] = {'!', '\0'};
56 #define PSTATE_GAMES 1
57 #define PSTATE_GAMEDEFS 2
58 #define PSTATE_GAMEDEFS_OFFSETS 3
59 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
60 #define PSTATE_GAMEDEFS_KEYS 5
61 #define PSTATE_GAMEDEFS_SUPPORTED 6
62 #define PSTATE_GAMEDEFS_SIGNATURES 7
63 #define PSTATE_GAMEDEFS_SIGNATURES_SIG 8
64 #define PSTATE_GAMEDEFS_CRC 9
65 #define PSTATE_GAMEDEFS_CRC_BINARY 10
67 #if defined PLATFORM_WINDOWS
68 #define PLATFORM_NAME "windows"
69 #define PLATFORM_SERVER_BINARY "server.dll"
70 #elif defined PLATFORM_LINUX
71 #define PLATFORM_NAME "linux"
72 #define PLATFORM_SERVER_BINARY "server_i486.so"
85 unsigned int s_ServerBinCRC;
86 bool s_ServerBinCRC_Ok = false;
88 CGameConfig::CGameConfig(const char *file)
90 m_pFile = sm_strdup(file);
91 m_pOffsets = sm_trie_create();
92 m_pProps = sm_trie_create();
93 m_pKeys = sm_trie_create();
94 m_pSigs = sm_trie_create();
95 m_pStrings = new BaseStringTable(512);
99 CGameConfig::~CGameConfig()
101 sm_trie_destroy(m_pOffsets);
102 sm_trie_destroy(m_pProps);
103 sm_trie_destroy(m_pKeys);
104 sm_trie_destroy(m_pSigs);
109 SMCParseResult CGameConfig::ReadSMC_NewSection(const char *name, bool opt_quotes)
114 return SMCParse_Continue;
117 switch (m_ParseState)
121 if (strcmp(name, "Games") == 0)
123 m_ParseState = PSTATE_GAMES;
131 if ((strcmp(name, "*") == 0)
132 || (strcmp(name, "#default") == 0)
133 || (strcasecmp(name, g_Game) == 0)
134 || (strcasecmp(name, g_GameDesc) == 0))
136 bShouldBeReadingDefault = true;
137 m_ParseState = PSTATE_GAMEDEFS;
138 strncopy(m_Game, name, sizeof(m_Game));
144 case PSTATE_GAMEDEFS:
146 if (strcmp(name, "Offsets") == 0)
148 m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
150 else if (strcmp(name, "Keys") == 0)
152 m_ParseState = PSTATE_GAMEDEFS_KEYS;
154 else if ((strcmp(name, "#supported") == 0)
155 && (strcmp(m_Game, "#default") == 0))
157 m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
158 /* Ignore this section unless we get a game. */
159 bShouldBeReadingDefault = false;
161 else if (strcmp(name, "Signatures") == 0)
163 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
165 else if (strcmp(name, "CRC") == 0)
167 m_ParseState = PSTATE_GAMEDEFS_CRC;
168 bShouldBeReadingDefault = false;
176 case PSTATE_GAMEDEFS_OFFSETS:
180 strncopy(m_offset, name, sizeof(m_offset));
181 m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
184 case PSTATE_GAMEDEFS_SIGNATURES:
186 strncopy(m_offset, name, sizeof(m_offset));
188 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
191 case PSTATE_GAMEDEFS_CRC:
195 if (strcmp(name, "server") != 0)
197 UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name);
199 else if (!s_ServerBinCRC_Ok)
202 char path[PLATFORM_MAX_PATH];
204 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
205 if ((fp = fopen(path, "rb")) == NULL)
207 UTIL_Format(error, sizeof(error), "Could not open binary: %s", path);
212 fseek(fp, 0, SEEK_END);
214 fseek(fp, 0, SEEK_SET);
216 buffer = malloc(size);
217 fread(buffer, size, 1, fp);
218 s_ServerBinCRC = UTIL_CRC32(buffer, size);
220 s_ServerBinCRC_Ok = true;
223 if (error[0] != '\0')
226 g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_pFile);
227 g_Logger.LogError("[SM] %s", error);
229 m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
233 /* No sub-sections allowed:
234 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
235 case PSTATE_GAMEDEFS_KEYS:
236 case PSTATE_GAMEDEFS_SUPPORTED:
237 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
238 case PSTATE_GAMEDEFS_CRC_BINARY:
242 /* If we don't know what we got, start ignoring */
248 return SMCParse_Continue;
251 SMCParseResult CGameConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
255 return SMCParse_Continue;
258 if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
260 if (strcmp(key, "class") == 0)
262 strncopy(m_Class, value, sizeof(m_Class));
263 } else if (strcmp(key, "prop") == 0) {
264 strncopy(m_Prop, value, sizeof(m_Prop));
265 } else if (strcmp(key, PLATFORM_NAME) == 0) {
266 int val = atoi(value);
267 sm_trie_replace(m_pOffsets, m_offset, (void *)val);
269 } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
270 int id = m_pStrings->AddString(value);
271 sm_trie_replace(m_pKeys, key, (void *)id);
272 } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
273 if (strcmp(key, "game") == 0
274 && (strcasecmp(value, g_Game) == 0 || strcasecmp(value, g_GameDesc) == 0))
276 bShouldBeReadingDefault = true;
278 } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
279 if (strcmp(key, PLATFORM_NAME) == 0)
281 strncopy(s_TempSig.sig, value, sizeof(s_TempSig.sig));
282 } else if (strcmp(key, "library") == 0) {
283 strncopy(s_TempSig.library, value, sizeof(s_TempSig.library));
285 } else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
286 if (strcmp(key, PLATFORM_NAME) == 0
288 && !bShouldBeReadingDefault)
290 unsigned int crc = 0;
291 sscanf(value, "%08X", &crc);
292 if (s_ServerBinCRC == crc)
294 bShouldBeReadingDefault = true;
299 return SMCParse_Continue;
302 SMCParseResult CGameConfig::ReadSMC_LeavingSection()
307 return SMCParse_Continue;
310 switch (m_ParseState)
314 m_ParseState = PSTATE_NONE;
317 case PSTATE_GAMEDEFS:
319 m_ParseState = PSTATE_GAMES;
322 case PSTATE_GAMEDEFS_KEYS:
323 case PSTATE_GAMEDEFS_OFFSETS:
325 m_ParseState = PSTATE_GAMEDEFS;
328 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
330 /* Parse the offset... */
331 if (m_Class[0] != '\0'
332 && m_Prop[0] != '\0')
334 SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop);
337 int val = pProp->GetOffset();
338 sm_trie_replace(m_pOffsets, m_offset, (void *)val);
339 sm_trie_replace(m_pProps, m_offset, pProp);
341 /* Check if it's a non-default game and no offsets exist */
342 if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
343 && (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
345 g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
353 m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
356 case PSTATE_GAMEDEFS_CRC:
357 case PSTATE_GAMEDEFS_SUPPORTED:
359 if (!bShouldBeReadingDefault)
361 /* If we shouldn't read the rest of this section, set the ignore level. */
363 m_ParseState = PSTATE_GAMES;
365 m_ParseState = PSTATE_GAMEDEFS;
369 case PSTATE_GAMEDEFS_CRC_BINARY:
371 m_ParseState = PSTATE_GAMEDEFS_CRC;
374 case PSTATE_GAMEDEFS_SIGNATURES:
376 m_ParseState = PSTATE_GAMEDEFS;
379 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
381 if (s_TempSig.library[0] == '\0')
384 strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
386 void *addrInBase = NULL;
387 if (strcmp(s_TempSig.library, "server") == 0)
389 addrInBase = (void *)g_SMAPI->GetServerFactory(false);
390 } else if (strcmp(s_TempSig.library, "engine") == 0) {
391 addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
393 void *final_addr = NULL;
394 if (addrInBase == NULL)
396 g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")",
400 #if defined PLATFORM_LINUX
401 if (s_TempSig.sig[0] == '@')
404 /* GNU only: returns 0 on error, inconsistent! >:[ */
405 if (dladdr(addrInBase, &info) != 0)
407 void *handle = dlopen(info.dli_fname, RTLD_NOW);
410 final_addr = dlsym(handle, &s_TempSig.sig[1]);
413 g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
418 g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
428 /* First, preprocess the signature */
434 length = strlen(s_TempSig.sig);
436 for (size_t i=0; i<length; i++)
438 if (real_bytes >= sizeof(real_sig))
442 real_sig[real_bytes++] = s_TempSig.sig[i];
443 if (s_TempSig.sig[i] == '\\'
444 && s_TempSig.sig[i+1] == 'x')
450 /* Get the hex part */
453 s_byte[0] = s_TempSig.sig[i+2];
454 s_byte[1] = s_TempSig.sig[i+3];
456 /* Read it as an integer */
457 sscanf(s_byte, "%x", &r_byte);
459 real_sig[real_bytes-1] = r_byte;
467 final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes);
471 #if defined PLATFORM_LINUX
474 sm_trie_replace(m_pSigs, m_offset, final_addr);
475 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
481 return SMCParse_Continue;
484 bool CGameConfig::Reparse(char *error, size_t maxlength)
488 char path[PLATFORM_MAX_PATH];
489 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s.txt", m_pFile);
490 /* Backwards compatibility */
491 if (!g_LibSys.PathExists(path))
493 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile);
496 /* Initialize parse states */
498 bShouldBeReadingDefault = true;
499 m_ParseState = PSTATE_NONE;
500 /* Reset cached data */
502 sm_trie_clear(m_pOffsets);
503 sm_trie_clear(m_pProps);
504 sm_trie_clear(m_pKeys);
506 if ((err=textparsers->ParseFile_SMC(path, this, NULL, NULL))
509 if (error && (err != SMCParse_Custom))
511 const char *str = textparsers->GetSMCErrorString(err);
512 snprintf(error, maxlength, "%s", str);
520 bool CGameConfig::GetOffset(const char *key, int *value)
524 if (!sm_trie_retrieve(m_pOffsets, key, &obj))
534 const char *CGameConfig::GetKeyValue(const char *key)
537 if (!sm_trie_retrieve(m_pKeys, key, &obj))
541 return m_pStrings->GetString((int)obj);
544 SendProp *CGameConfig::GetSendProp(const char *key)
548 if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
556 bool CGameConfig::GetMemSig(const char *key, void **addr)
558 return sm_trie_retrieve(m_pSigs, key, addr);
561 void CGameConfig::IncRefCount()
566 unsigned int CGameConfig::DecRefCount()
572 GameConfigManager::GameConfigManager()
574 m_pLookup = sm_trie_create();
577 GameConfigManager::~GameConfigManager()
579 sm_trie_destroy(m_pLookup);
582 void GameConfigManager::OnSourceModStartup(bool late)
584 LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
586 strncopy(g_Game, g_SourceMod.GetGameFolderName(), sizeof(g_Game));
587 strncopy(g_GameDesc + 1, SERVER_CALL(GetGameDescription)(), sizeof(g_GameDesc) - 1);
590 void GameConfigManager::OnSourceModAllInitialized()
592 /* NOW initialize the game file */
593 CGameConfig *pGameConf = (CGameConfig *)g_pGameConf;
596 if (!pGameConf->Reparse(error, sizeof(error)))
601 g_ShareSys.AddInterface(NULL, this);
604 void GameConfigManager::OnSourceModAllShutdown()
606 CloseGameConfigFile(g_pGameConf);
609 bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
611 CGameConfig *pConfig;
613 if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
615 pConfig->IncRefCount();
620 pConfig = new CGameConfig(file);
622 /* :HACKHACK: Don't parse the main config file */
624 if (_pConfig != &g_pGameConf)
626 retval = pConfig->Reparse(error, maxlength);
629 m_cfgs.push_back(pConfig);
630 sm_trie_insert(m_pLookup, file, pConfig);
632 pConfig->IncRefCount();
639 void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg)
641 CGameConfig *pConfig = (CGameConfig *)cfg;
643 if (pConfig->DecRefCount() == 0)
645 sm_trie_delete(m_pLookup, pConfig->m_pFile);
650 extern HandleType_t g_GameConfigsType;
652 IGameConfig *GameConfigManager::ReadHandle(Handle_t hndl, IdentityToken_t *ident, HandleError *err)
654 HandleSecurity sec(ident, g_pCoreIdent);
655 IGameConfig *conf = NULL;
656 HandleError _err = g_HandleSys.ReadHandle(hndl, g_GameConfigsType, &sec, (void **)&conf);