3 * =============================================================================
5 * Copyright (C) 2004-2008 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"
40 //#include "ShareSys.h"
41 //#include "MemoryUtils.h"
42 //#include "LibrarySys.h"
43 //#include "HandleSys.h"
44 //#include "sm_crc32.h"
46 //#if defined PLATFORM_LINUX
51 #define PSTATE_GAMES 1
52 #define PSTATE_GAMEDEFS 2
53 #define PSTATE_GAMEDEFS_OFFSETS 3
54 #define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4
55 #define PSTATE_GAMEDEFS_KEYS 5
56 #define PSTATE_GAMEDEFS_SUPPORTED 6
57 #define PSTATE_GAMEDEFS_SIGNATURES 7
58 #define PSTATE_GAMEDEFS_SIGNATURES_SIG 8
59 #define PSTATE_GAMEDEFS_CRC 9
60 #define PSTATE_GAMEDEFS_CRC_BINARY 10
61 #define PSTATE_GAMEDEFS_CUSTOM 11
66 #define PLATFORM_NAME "linux"
67 #define PLATFORM_SERVER_BINARY "server_i486.so"
72 unsigned int s_ServerBinCRC;
73 bool s_ServerBinCRC_Ok = false;
75 bool /*CGameConfig::*/DoesGameMatch(const char *value)
77 if (strcmp(value, /*m_gdcG*/game) == 0)
84 bool /*CGameConfig::*/DoesEngineMatch(const char *value)
86 if (strcmp(value, /*m_gdcE*/engine) == 0)
93 CGameConfig::CGameConfig()//const char *file)//, const char *game, const char *engine)
95 // strncopy(m_File, file, sizeof(m_File));
96 // strncopy(m_gdcGame, game, sizeof(m_gdcGame));
97 // strncopy(m_gdcEngine, engine, sizeof(m_gdcEngine));
98 //m_pStrings = new BaseStringTable(512);
102 m_CustomHandler = NULL;
105 CGameConfig::~CGameConfig()
107 // delete m_pStrings;
110 SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *name)
115 return SMCResult_Continue;
118 switch (m_ParseState)
122 if (strcmp(name, "Games") == 0)
124 m_ParseState = PSTATE_GAMES;
132 if (strcmp(name, "*") == 0 ||
133 strcmp(name, "#default") == 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) && (strcmp(m_Game, "#default") == 0))
156 m_ParseState = PSTATE_GAMEDEFS_SUPPORTED;
157 /* Ignore this section unless we get a game. */
158 bShouldBeReadingDefault = false;
160 matched_game = false;
162 matched_engine = false;
164 else if (strcmp(name, "Signatures") == 0)
166 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
168 else if (strcmp(name, "CRC") == 0)
171 m_ParseState = PSTATE_GAMEDEFS_CRC;
172 bShouldBeReadingDefault = false;
178 ITextListener_SMC **pListen = g_GameConfigs.m_customHandlers.retrieve(name);
183 m_ParseState = PSTATE_GAMEDEFS_CUSTOM;
184 m_CustomHandler = *pListen;
185 m_CustomHandler->ReadSMC_ParseStart();
192 case PSTATE_GAMEDEFS_OFFSETS:
196 tempOffset = new Offset();
197 tempOffset->setName(name);
198 m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET;
201 case PSTATE_GAMEDEFS_SIGNATURES:
204 tempSig->setName(name);
205 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES_SIG;
209 case PSTATE_GAMEDEFS_CRC:
213 if (strcmp(name, "server") != 0)
215 UTIL_Format(error, sizeof(error), "Unrecognized library \"%s\"", name);
217 else if (!s_ServerBinCRC_Ok)
220 char path[PLATFORM_MAX_PATH];
222 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "bin/" PLATFORM_SERVER_BINARY);
223 if ((fp = fopen(path, "rb")) == NULL)
225 UTIL_Format(error, sizeof(error), "Could not open binary: %s", path);
230 fseek(fp, 0, SEEK_END);
232 fseek(fp, 0, SEEK_SET);
234 buffer = malloc(size);
235 fread(buffer, size, 1, fp);
236 s_ServerBinCRC = UTIL_CRC32(buffer, size);
238 s_ServerBinCRC_Ok = true;
242 if (error[0] != '\0')
245 g_Logger.LogError("[SM] Error while parsing CRC section for \"%s\" (%s):", m_Game, m_CurFile);
246 g_Logger.LogError("[SM] %s", error);
248 m_ParseState = PSTATE_GAMEDEFS_CRC_BINARY;
252 case PSTATE_GAMEDEFS_CUSTOM:
255 return m_CustomHandler->ReadSMC_NewSection(states, name);
259 /* No sub-sections allowed:
260 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
261 case PSTATE_GAMEDEFS_KEYS:
262 case PSTATE_GAMEDEFS_SUPPORTED:
263 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
264 case PSTATE_GAMEDEFS_CRC_BINARY:
268 /* If we don't know what we got, start ignoring */
274 return SMCResult_Continue;
277 SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
281 return SMCResult_Continue;
284 if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET)
286 if (strcmp(key, "class") == 0)
288 strncopy(m_Class, value, sizeof(m_Class));
289 } else if (strcmp(key, "prop") == 0) {
290 strncopy(m_Prop, value, sizeof(m_Prop));
291 } else /*if (strcmp(key, PLATFORM_NAME) == 0) */ {
292 int val = atoi(value);
293 // sm_trie_replace(m_pOffsets, m_offset, (void *)val);
294 if (strcmp(key, "windows") == 0) tempOffset->setWin(val);
295 else if (strcmp(key, "linux") == 0) tempOffset->setLin(val);
297 } else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) {
299 // printf("Inserting %s - %s\n", key, value);
300 // int id = m_pStrings->AddString(value);
301 // sm_trie_replace(m_pKeys, key, (void *)id);
302 } else if (m_ParseState == PSTATE_GAMEDEFS_SUPPORTED) {
303 if (strcmp(key, "game") == 0)
306 if (DoesGameMatch(value))
310 if ((!had_engine && matched_game) || (matched_engine && matched_game))
312 bShouldBeReadingDefault = true;
315 else if (strcmp(key, "engine") == 0)
318 if (DoesEngineMatch(value))
320 matched_engine = true;
322 if ((!had_game && matched_engine) || (matched_game && matched_engine))
324 bShouldBeReadingDefault = true;
327 } else if (m_ParseState == PSTATE_GAMEDEFS_SIGNATURES_SIG) {
328 if (strcmp(key, "windows") == 0) tempSig->setWin(value);
329 else if (strcmp(key, "linux") == 0) tempSig->setLin(value);
330 else if (strcmp(key, "library") == 0) tempSig->setLib(value);
331 } else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
332 if (strcmp(key, PLATFORM_NAME) == 0
334 && !bShouldBeReadingDefault)
336 unsigned int crc = 0;
337 sscanf(value, "%08X", &crc);
338 if (s_ServerBinCRC == crc)
340 bShouldBeReadingDefault = true;
343 } else if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM) {
344 return m_CustomHandler->ReadSMC_KeyValue(states, key, value);
347 return SMCResult_Continue;
350 SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
355 return SMCResult_Continue;
361 m_CustomHandler->ReadSMC_LeavingSection(states);
362 return SMCResult_Continue;
365 switch (m_ParseState)
369 m_ParseState = PSTATE_NONE;
372 case PSTATE_GAMEDEFS:
374 m_ParseState = PSTATE_GAMES;
377 case PSTATE_GAMEDEFS_CUSTOM:
379 m_ParseState = PSTATE_GAMEDEFS;
380 m_CustomHandler->ReadSMC_ParseEnd(false, false);
383 case PSTATE_GAMEDEFS_KEYS:
384 case PSTATE_GAMEDEFS_OFFSETS:
386 m_ParseState = PSTATE_GAMEDEFS;
389 case PSTATE_GAMEDEFS_OFFSETS_OFFSET:
391 m_Offsets.push_back(*tempOffset);
393 /* Parse the offset... */
394 if (m_Class[0] != '\0'
395 && m_Prop[0] != '\0')
397 SendProp *pProp = g_HL2.FindInSendTable(m_Class, m_Prop);
400 int val = pProp->GetOffset();
401 sm_trie_replace(m_pOffsets, m_offset, (void *)val);
402 sm_trie_replace(m_pProps, m_offset, pProp);
404 /* Check if it's a non-default game and no offsets exist */
405 if (((strcmp(m_Game, "*") != 0) && strcmp(m_Game, "#default") != 0)
406 && (!sm_trie_retrieve(m_pOffsets, m_offset, NULL)))
408 g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")",
417 m_ParseState = PSTATE_GAMEDEFS_OFFSETS;
420 case PSTATE_GAMEDEFS_CRC:
421 case PSTATE_GAMEDEFS_SUPPORTED:
423 if (!bShouldBeReadingDefault)
425 /* If we shouldn't read the rest of this section, set the ignore level. */
427 m_ParseState = PSTATE_GAMES;
429 m_ParseState = PSTATE_GAMEDEFS;
433 case PSTATE_GAMEDEFS_CRC_BINARY:
435 m_ParseState = PSTATE_GAMEDEFS_CRC;
438 case PSTATE_GAMEDEFS_SIGNATURES:
440 m_ParseState = PSTATE_GAMEDEFS;
443 case PSTATE_GAMEDEFS_SIGNATURES_SIG:
445 m_Sigs.push_back(*tempSig);
447 if (s_TempSig.library[0] == '\0')
450 strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
452 void *addrInBase = NULL;
453 if (strcmp(s_TempSig.library, "server") == 0)
455 addrInBase = (void *)g_SMAPI->GetServerFactory(false);
456 } else if (strcmp(s_TempSig.library, "engine") == 0) {
457 addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
459 void *final_addr = NULL;
460 if (addrInBase == NULL)
462 g_Logger.LogError("[SM] Unrecognized library \"%s\" (gameconf \"%s\")",
466 #if defined PLATFORM_LINUX
467 if (s_TempSig.sig[0] == '@')
470 /* GNU only: returns 0 on error, inconsistent! >:[ */
471 if (dladdr(addrInBase, &info) != 0)
473 void *handle = dlopen(info.dli_fname, RTLD_NOW);
476 final_addr = dlsym(handle, &s_TempSig.sig[1]);
479 g_Logger.LogError("[SM] Unable to load library \"%s\" (gameconf \"%s\")",
484 g_Logger.LogError("[SM] Unable to find library \"%s\" in memory (gameconf \"%s\")",
494 /* First, preprocess the signature */
500 length = strlen(s_TempSig.sig);
502 for (size_t i=0; i<length; i++)
504 if (real_bytes >= sizeof(real_sig))
508 real_sig[real_bytes++] = s_TempSig.sig[i];
509 if (s_TempSig.sig[i] == '\\'
510 && s_TempSig.sig[i+1] == 'x')
516 /* Get the hex part */
519 s_byte[0] = s_TempSig.sig[i+2];
520 s_byte[1] = s_TempSig.sig[i+3];
522 /* Read it as an integer */
523 sscanf(s_byte, "%x", &r_byte);
525 real_sig[real_bytes-1] = r_byte;
533 final_addr = g_MemUtils.FindPattern(addrInBase, real_sig, real_bytes);
537 #if defined PLATFORM_LINUX
540 sm_trie_replace(m_pSigs, m_offset, final_addr);
542 m_ParseState = PSTATE_GAMEDEFS_SIGNATURES;
548 return SMCResult_Continue;
551 #define MSTATE_NONE 0
552 #define MSTATE_MAIN 1
553 #define MSTATE_FILE 2
555 class MasterReader : public ITextListener_SMC
558 virtual void ReadSMC_ParseStart()
564 virtual SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
568 return SMCResult_Continue;
571 if (state == MSTATE_NONE)
573 if (strcmp(name, "Game Master") == 0)
582 else if (state == MSTATE_MAIN)
584 strncopy(cur_file, name, sizeof(cur_file));
586 matched_engine = false;
588 matched_game = false;
591 else if (state == MSTATE_FILE)
596 return SMCResult_Continue;
599 virtual SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
601 if (ignoreLevel || state != MSTATE_FILE)
603 return SMCResult_Continue;
606 if (strcmp(key, "engine") == 0)
609 if (DoesEngineMatch(value))
611 matched_engine = true;
614 else if (strcmp(key, "game") == 0)
617 if (DoesGameMatch(value))
623 return SMCResult_Continue;
626 virtual SMCResult ReadSMC_LeavingSection(const SMCStates *states)
631 return SMCResult_Continue;
634 if (state == MSTATE_FILE)
636 /* The four success conditions:
638 * 2. Needed game only.
639 * 3. Needed engine only.
640 * 4. Needed both engine and game.
641 * Final result is minimized via k-map.
644 if ((!had_engine && !had_game) ||
645 (!had_engine && (had_game && matched_game)) ||
646 (!had_game && (had_engine && matched_engine)) ||
647 ((had_game && had_engine) && (matched_game && matched_engine)))
649 if ((!had_engine && !had_game) ||
650 (!had_engine && matched_game) ||
651 (!had_game && matched_engine) ||
652 (matched_engine && matched_game))
654 fileList->push_back(cur_file);
658 else if (state == MSTATE_MAIN)
663 return SMCResult_Continue;
666 list<char*> *fileList;
668 unsigned int ignoreLevel;
669 char cur_file[PLATFORM_MAX_PATH];
676 static MasterReader master_reader;
678 bool CGameConfig::Reparse(char *error, size_t maxlength)
680 /* Reset cached data */
681 // m_pStrings->Reset();
686 char path[PLATFORM_MAX_PATH];
688 /* See if we can use the extended gamedata format. */
689 //TODO pass in path to gamedata somehow
690 // g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "gamedata/%s/master.games.txt", m_File);
692 /* Otherwise, it's time to parse the master. */
694 SMCStates state = {0, 0};
695 list<char*> fileList;
696 master_reader.fileList = &fileList;
698 err = textparsers->ParseSMCFile(path, &master_reader, &state, error, maxlength);
699 if (err != SMCError_Okay)
701 const char *msg = textparsers->GetSMCErrorString(err);
703 printf("[SM] Error parsing master gameconf file \"%s\":", path);
704 printf("[SM] Error %d on line %d, col %d: %s",
708 msg ? msg : "Unknown error");
712 /* Go through each file we found and parse it. */
713 list<char*>::iterator iter;
714 for (iter = fileList.begin(); iter != fileList.end(); iter++)
716 UTIL_Format(path, sizeof(path), "%s/%s", m_File, *iter);
717 if (!EnterFile(path, error, maxlength))
726 bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength)
729 SMCStates state = {0, 0};
731 /* Initialize parse states */
733 bShouldBeReadingDefault = true;
734 m_ParseState = PSTATE_NONE;
736 if ((err=textparsers->ParseSMCFile(file, this, &state, error, maxlength))
741 msg = textparsers->GetSMCErrorString(err);
743 printf("[SM] Error parsing gameconfig file \"%s\":\n", file);
744 printf("[SM] Error %d on line %d, col %d: %s\n",
748 msg ? msg : "Unknown error");
750 if (m_ParseState == PSTATE_GAMEDEFS_CUSTOM)
752 //error occurred while parsing a custom section
753 m_CustomHandler->ReadSMC_ParseEnd(true, true);
754 m_CustomHandler = NULL;
765 bool CGameConfig::GetOffset(const char *key, int *value)
769 if (!sm_trie_retrieve(m_pOffsets, key, &obj))
779 const char *CGameConfig::GetKeyValue(const char *key)
782 if (!sm_trie_retrieve(m_pKeys, key, &obj))
786 return m_pStrings->GetString((int)obj);
789 SendProp *CGameConfig::GetSendProp(const char *key)
793 if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp))
801 bool CGameConfig::GetMemSig(const char *key, void **addr)
803 return sm_trie_retrieve(m_pSigs, key, addr);
806 void CGameConfig::IncRefCount()
811 unsigned int CGameConfig::DecRefCount()
818 const char *CGameConfig::GetKeyValue(const char *key)
820 map<const char*,const char*,cmp_str>::iterator it = m_Keys.find(key);
821 if (it == m_Keys.end()) return NULL;
826 list<Offset> CGameConfig::GetOffsets() { return m_Offsets; }
827 list<Sig> CGameConfig::GetSigs() { return m_Sigs; }