|
dvander@547
|
1 |
/** |
|
dvander@547
|
2 |
* =============================================================== |
|
dvander@547
|
3 |
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. |
|
dvander@547
|
4 |
* =============================================================== |
|
dvander@547
|
5 |
* |
|
dvander@547
|
6 |
* This file is not open source and may not be copied without explicit |
|
dvander@547
|
7 |
* written permission of AlliedModders LLC. This file may not be redistributed |
|
dvander@547
|
8 |
* in whole or significant part. |
|
dvander@547
|
9 |
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php |
|
dvander@547
|
10 |
* |
|
dvander@547
|
11 |
* Version: $Id$ |
|
dvander@547
|
12 |
*/ |
|
dvander@547
|
13 |
|
|
dvander@547
|
14 |
#include <string.h> |
|
dvander@547
|
15 |
#include <stdlib.h> |
|
dvander@592
|
16 |
#include "GameConfigs.h" |
|
dvander@591
|
17 |
#include "TextParsers.h" |
|
dvander@547
|
18 |
#include "sm_stringutil.h" |
|
dvander@547
|
19 |
#include "sourcemod.h" |
|
dvander@547
|
20 |
#include "sourcemm_api.h" |
|
dvander@592
|
21 |
#include "HalfLife2.h" |
|
dvander@592
|
22 |
#include "Logger.h" |
|
dvander@547
|
23 |
#include "ShareSys.h" |
|
dvander@547
|
24 |
|
|
dvander@616
|
25 |
GameConfigManager g_GameConfigs; |
|
dvander@547
|
26 |
IGameConfig *g_pGameConf = NULL; |
|
dvander@547
|
27 |
char g_mod[255]; |
|
dvander@547
|
28 |
|
|
dvander@547
|
29 |
#define PSTATE_NONE 0 |
|
dvander@547
|
30 |
#define PSTATE_GAMES 1 |
|
dvander@547
|
31 |
#define PSTATE_GAMEDEFS 2 |
|
dvander@547
|
32 |
#define PSTATE_GAMEDEFS_OFFSETS 3 |
|
dvander@547
|
33 |
#define PSTATE_GAMEDEFS_OFFSETS_OFFSET 4 |
|
dvander@548
|
34 |
#define PSTATE_GAMEDEFS_KEYS 5 |
|
dvander@547
|
35 |
|
|
dvander@547
|
36 |
#if defined PLATFORM_WINDOWS |
|
dvander@547
|
37 |
#define PLATFORM_NAME "windows" |
|
dvander@547
|
38 |
#elif defined PLATFORM_LINUX |
|
dvander@547
|
39 |
#define PLATFORM_NAME "linux" |
|
dvander@547
|
40 |
#endif |
|
dvander@547
|
41 |
|
|
dvander@547
|
42 |
CGameConfig::CGameConfig(const char *file) |
|
dvander@547
|
43 |
{ |
|
dvander@547
|
44 |
m_pFile = sm_strdup(file); |
|
dvander@547
|
45 |
m_pOffsets = sm_trie_create(); |
|
dvander@547
|
46 |
m_pProps = sm_trie_create(); |
|
dvander@548
|
47 |
m_pKeys = sm_trie_create(); |
|
dvander@548
|
48 |
m_pStrings = new BaseStringTable(512); |
|
dvander@547
|
49 |
} |
|
dvander@547
|
50 |
|
|
dvander@547
|
51 |
CGameConfig::~CGameConfig() |
|
dvander@547
|
52 |
{ |
|
dvander@547
|
53 |
sm_trie_destroy(m_pOffsets); |
|
dvander@547
|
54 |
sm_trie_destroy(m_pProps); |
|
dvander@548
|
55 |
sm_trie_destroy(m_pKeys); |
|
dvander@547
|
56 |
delete [] m_pFile; |
|
dvander@548
|
57 |
delete m_pStrings; |
|
dvander@547
|
58 |
} |
|
dvander@547
|
59 |
|
|
dvander@547
|
60 |
SMCParseResult CGameConfig::ReadSMC_NewSection(const char *name, bool opt_quotes) |
|
dvander@547
|
61 |
{ |
|
dvander@547
|
62 |
if (m_IgnoreLevel) |
|
dvander@547
|
63 |
{ |
|
dvander@547
|
64 |
m_IgnoreLevel++; |
|
dvander@547
|
65 |
return SMCParse_Continue; |
|
dvander@547
|
66 |
} |
|
dvander@547
|
67 |
switch (m_ParseState) |
|
dvander@547
|
68 |
{ |
|
dvander@547
|
69 |
case PSTATE_NONE: |
|
dvander@547
|
70 |
{ |
|
dvander@547
|
71 |
if (strcmp(name, "Games") == 0) |
|
dvander@547
|
72 |
{ |
|
dvander@547
|
73 |
m_ParseState = PSTATE_GAMES; |
|
dvander@547
|
74 |
} else { |
|
dvander@547
|
75 |
m_IgnoreLevel++; |
|
dvander@547
|
76 |
} |
|
dvander@547
|
77 |
break; |
|
dvander@547
|
78 |
} |
|
dvander@547
|
79 |
case PSTATE_GAMES: |
|
dvander@547
|
80 |
{ |
|
dvander@547
|
81 |
if ((strcmp(name, "*") == 0) |
|
dvander@547
|
82 |
|| (strcmp(name, g_mod) == 0)) |
|
dvander@547
|
83 |
{ |
|
dvander@547
|
84 |
m_ParseState = PSTATE_GAMEDEFS; |
|
dvander@547
|
85 |
strncopy(m_mod, name, sizeof(m_mod)); |
|
dvander@547
|
86 |
} else { |
|
dvander@547
|
87 |
m_IgnoreLevel++; |
|
dvander@547
|
88 |
} |
|
dvander@547
|
89 |
break; |
|
dvander@547
|
90 |
} |
|
dvander@547
|
91 |
case PSTATE_GAMEDEFS: |
|
dvander@547
|
92 |
{ |
|
dvander@547
|
93 |
if (strcmp(name, "Offsets") == 0) |
|
dvander@547
|
94 |
{ |
|
dvander@547
|
95 |
m_ParseState = PSTATE_GAMEDEFS_OFFSETS; |
|
dvander@548
|
96 |
} else if (strcmp(name, "Keys") == 0) { |
|
dvander@548
|
97 |
m_ParseState = PSTATE_GAMEDEFS_KEYS; |
|
dvander@547
|
98 |
} else { |
|
dvander@547
|
99 |
m_IgnoreLevel++; |
|
dvander@547
|
100 |
} |
|
dvander@547
|
101 |
break; |
|
dvander@547
|
102 |
} |
|
dvander@547
|
103 |
case PSTATE_GAMEDEFS_OFFSETS: |
|
dvander@547
|
104 |
{ |
|
dvander@547
|
105 |
m_prop[0] = '\0'; |
|
dvander@547
|
106 |
m_class[0] = '\0'; |
|
dvander@547
|
107 |
strncopy(m_offset, name, sizeof(m_offset)); |
|
dvander@547
|
108 |
m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET; |
|
dvander@547
|
109 |
break; |
|
dvander@547
|
110 |
} |
|
dvander@547
|
111 |
/* No sub-sections allowed: |
|
dvander@547
|
112 |
case PSTATE_GAMEDEFS_OFFSETS_OFFSET: |
|
dvander@548
|
113 |
case PSTATE_GAMEDEFS_KEYS: |
|
dvander@547
|
114 |
*/ |
|
dvander@547
|
115 |
default: |
|
dvander@547
|
116 |
{ |
|
dvander@547
|
117 |
/* If we don't know what we got, start ignoring */ |
|
dvander@547
|
118 |
m_IgnoreLevel++; |
|
dvander@547
|
119 |
break; |
|
dvander@547
|
120 |
} |
|
dvander@547
|
121 |
} |
|
dvander@547
|
122 |
|
|
dvander@547
|
123 |
return SMCParse_Continue; |
|
dvander@547
|
124 |
} |
|
dvander@547
|
125 |
|
|
dvander@547
|
126 |
SMCParseResult CGameConfig::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes) |
|
dvander@547
|
127 |
{ |
|
dvander@547
|
128 |
if (m_IgnoreLevel) |
|
dvander@547
|
129 |
{ |
|
dvander@547
|
130 |
return SMCParse_Continue; |
|
dvander@547
|
131 |
} |
|
dvander@547
|
132 |
|
|
dvander@547
|
133 |
if (m_ParseState == PSTATE_GAMEDEFS_OFFSETS_OFFSET) |
|
dvander@547
|
134 |
{ |
|
dvander@547
|
135 |
if (strcmp(key, "class") == 0) |
|
dvander@547
|
136 |
{ |
|
dvander@547
|
137 |
strncopy(m_class, value, sizeof(m_class)); |
|
dvander@547
|
138 |
} else if (strcmp(key, "prop") == 0) { |
|
dvander@547
|
139 |
strncopy(m_prop, value, sizeof(m_prop)); |
|
dvander@547
|
140 |
} else if (strcmp(key, PLATFORM_NAME) == 0) { |
|
dvander@547
|
141 |
int val = atoi(value); |
|
dvander@547
|
142 |
sm_trie_insert(m_pOffsets, m_offset, (void *)val); |
|
dvander@547
|
143 |
} |
|
dvander@548
|
144 |
} else if (m_ParseState == PSTATE_GAMEDEFS_KEYS) { |
|
dvander@548
|
145 |
int id = m_pStrings->AddString(value); |
|
dvander@548
|
146 |
sm_trie_insert(m_pKeys, key, (void *)id); |
|
dvander@547
|
147 |
} |
|
dvander@547
|
148 |
|
|
dvander@547
|
149 |
return SMCParse_Continue; |
|
dvander@547
|
150 |
} |
|
dvander@547
|
151 |
|
|
dvander@547
|
152 |
SMCParseResult CGameConfig::ReadSMC_LeavingSection() |
|
dvander@547
|
153 |
{ |
|
dvander@547
|
154 |
if (m_IgnoreLevel) |
|
dvander@547
|
155 |
{ |
|
dvander@547
|
156 |
m_IgnoreLevel--; |
|
dvander@547
|
157 |
return SMCParse_Continue; |
|
dvander@547
|
158 |
} |
|
dvander@547
|
159 |
|
|
dvander@547
|
160 |
switch (m_ParseState) |
|
dvander@547
|
161 |
{ |
|
dvander@547
|
162 |
case PSTATE_GAMES: |
|
dvander@547
|
163 |
{ |
|
dvander@547
|
164 |
m_ParseState = PSTATE_NONE; |
|
dvander@547
|
165 |
break; |
|
dvander@547
|
166 |
} |
|
dvander@547
|
167 |
case PSTATE_GAMEDEFS: |
|
dvander@547
|
168 |
{ |
|
dvander@547
|
169 |
m_ParseState = PSTATE_GAMES; |
|
dvander@547
|
170 |
break; |
|
dvander@547
|
171 |
} |
|
dvander@548
|
172 |
case PSTATE_GAMEDEFS_KEYS: |
|
dvander@547
|
173 |
case PSTATE_GAMEDEFS_OFFSETS: |
|
dvander@547
|
174 |
{ |
|
dvander@547
|
175 |
m_ParseState = PSTATE_GAMEDEFS; |
|
dvander@547
|
176 |
break; |
|
dvander@547
|
177 |
} |
|
dvander@547
|
178 |
case PSTATE_GAMEDEFS_OFFSETS_OFFSET: |
|
dvander@547
|
179 |
{ |
|
dvander@547
|
180 |
/* Parse the offset... */ |
|
dvander@547
|
181 |
if (m_class[0] != '\0' |
|
dvander@547
|
182 |
&& m_prop[0] != '\0') |
|
dvander@547
|
183 |
{ |
|
dvander@547
|
184 |
SendProp *pProp = g_HL2.FindInSendTable(m_class, m_prop); |
|
dvander@547
|
185 |
if (pProp) |
|
dvander@547
|
186 |
{ |
|
dvander@547
|
187 |
int val = pProp->GetOffset(); |
|
dvander@547
|
188 |
sm_trie_insert(m_pOffsets, m_offset, (void *)val); |
|
dvander@547
|
189 |
sm_trie_insert(m_pProps, m_offset, pProp); |
|
dvander@547
|
190 |
} else { |
|
dvander@547
|
191 |
/* Check if it's a non-default game and no offsets exist */ |
|
dvander@547
|
192 |
if ((strcmp(m_mod, "*") != 0) |
|
dvander@547
|
193 |
&& (!sm_trie_retrieve(m_pOffsets, m_offset, NULL))) |
|
dvander@547
|
194 |
{ |
|
dvander@547
|
195 |
g_Logger.LogError("[SM] Unable to find property %s.%s (file \"%s\") (mod \"%s\")", |
|
dvander@547
|
196 |
m_class, |
|
dvander@547
|
197 |
m_prop, |
|
dvander@547
|
198 |
m_pFile, |
|
dvander@547
|
199 |
m_mod); |
|
dvander@547
|
200 |
} |
|
dvander@547
|
201 |
} |
|
dvander@547
|
202 |
} |
|
dvander@547
|
203 |
m_ParseState = PSTATE_GAMEDEFS_OFFSETS; |
|
dvander@547
|
204 |
break; |
|
dvander@547
|
205 |
} |
|
dvander@547
|
206 |
} |
|
dvander@547
|
207 |
|
|
dvander@547
|
208 |
return SMCParse_Continue; |
|
dvander@547
|
209 |
} |
|
dvander@547
|
210 |
|
|
dvander@547
|
211 |
bool CGameConfig::Reparse(char *error, size_t maxlength) |
|
dvander@547
|
212 |
{ |
|
dvander@547
|
213 |
SMCParseError err; |
|
dvander@547
|
214 |
|
|
dvander@547
|
215 |
char path[PLATFORM_MAX_PATH]; |
|
dvander@547
|
216 |
g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "configs/gamedata/%s.txt", m_pFile); |
|
dvander@547
|
217 |
|
|
dvander@548
|
218 |
/* Initialize parse states */ |
|
dvander@547
|
219 |
m_IgnoreLevel = 0; |
|
dvander@547
|
220 |
m_ParseState = PSTATE_NONE; |
|
dvander@548
|
221 |
/* Reset cached data */ |
|
dvander@548
|
222 |
m_pStrings->Reset(); |
|
dvander@547
|
223 |
sm_trie_clear(m_pOffsets); |
|
dvander@548
|
224 |
sm_trie_clear(m_pProps); |
|
dvander@548
|
225 |
sm_trie_clear(m_pKeys); |
|
dvander@547
|
226 |
|
|
dvander@547
|
227 |
if ((err=g_TextParser.ParseFile_SMC(path, this, NULL, NULL)) |
|
dvander@547
|
228 |
!= SMCParse_Okay) |
|
dvander@547
|
229 |
{ |
|
dvander@547
|
230 |
if (error && (err != SMCParse_Custom)) |
|
dvander@547
|
231 |
{ |
|
dvander@547
|
232 |
const char *str = g_TextParser.GetSMCErrorString(err); |
|
dvander@551
|
233 |
snprintf(error, maxlength, "%s", str); |
|
dvander@547
|
234 |
} |
|
dvander@547
|
235 |
return false; |
|
dvander@547
|
236 |
} |
|
dvander@547
|
237 |
|
|
dvander@547
|
238 |
return true; |
|
dvander@547
|
239 |
} |
|
dvander@547
|
240 |
|
|
dvander@547
|
241 |
bool CGameConfig::GetOffset(const char *key, int *value) |
|
dvander@547
|
242 |
{ |
|
dvander@547
|
243 |
void *obj; |
|
dvander@547
|
244 |
|
|
dvander@547
|
245 |
if (!sm_trie_retrieve(m_pOffsets, key, &obj)) |
|
dvander@547
|
246 |
{ |
|
dvander@547
|
247 |
return false; |
|
dvander@547
|
248 |
} |
|
dvander@547
|
249 |
|
|
dvander@547
|
250 |
*value = (int)obj; |
|
dvander@547
|
251 |
|
|
dvander@547
|
252 |
return true; |
|
dvander@547
|
253 |
} |
|
dvander@547
|
254 |
|
|
dvander@547
|
255 |
const char *CGameConfig::GetKeyValue(const char *key) |
|
dvander@547
|
256 |
{ |
|
dvander@548
|
257 |
void *obj; |
|
dvander@548
|
258 |
if (!sm_trie_retrieve(m_pKeys, key, &obj)) |
|
dvander@548
|
259 |
{ |
|
dvander@548
|
260 |
return NULL; |
|
dvander@548
|
261 |
} |
|
dvander@548
|
262 |
return m_pStrings->GetString((int)obj); |
|
dvander@547
|
263 |
} |
|
dvander@547
|
264 |
|
|
dvander@547
|
265 |
SendProp *CGameConfig::GetSendProp(const char *key) |
|
dvander@547
|
266 |
{ |
|
dvander@547
|
267 |
SendProp *pProp; |
|
dvander@547
|
268 |
|
|
dvander@547
|
269 |
if (!sm_trie_retrieve(m_pProps, key, (void **)&pProp)) |
|
dvander@547
|
270 |
{ |
|
dvander@547
|
271 |
return NULL; |
|
dvander@547
|
272 |
} |
|
dvander@547
|
273 |
|
|
dvander@547
|
274 |
return pProp; |
|
dvander@547
|
275 |
} |
|
dvander@547
|
276 |
|
|
dvander@547
|
277 |
void CGameConfig::IncRefCount() |
|
dvander@547
|
278 |
{ |
|
dvander@547
|
279 |
m_RefCount++; |
|
dvander@547
|
280 |
} |
|
dvander@547
|
281 |
|
|
dvander@547
|
282 |
unsigned int CGameConfig::DecRefCount() |
|
dvander@547
|
283 |
{ |
|
dvander@547
|
284 |
m_RefCount--; |
|
dvander@547
|
285 |
return m_RefCount; |
|
dvander@547
|
286 |
} |
|
dvander@547
|
287 |
|
|
dvander@616
|
288 |
GameConfigManager::GameConfigManager() |
|
dvander@547
|
289 |
{ |
|
dvander@547
|
290 |
m_pLookup = sm_trie_create(); |
|
dvander@547
|
291 |
} |
|
dvander@547
|
292 |
|
|
dvander@616
|
293 |
GameConfigManager::~GameConfigManager() |
|
dvander@547
|
294 |
{ |
|
dvander@547
|
295 |
sm_trie_destroy(m_pLookup); |
|
dvander@547
|
296 |
} |
|
dvander@547
|
297 |
|
|
dvander@616
|
298 |
void GameConfigManager::OnSourceModStartup(bool late) |
|
dvander@547
|
299 |
{ |
|
dvander@547
|
300 |
LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0); |
|
dvander@547
|
301 |
|
|
dvander@547
|
302 |
char mod[255]; |
|
dvander@547
|
303 |
engine->GetGameDir(mod, sizeof(mod)); |
|
dvander@547
|
304 |
|
|
dvander@547
|
305 |
g_mod[0] = '\0'; |
|
dvander@547
|
306 |
size_t len = strlen(mod); |
|
dvander@547
|
307 |
for (size_t i=len-1; i>=0 && i<len; i++) |
|
dvander@547
|
308 |
{ |
|
dvander@547
|
309 |
if (mod[i] == '/') |
|
dvander@547
|
310 |
{ |
|
dvander@547
|
311 |
if (i == len-1) |
|
dvander@547
|
312 |
{ |
|
dvander@547
|
313 |
mod[i] = '\0'; |
|
dvander@547
|
314 |
continue; |
|
dvander@547
|
315 |
} |
|
dvander@547
|
316 |
strcpy(g_mod, &mod[i]); |
|
dvander@547
|
317 |
break; |
|
dvander@547
|
318 |
} |
|
dvander@547
|
319 |
} |
|
dvander@547
|
320 |
if (g_mod[0] != '\0') |
|
dvander@547
|
321 |
{ |
|
dvander@547
|
322 |
strcpy(g_mod, mod); |
|
dvander@547
|
323 |
} |
|
dvander@547
|
324 |
} |
|
dvander@547
|
325 |
|
|
dvander@616
|
326 |
void GameConfigManager::OnSourceModAllInitialized() |
|
dvander@547
|
327 |
{ |
|
dvander@547
|
328 |
/* NOW initialize the game file */ |
|
dvander@547
|
329 |
CGameConfig *pGameConf = (CGameConfig *)g_pGameConf; |
|
dvander@547
|
330 |
|
|
dvander@547
|
331 |
char error[255]; |
|
dvander@547
|
332 |
if (!pGameConf->Reparse(error, sizeof(error))) |
|
dvander@547
|
333 |
{ |
|
dvander@547
|
334 |
/* :TODO: log */ |
|
dvander@547
|
335 |
} |
|
dvander@547
|
336 |
|
|
dvander@547
|
337 |
g_ShareSys.AddInterface(NULL, this); |
|
dvander@547
|
338 |
} |
|
dvander@547
|
339 |
|
|
dvander@616
|
340 |
void GameConfigManager::OnSourceModAllShutdown() |
|
dvander@547
|
341 |
{ |
|
dvander@547
|
342 |
CloseGameConfigFile(g_pGameConf); |
|
dvander@547
|
343 |
} |
|
dvander@547
|
344 |
|
|
dvander@616
|
345 |
bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength) |
|
dvander@547
|
346 |
{ |
|
dvander@547
|
347 |
CGameConfig *pConfig; |
|
dvander@547
|
348 |
|
|
dvander@547
|
349 |
if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig)) |
|
dvander@547
|
350 |
{ |
|
dvander@547
|
351 |
pConfig->IncRefCount(); |
|
dvander@547
|
352 |
*_pConfig = pConfig; |
|
dvander@547
|
353 |
return true; |
|
dvander@547
|
354 |
} |
|
dvander@547
|
355 |
|
|
dvander@547
|
356 |
pConfig = new CGameConfig(file); |
|
dvander@547
|
357 |
|
|
dvander@547
|
358 |
/* :HACKHACK: Don't parse the main config file */ |
|
dvander@547
|
359 |
bool retval = true; |
|
dvander@547
|
360 |
if (_pConfig != &g_pGameConf) |
|
dvander@547
|
361 |
{ |
|
dvander@547
|
362 |
retval = pConfig->Reparse(error, maxlength); |
|
dvander@547
|
363 |
} |
|
dvander@547
|
364 |
|
|
dvander@547
|
365 |
m_cfgs.push_back(pConfig); |
|
dvander@547
|
366 |
sm_trie_insert(m_pLookup, file, pConfig); |
|
dvander@547
|
367 |
|
|
dvander@547
|
368 |
pConfig->IncRefCount(); |
|
dvander@547
|
369 |
|
|
dvander@547
|
370 |
*_pConfig = pConfig; |
|
dvander@547
|
371 |
|
|
dvander@547
|
372 |
return retval; |
|
dvander@547
|
373 |
} |
|
dvander@547
|
374 |
|
|
dvander@616
|
375 |
void GameConfigManager::CloseGameConfigFile(IGameConfig *cfg) |
|
dvander@547
|
376 |
{ |
|
dvander@547
|
377 |
CGameConfig *pConfig = (CGameConfig *)cfg; |
|
dvander@547
|
378 |
|
|
dvander@547
|
379 |
if (pConfig->DecRefCount() == 0) |
|
dvander@547
|
380 |
{ |
|
dvander@547
|
381 |
delete pConfig; |
|
dvander@547
|
382 |
} |
|
dvander@547
|
383 |
} |