|
ds@1952
|
1 |
/** |
|
ds@1952
|
2 |
* vim: set ts=4 : |
|
ds@1952
|
3 |
* ============================================================================= |
|
ds@1952
|
4 |
* SourceMod |
|
ds@1952
|
5 |
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. |
|
ds@1952
|
6 |
* ============================================================================= |
|
ds@1952
|
7 |
* |
|
ds@1952
|
8 |
* This program is free software; you can redistribute it and/or modify it under |
|
ds@1952
|
9 |
* the terms of the GNU General Public License, version 3.0, as published by the |
|
ds@1952
|
10 |
* Free Software Foundation. |
|
ds@1952
|
11 |
* |
|
ds@1952
|
12 |
* This program is distributed in the hope that it will be useful, but WITHOUT |
|
ds@1952
|
13 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
ds@1952
|
14 |
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
|
ds@1952
|
15 |
* details. |
|
ds@1952
|
16 |
* |
|
ds@1952
|
17 |
* You should have received a copy of the GNU General Public License along with |
|
ds@1952
|
18 |
* this program. If not, see <http://www.gnu.org/licenses/>. |
|
ds@1952
|
19 |
* |
|
ds@1952
|
20 |
* As a special exception, AlliedModders LLC gives you permission to link the |
|
ds@1952
|
21 |
* code of this program (as well as its derivative works) to "Half-Life 2," the |
|
ds@1952
|
22 |
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software |
|
ds@1952
|
23 |
* by the Valve Corporation. You must obey the GNU General Public License in |
|
ds@1952
|
24 |
* all respects for all other code used. Additionally, AlliedModders LLC grants |
|
ds@1952
|
25 |
* this exception to all derivative works. AlliedModders LLC defines further |
|
ds@1952
|
26 |
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), |
|
ds@1952
|
27 |
* or <http://www.sourcemod.net/license.php>. |
|
ds@1952
|
28 |
* |
|
ds@1952
|
29 |
* Version: $Id$ |
|
ds@1952
|
30 |
*/ |
|
ds@1952
|
31 |
|
|
ds@1952
|
32 |
#include <sh_list.h> |
|
ds@1952
|
33 |
#include "sm_globals.h" |
|
ds@1952
|
34 |
#include "sm_trie_tpl.h" |
|
ds@1952
|
35 |
#include "CellArray.h" |
|
ds@1952
|
36 |
#include "convar.h" |
|
ds@1952
|
37 |
#include "sourcemm_api.h" |
|
ds@1952
|
38 |
#include "LibrarySys.h" |
|
ds@1952
|
39 |
#include "TextParsers.h" |
|
ds@1952
|
40 |
#include "sm_stringutil.h" |
|
ds@1952
|
41 |
#include "sourcemod.h" |
|
ds@1952
|
42 |
#include "Logger.h" |
|
ds@1952
|
43 |
#include "HandleSys.h" |
|
ds@1952
|
44 |
|
|
ds@1952
|
45 |
using namespace SourceHook; |
|
ds@1952
|
46 |
|
|
ds@1952
|
47 |
struct maplist_info_t |
|
ds@1952
|
48 |
{ |
|
ds@1952
|
49 |
bool bIsCompat; |
|
ds@1952
|
50 |
bool bIsPath; |
|
ds@1952
|
51 |
char name[PLATFORM_MAX_PATH]; |
|
ds@1952
|
52 |
char path[PLATFORM_MAX_PATH]; |
|
ds@1952
|
53 |
time_t last_modified_time; |
|
ds@1952
|
54 |
CellArray *pArray; |
|
ds@1952
|
55 |
int serial; |
|
ds@1952
|
56 |
}; |
|
ds@1952
|
57 |
|
|
ds@1952
|
58 |
#define MAPLIST_FLAG_MAPSFOLDER (1<<0) /**< On failure, use all maps in the maps folder. */ |
|
ds@1952
|
59 |
#define MAPLIST_FLAG_CLEARARRAY (1<<1) /**< If an input array is specified, clear it before adding. */ |
|
ds@1952
|
60 |
#define MAPLIST_FLAG_NO_DEFAULT (1<<2) /**< Do not read "default" or "mapcyclefile" on failure. */ |
|
ds@1952
|
61 |
|
|
ds@1952
|
62 |
class MapLists : public SMGlobalClass, public ITextListener_SMC |
|
ds@1952
|
63 |
{ |
|
ds@1952
|
64 |
public: |
|
ds@1952
|
65 |
enum MapListState |
|
ds@1952
|
66 |
{ |
|
ds@1952
|
67 |
MPS_NONE, |
|
ds@1952
|
68 |
MPS_GLOBAL, |
|
ds@1952
|
69 |
MPS_MAPLIST, |
|
ds@1952
|
70 |
}; |
|
ds@1952
|
71 |
public: |
|
ds@1952
|
72 |
MapLists() |
|
ds@1952
|
73 |
{ |
|
ds@1952
|
74 |
m_pMapCycleFile = NULL; |
|
ds@1952
|
75 |
m_ConfigLastChanged = 0; |
|
ds@1952
|
76 |
m_nSerialChange = 0; |
|
ds@1952
|
77 |
} |
|
ds@1952
|
78 |
void OnSourceModAllInitialized() |
|
ds@1952
|
79 |
{ |
|
ds@1952
|
80 |
g_SourceMod.BuildPath(Path_SM, m_ConfigFile, sizeof(m_ConfigFile), "configs/maplists.cfg"); |
|
ds@1952
|
81 |
} |
|
ds@1952
|
82 |
void OnSourceModShutdown() |
|
ds@1952
|
83 |
{ |
|
ds@1952
|
84 |
DumpCache(NULL); |
|
ds@1952
|
85 |
} |
|
ds@1952
|
86 |
void AddOrUpdateDefault(const char *name, const char *file) |
|
ds@1952
|
87 |
{ |
|
ds@1952
|
88 |
char path[PLATFORM_MAX_PATH]; |
|
ds@1952
|
89 |
maplist_info_t *pMapList, **ppMapList; |
|
ds@1952
|
90 |
|
|
ds@1952
|
91 |
if ((ppMapList = m_ListLookup.retrieve(name)) == NULL) |
|
ds@1952
|
92 |
{ |
|
ds@1952
|
93 |
pMapList = new maplist_info_t; |
|
ds@1952
|
94 |
pMapList->bIsCompat = true; |
|
ds@1952
|
95 |
pMapList->bIsPath = true; |
|
ds@1952
|
96 |
pMapList->last_modified_time = 0; |
|
ds@1952
|
97 |
strncopy(pMapList->name, name, sizeof(pMapList->name)); |
|
ds@1952
|
98 |
pMapList->pArray = NULL; |
|
ds@1952
|
99 |
g_SourceMod.BuildPath(Path_Game, |
|
ds@1952
|
100 |
pMapList->path, |
|
ds@1952
|
101 |
sizeof(pMapList->path), |
|
ds@1952
|
102 |
"%s", |
|
ds@1952
|
103 |
file); |
|
ds@1952
|
104 |
pMapList->serial = 0; |
|
ds@1952
|
105 |
m_ListLookup.insert(name, pMapList); |
|
ds@1952
|
106 |
m_MapLists.push_back(pMapList); |
|
ds@1952
|
107 |
return; |
|
ds@1952
|
108 |
} |
|
ds@1952
|
109 |
|
|
ds@1952
|
110 |
pMapList = *ppMapList; |
|
ds@1952
|
111 |
|
|
ds@1952
|
112 |
/* Don't modify if it's from the config file */ |
|
ds@1952
|
113 |
if (!pMapList->bIsCompat) |
|
ds@1952
|
114 |
{ |
|
ds@1952
|
115 |
return; |
|
ds@1952
|
116 |
} |
|
ds@1952
|
117 |
|
|
ds@1952
|
118 |
g_SourceMod.BuildPath(Path_Game, |
|
ds@1952
|
119 |
path, |
|
ds@1952
|
120 |
sizeof(path), |
|
ds@1952
|
121 |
"%s", |
|
ds@1952
|
122 |
file); |
|
ds@1952
|
123 |
|
|
ds@1952
|
124 |
/* If the path matches, don't reset the serial/time */ |
|
ds@1952
|
125 |
if (strcmp(path, pMapList->path) == 0) |
|
ds@1952
|
126 |
{ |
|
ds@1952
|
127 |
return; |
|
ds@1952
|
128 |
} |
|
ds@1952
|
129 |
|
|
ds@1952
|
130 |
strncopy(pMapList->path, path, sizeof(pMapList->path)); |
|
ds@1952
|
131 |
pMapList->bIsPath = true; |
|
ds@1952
|
132 |
pMapList->last_modified_time = 0; |
|
ds@1952
|
133 |
pMapList->serial = 0; |
|
ds@1952
|
134 |
} |
|
ds@1952
|
135 |
void UpdateCache() |
|
ds@1952
|
136 |
{ |
|
ds@1952
|
137 |
bool fileFound; |
|
ds@1952
|
138 |
SMCError error; |
|
ds@1952
|
139 |
time_t fileTime; |
|
ds@1952
|
140 |
SMCStates states = {0, 0}; |
|
ds@1952
|
141 |
|
|
ds@1952
|
142 |
fileFound = g_LibSys.FileTime(m_ConfigFile, FileTime_LastChange, &fileTime); |
|
ds@1952
|
143 |
|
|
ds@1952
|
144 |
/* If the file is found and hasn't changed, bail out now. */ |
|
ds@1952
|
145 |
if (fileFound && fileTime == m_ConfigLastChanged) |
|
ds@1952
|
146 |
{ |
|
ds@1952
|
147 |
return; |
|
ds@1952
|
148 |
} |
|
ds@1952
|
149 |
|
|
ds@1952
|
150 |
/* If the file wasn't found, and we already have entries, we bail out too. |
|
ds@1952
|
151 |
* This case lets us optimize when a user deletes the config file, so we |
|
ds@1952
|
152 |
* don't reparse every single time the function is called. |
|
ds@1952
|
153 |
*/ |
|
ds@1952
|
154 |
if (!fileFound && m_MapLists.size() > 0) |
|
ds@1952
|
155 |
{ |
|
ds@1952
|
156 |
return; |
|
ds@1952
|
157 |
} |
|
ds@1952
|
158 |
|
|
ds@1952
|
159 |
m_pMapCycleFile = icvar->FindVar("mapcyclefile"); |
|
ds@1952
|
160 |
|
|
ds@1952
|
161 |
/* Dump everything we know about. */ |
|
ds@1952
|
162 |
List<maplist_info_t *> compat; |
|
ds@1952
|
163 |
DumpCache(&compat); |
|
ds@1952
|
164 |
|
|
ds@1952
|
165 |
/* All this is to add the default entry back in. */ |
|
ds@1952
|
166 |
maplist_info_t *pDefList = new maplist_info_t; |
|
ds@1952
|
167 |
|
|
ds@1952
|
168 |
pDefList->bIsPath = true; |
|
ds@1952
|
169 |
strncopy(pDefList->name, "mapcyclefile", sizeof(pDefList->name)); |
|
ds@1952
|
170 |
g_SourceMod.BuildPath(Path_Game, |
|
ds@1952
|
171 |
pDefList->path, |
|
ds@1952
|
172 |
sizeof(pDefList->path), |
|
ds@1952
|
173 |
"%s", |
|
ds@1952
|
174 |
m_pMapCycleFile ? m_pMapCycleFile->GetString() : "mapcycle.txt"); |
|
ds@1952
|
175 |
pDefList->last_modified_time = 0; |
|
ds@1952
|
176 |
pDefList->pArray = NULL; |
|
ds@1952
|
177 |
pDefList->serial = 0; |
|
ds@1952
|
178 |
|
|
ds@1952
|
179 |
m_ListLookup.insert("mapcyclefile", pDefList); |
|
ds@1952
|
180 |
m_MapLists.push_back(pDefList); |
|
ds@1952
|
181 |
|
|
ds@1952
|
182 |
/* Now parse the config file even if we don't know about it. |
|
ds@1952
|
183 |
* This will give us a nice error message. |
|
ds@1952
|
184 |
*/ |
|
ds@1952
|
185 |
if ((error = g_TextParser.ParseFile_SMC(m_ConfigFile, this, &states)) |
|
ds@1952
|
186 |
!= SMCError_Okay) |
|
ds@1952
|
187 |
{ |
|
ds@1952
|
188 |
const char *errmsg = g_TextParser.GetSMCErrorString(error); |
|
ds@1952
|
189 |
if (errmsg == NULL) |
|
ds@1952
|
190 |
{ |
|
ds@1952
|
191 |
errmsg = "Unknown error"; |
|
ds@1952
|
192 |
} |
|
ds@1952
|
193 |
g_Logger.LogError("[SM] Could not parse file \"%s\"", m_ConfigFile); |
|
ds@1952
|
194 |
g_Logger.LogError("[SM] Error on line %d (col %d): %s", |
|
ds@1952
|
195 |
states.line, |
|
ds@1952
|
196 |
states.col, |
|
ds@1952
|
197 |
errmsg); |
|
ds@1952
|
198 |
} |
|
ds@1952
|
199 |
else |
|
ds@1952
|
200 |
{ |
|
ds@1952
|
201 |
m_ConfigLastChanged = fileTime; |
|
ds@1952
|
202 |
} |
|
ds@1952
|
203 |
|
|
ds@1952
|
204 |
/* Now, re-add compat stuff back in if we can. */ |
|
ds@1952
|
205 |
List<maplist_info_t *>::iterator iter = compat.begin(); |
|
ds@1952
|
206 |
while (iter != compat.end()) |
|
ds@1952
|
207 |
{ |
|
ds@1952
|
208 |
if (m_ListLookup.retrieve((*iter)->name) != NULL) |
|
ds@1952
|
209 |
{ |
|
ds@1952
|
210 |
/* The compatibility shim is no longer needed. */ |
|
ds@1952
|
211 |
delete (*iter)->pArray; |
|
ds@1952
|
212 |
delete (*iter); |
|
ds@1952
|
213 |
} |
|
ds@1952
|
214 |
else |
|
ds@1952
|
215 |
{ |
|
ds@1952
|
216 |
m_ListLookup.insert((*iter)->name, (*iter)); |
|
ds@1952
|
217 |
m_MapLists.push_back((*iter)); |
|
ds@1952
|
218 |
} |
|
ds@1952
|
219 |
iter = compat.erase(iter); |
|
ds@1952
|
220 |
} |
|
ds@1952
|
221 |
} |
|
ds@1952
|
222 |
void ReadSMC_ParseStart() |
|
ds@1952
|
223 |
{ |
|
ds@1952
|
224 |
m_CurState = MPS_NONE; |
|
ds@1952
|
225 |
m_IgnoreLevel = 0; |
|
ds@1952
|
226 |
m_pCurMapList = NULL; |
|
ds@1952
|
227 |
} |
|
ds@1952
|
228 |
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name) |
|
ds@1952
|
229 |
{ |
|
ds@1952
|
230 |
if (m_IgnoreLevel) |
|
ds@1952
|
231 |
{ |
|
ds@1952
|
232 |
m_IgnoreLevel++; |
|
ds@1952
|
233 |
return SMCResult_Continue; |
|
ds@1952
|
234 |
} |
|
ds@1952
|
235 |
|
|
ds@1952
|
236 |
if (m_CurState == MPS_NONE) |
|
ds@1952
|
237 |
{ |
|
ds@1952
|
238 |
if (strcmp(name, "MapLists") == 0) |
|
ds@1952
|
239 |
{ |
|
ds@1952
|
240 |
m_CurState = MPS_GLOBAL; |
|
ds@1952
|
241 |
} |
|
ds@1952
|
242 |
else |
|
ds@1952
|
243 |
{ |
|
ds@1952
|
244 |
m_IgnoreLevel = 1; |
|
ds@1952
|
245 |
} |
|
ds@1952
|
246 |
} |
|
ds@1952
|
247 |
else if (m_CurState == MPS_GLOBAL) |
|
ds@1952
|
248 |
{ |
|
ds@1952
|
249 |
m_pCurMapList = new maplist_info_t; |
|
ds@1952
|
250 |
|
|
ds@1952
|
251 |
memset(m_pCurMapList, 0, sizeof(maplist_info_t)); |
|
ds@1952
|
252 |
strncopy(m_pCurMapList->name, name, sizeof(m_pCurMapList->name)); |
|
ds@1952
|
253 |
|
|
ds@1952
|
254 |
m_CurState = MPS_MAPLIST; |
|
ds@1952
|
255 |
} |
|
ds@1952
|
256 |
else if (m_CurState == MPS_MAPLIST) |
|
ds@1952
|
257 |
{ |
|
ds@1952
|
258 |
m_IgnoreLevel++; |
|
ds@1952
|
259 |
} |
|
ds@1952
|
260 |
|
|
ds@1952
|
261 |
return SMCResult_Continue; |
|
ds@1952
|
262 |
} |
|
ds@1952
|
263 |
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) |
|
ds@1952
|
264 |
{ |
|
ds@1952
|
265 |
if (m_IgnoreLevel || m_pCurMapList == NULL) |
|
ds@1952
|
266 |
{ |
|
ds@1952
|
267 |
return SMCResult_Continue; |
|
ds@1952
|
268 |
} |
|
ds@1952
|
269 |
|
|
ds@1952
|
270 |
if (strcmp(key, "file") == 0) |
|
ds@1952
|
271 |
{ |
|
ds@1952
|
272 |
g_SourceMod.BuildPath(Path_Game, |
|
ds@1952
|
273 |
m_pCurMapList->path, |
|
ds@1952
|
274 |
sizeof(m_pCurMapList->path), |
|
ds@1952
|
275 |
"%s", |
|
ds@1952
|
276 |
value); |
|
ds@1952
|
277 |
m_pCurMapList->bIsPath = true; |
|
ds@1952
|
278 |
} |
|
ds@1952
|
279 |
else if (strcmp(key, "target") == 0) |
|
ds@1952
|
280 |
{ |
|
ds@1952
|
281 |
strncopy(m_pCurMapList->path, value, sizeof(m_pCurMapList->path)); |
|
ds@1952
|
282 |
m_pCurMapList->bIsPath = false; |
|
ds@1952
|
283 |
} |
|
ds@1952
|
284 |
|
|
ds@1952
|
285 |
return SMCResult_Continue; |
|
ds@1952
|
286 |
} |
|
ds@1952
|
287 |
SMCResult ReadSMC_LeavingSection(const SMCStates *states) |
|
ds@1952
|
288 |
{ |
|
ds@1952
|
289 |
if (m_IgnoreLevel) |
|
ds@1952
|
290 |
{ |
|
ds@1952
|
291 |
m_IgnoreLevel--; |
|
ds@1952
|
292 |
return SMCResult_Continue; |
|
ds@1952
|
293 |
} |
|
ds@1952
|
294 |
|
|
ds@1952
|
295 |
if (m_CurState == MPS_MAPLIST) |
|
ds@1952
|
296 |
{ |
|
ds@1952
|
297 |
if (m_pCurMapList != NULL |
|
ds@1952
|
298 |
&& m_pCurMapList->path[0] != '\0' |
|
ds@1952
|
299 |
&& m_ListLookup.retrieve(m_pCurMapList->name) == NULL) |
|
ds@1952
|
300 |
{ |
|
ds@1952
|
301 |
m_ListLookup.insert(m_pCurMapList->name, m_pCurMapList); |
|
ds@1952
|
302 |
m_MapLists.push_back(m_pCurMapList); |
|
ds@1952
|
303 |
m_pCurMapList = NULL; |
|
ds@1952
|
304 |
} |
|
ds@1952
|
305 |
else |
|
ds@1952
|
306 |
{ |
|
ds@1952
|
307 |
delete m_pCurMapList; |
|
ds@1952
|
308 |
m_pCurMapList = NULL; |
|
ds@1952
|
309 |
} |
|
ds@1952
|
310 |
m_CurState = MPS_GLOBAL; |
|
ds@1952
|
311 |
} |
|
ds@1952
|
312 |
else if (m_CurState == MPS_GLOBAL) |
|
ds@1952
|
313 |
{ |
|
ds@1952
|
314 |
m_CurState = MPS_NONE; |
|
ds@1952
|
315 |
} |
|
ds@1952
|
316 |
|
|
ds@1952
|
317 |
return SMCResult_Continue; |
|
ds@1952
|
318 |
} |
|
ds@1952
|
319 |
void ReadSMC_ParseEnd(bool halted, bool failed) |
|
ds@1952
|
320 |
{ |
|
ds@1952
|
321 |
delete m_pCurMapList; |
|
ds@1952
|
322 |
m_pCurMapList = NULL; |
|
ds@1952
|
323 |
} |
|
ds@1952
|
324 |
static int sort_maps_in_adt_array(const void *str1, const void *str2) |
|
ds@1952
|
325 |
{ |
|
ds@1952
|
326 |
return strcmp((char *)str1, (char *)str2); |
|
ds@1952
|
327 |
} |
|
ds@1952
|
328 |
CellArray *UpdateMapList(CellArray *pUseArray, const char *name, int *pSerial, unsigned int flags) |
|
ds@1952
|
329 |
{ |
|
ds@1952
|
330 |
int change_serial; |
|
ds@1952
|
331 |
CellArray *pNewArray = NULL; |
|
ds@1952
|
332 |
bool success, free_new_array; |
|
ds@1952
|
333 |
|
|
ds@1952
|
334 |
free_new_array = false; |
|
ds@1952
|
335 |
|
|
ds@1952
|
336 |
if ((success = GetMapList(&pNewArray, name, &change_serial)) == false) |
|
ds@1952
|
337 |
{ |
|
ds@1952
|
338 |
if ((flags & MAPLIST_FLAG_NO_DEFAULT) != MAPLIST_FLAG_NO_DEFAULT) |
|
ds@1952
|
339 |
{ |
|
ds@1952
|
340 |
/* If this list failed, and it's not the default, try the default. |
|
ds@1952
|
341 |
*/ |
|
ds@1952
|
342 |
if (strcmp(name, "default") != 0) |
|
ds@1952
|
343 |
{ |
|
ds@1952
|
344 |
success = GetMapList(&pNewArray, name, &change_serial); |
|
ds@1952
|
345 |
} |
|
ds@1952
|
346 |
/* If either of the last two conditions failed, try again if we can. */ |
|
ds@1952
|
347 |
if (!success && strcmp(name, "mapcyclefile") != 0) |
|
ds@1952
|
348 |
{ |
|
ds@1952
|
349 |
success = GetMapList(&pNewArray, "mapcyclefile", &change_serial); |
|
ds@1952
|
350 |
} |
|
ds@1952
|
351 |
} |
|
ds@1952
|
352 |
} |
|
ds@1952
|
353 |
|
|
ds@1952
|
354 |
/* If there was a success, and the serial has not changed, bail out. */ |
|
ds@1952
|
355 |
if (success && *pSerial == change_serial) |
|
ds@1952
|
356 |
{ |
|
ds@1952
|
357 |
return NULL; |
|
ds@1952
|
358 |
} |
|
ds@1952
|
359 |
|
|
ds@1952
|
360 |
/** |
|
ds@1952
|
361 |
* If there was a success but no map list, we need to look in the maps folder. |
|
ds@1952
|
362 |
* If there was a failure and the flag is specified, we need to look in the maps folder. |
|
ds@1952
|
363 |
*/ |
|
ds@1952
|
364 |
if ((success && pNewArray == NULL) |
|
ds@1952
|
365 |
|| (!success && ((flags & MAPLIST_FLAG_MAPSFOLDER) == MAPLIST_FLAG_MAPSFOLDER))) |
|
ds@1952
|
366 |
{ |
|
ds@1952
|
367 |
char path[255]; |
|
ds@1952
|
368 |
IDirectory *pDir; |
|
ds@1952
|
369 |
|
|
ds@1952
|
370 |
pNewArray = new CellArray(64); |
|
ds@1952
|
371 |
free_new_array = true; |
|
ds@1952
|
372 |
g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "maps"); |
|
ds@1952
|
373 |
|
|
ds@1952
|
374 |
if ((pDir = g_LibSys.OpenDirectory(path)) != NULL) |
|
ds@1952
|
375 |
{ |
|
ds@1952
|
376 |
char *ptr; |
|
ds@1952
|
377 |
cell_t *blk; |
|
ds@1952
|
378 |
char buffer[PLATFORM_MAX_PATH]; |
|
ds@1952
|
379 |
|
|
ds@1952
|
380 |
while (pDir->MoreFiles()) |
|
ds@1952
|
381 |
{ |
|
ds@1952
|
382 |
if (!pDir->IsEntryFile() |
|
ds@1952
|
383 |
|| strcmp(pDir->GetEntryName(), ".") == 0 |
|
ds@1952
|
384 |
|| strcmp(pDir->GetEntryName(), "..") == 0) |
|
ds@1952
|
385 |
{ |
|
ds@1952
|
386 |
pDir->NextEntry(); |
|
ds@1952
|
387 |
continue; |
|
ds@1952
|
388 |
} |
|
ds@1952
|
389 |
strncopy(buffer, pDir->GetEntryName(), sizeof(buffer)); |
|
ds@1952
|
390 |
if ((ptr = strstr(buffer, ".bsp")) == NULL) |
|
ds@1952
|
391 |
{ |
|
ds@1952
|
392 |
pDir->NextEntry(); |
|
ds@1952
|
393 |
continue; |
|
ds@1952
|
394 |
} |
|
ds@1952
|
395 |
*ptr = '\0'; |
|
ds@1952
|
396 |
if (!engine->IsMapValid(buffer)) |
|
ds@1952
|
397 |
{ |
|
ds@1952
|
398 |
pDir->NextEntry(); |
|
ds@1952
|
399 |
continue; |
|
ds@1952
|
400 |
} |
|
ds@1952
|
401 |
if ((blk = pNewArray->push()) == NULL) |
|
ds@1952
|
402 |
{ |
|
ds@1952
|
403 |
pDir->NextEntry(); |
|
ds@1952
|
404 |
continue; |
|
ds@1952
|
405 |
} |
|
ds@1952
|
406 |
strncopy((char *)blk, buffer, 255); |
|
ds@1952
|
407 |
pDir->NextEntry(); |
|
ds@1952
|
408 |
} |
|
ds@1952
|
409 |
g_LibSys.CloseDirectory(pDir); |
|
ds@1952
|
410 |
} |
|
ds@1952
|
411 |
|
|
ds@1952
|
412 |
/* Remove the array if there were no items. */ |
|
ds@1952
|
413 |
if (pNewArray->size() == 0) |
|
ds@1952
|
414 |
{ |
|
ds@1952
|
415 |
delete pNewArray; |
|
ds@1952
|
416 |
pNewArray = NULL; |
|
ds@1952
|
417 |
} |
|
ds@1952
|
418 |
else |
|
ds@1952
|
419 |
{ |
|
ds@1952
|
420 |
qsort(pNewArray->base(), |
|
ds@1952
|
421 |
pNewArray->size(), |
|
ds@1952
|
422 |
pNewArray->blocksize() * sizeof(cell_t), |
|
ds@1952
|
423 |
sort_maps_in_adt_array); |
|
ds@1952
|
424 |
} |
|
ds@1952
|
425 |
|
|
ds@1952
|
426 |
change_serial = -1; |
|
ds@1952
|
427 |
} |
|
ds@1952
|
428 |
|
|
ds@1952
|
429 |
/* If there is still no array by this point, bail out. */ |
|
ds@1952
|
430 |
if (pNewArray == NULL) |
|
ds@1952
|
431 |
{ |
|
ds@1952
|
432 |
*pSerial = -1; |
|
ds@1952
|
433 |
return NULL; |
|
ds@1952
|
434 |
} |
|
ds@1952
|
435 |
|
|
ds@1952
|
436 |
*pSerial = change_serial; |
|
ds@1952
|
437 |
|
|
ds@1952
|
438 |
/* If there is no input array, return something temporary. */ |
|
ds@1952
|
439 |
if (pUseArray == NULL) |
|
ds@1952
|
440 |
{ |
|
ds@1952
|
441 |
if (free_new_array) |
|
ds@1952
|
442 |
{ |
|
ds@1952
|
443 |
return pNewArray; |
|
ds@1952
|
444 |
} |
|
ds@1952
|
445 |
else |
|
ds@1952
|
446 |
{ |
|
ds@1952
|
447 |
return pNewArray->clone(); |
|
ds@1952
|
448 |
} |
|
ds@1952
|
449 |
} |
|
ds@1952
|
450 |
|
|
ds@1952
|
451 |
/* Clear the input array if necessary. */ |
|
ds@1952
|
452 |
if ((flags & MAPLIST_FLAG_CLEARARRAY) == MAPLIST_FLAG_CLEARARRAY) |
|
ds@1952
|
453 |
{ |
|
ds@1952
|
454 |
pUseArray->clear(); |
|
ds@1952
|
455 |
} |
|
ds@1952
|
456 |
|
|
ds@1952
|
457 |
/* Copy. */ |
|
ds@1952
|
458 |
cell_t *blk_dst; |
|
ds@1952
|
459 |
cell_t *blk_src; |
|
ds@1952
|
460 |
for (size_t i = 0; i < pNewArray->size(); i++) |
|
ds@1952
|
461 |
{ |
|
ds@1952
|
462 |
blk_dst = pUseArray->push(); |
|
ds@1952
|
463 |
blk_src = pNewArray->at(i); |
|
ds@1952
|
464 |
strncopy((char *)blk_dst, (char *)blk_src, pUseArray->blocksize() * sizeof(cell_t)); |
|
ds@1952
|
465 |
} |
|
ds@1952
|
466 |
|
|
ds@1952
|
467 |
/* Free resources if necessary. */ |
|
ds@1952
|
468 |
if (free_new_array) |
|
ds@1952
|
469 |
{ |
|
ds@1952
|
470 |
delete pNewArray; |
|
ds@1952
|
471 |
} |
|
ds@1952
|
472 |
|
|
ds@1952
|
473 |
/* Return the array we were given. */ |
|
ds@1952
|
474 |
return pUseArray; |
|
ds@1952
|
475 |
} |
|
ds@1952
|
476 |
private: |
|
ds@1952
|
477 |
bool GetMapList(CellArray **ppArray, const char *name, int *pSerial) |
|
ds@1952
|
478 |
{ |
|
ds@1952
|
479 |
time_t last_time; |
|
ds@1952
|
480 |
maplist_info_t *pMapList, **ppMapList; |
|
ds@1952
|
481 |
|
|
ds@1952
|
482 |
if ((ppMapList = m_ListLookup.retrieve(name)) == NULL) |
|
ds@1952
|
483 |
{ |
|
ds@1952
|
484 |
return false; |
|
ds@1952
|
485 |
} |
|
ds@1952
|
486 |
|
|
ds@1952
|
487 |
pMapList = *ppMapList; |
|
ds@1952
|
488 |
|
|
ds@1952
|
489 |
if (!pMapList->bIsPath) |
|
ds@1952
|
490 |
{ |
|
ds@1952
|
491 |
return GetMapList(ppArray, pMapList->path, pSerial); |
|
ds@1952
|
492 |
} |
|
ds@1952
|
493 |
|
|
ds@1952
|
494 |
/* If it is a path, and the path is "*", assume all files must be used. */ |
|
ds@1952
|
495 |
if (strcmp(pMapList->path, "*") == 0) |
|
ds@1952
|
496 |
{ |
|
ds@1952
|
497 |
*ppArray = NULL; |
|
ds@1952
|
498 |
return true; |
|
ds@1952
|
499 |
} |
|
ds@1952
|
500 |
|
|
ds@1952
|
501 |
if (m_pMapCycleFile != NULL && strcmp(name, "mapcyclefile") == 0) |
|
ds@1952
|
502 |
{ |
|
ds@1952
|
503 |
char path[PLATFORM_MAX_PATH]; |
|
ds@1952
|
504 |
g_SourceMod.BuildPath(Path_Game, |
|
ds@1952
|
505 |
path, |
|
ds@1952
|
506 |
sizeof(path), |
|
ds@1952
|
507 |
"%s", |
|
ds@1952
|
508 |
m_pMapCycleFile ? m_pMapCycleFile->GetString() : "mapcycle.txt"); |
|
ds@1952
|
509 |
|
|
ds@1952
|
510 |
if (strcmp(path, pMapList->path) != 0) |
|
ds@1952
|
511 |
{ |
|
ds@1952
|
512 |
strncopy(pMapList->path, path, sizeof(pMapList->path)); |
|
ds@1952
|
513 |
pMapList->last_modified_time = 0; |
|
ds@1952
|
514 |
} |
|
ds@1952
|
515 |
} |
|
ds@1952
|
516 |
|
|
ds@1952
|
517 |
if (!g_LibSys.FileTime(pMapList->path, FileTime_LastChange, &last_time) |
|
ds@1952
|
518 |
|| last_time > pMapList->last_modified_time) |
|
ds@1952
|
519 |
{ |
|
ds@1952
|
520 |
/* Reparse */ |
|
ds@1952
|
521 |
FILE *fp; |
|
ds@1952
|
522 |
cell_t *blk; |
|
ds@1952
|
523 |
char buffer[255]; |
|
ds@1952
|
524 |
|
|
ds@1952
|
525 |
if ((fp = fopen(pMapList->path, "rt")) == NULL) |
|
ds@1952
|
526 |
{ |
|
ds@1952
|
527 |
return false; |
|
ds@1952
|
528 |
} |
|
ds@1952
|
529 |
|
|
ds@1952
|
530 |
delete pMapList->pArray; |
|
ds@1952
|
531 |
pMapList->pArray = new CellArray(64); |
|
ds@1952
|
532 |
|
|
ds@1952
|
533 |
while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL) |
|
ds@1952
|
534 |
{ |
|
ds@1952
|
535 |
size_t len = strlen(buffer); |
|
ds@1952
|
536 |
char *ptr = UTIL_TrimWhitespace(buffer, len); |
|
ds@1952
|
537 |
if (*ptr == '\0' |
|
ds@1952
|
538 |
|| *ptr == ';' |
|
ds@1952
|
539 |
|| strncmp(ptr, "//", 2) == 0) |
|
ds@1952
|
540 |
{ |
|
ds@1952
|
541 |
continue; |
|
ds@1952
|
542 |
} |
|
ds@1952
|
543 |
if (!engine->IsMapValid(ptr)) |
|
ds@1952
|
544 |
{ |
|
ds@1952
|
545 |
continue; |
|
ds@1952
|
546 |
} |
|
ds@1952
|
547 |
if ((blk = pMapList->pArray->push()) != NULL) |
|
ds@1952
|
548 |
{ |
|
ds@1952
|
549 |
strncopy((char *)blk, ptr, 255); |
|
ds@1952
|
550 |
} |
|
ds@1952
|
551 |
} |
|
ds@1952
|
552 |
|
|
ds@1952
|
553 |
fclose(fp); |
|
ds@1952
|
554 |
|
|
ds@1952
|
555 |
pMapList->last_modified_time = last_time; |
|
ds@1952
|
556 |
pMapList->serial = ++m_nSerialChange; |
|
ds@1952
|
557 |
} |
|
ds@1952
|
558 |
|
|
ds@1952
|
559 |
if (pMapList->pArray == NULL || pMapList->pArray->size() == 0) |
|
ds@1952
|
560 |
{ |
|
ds@1952
|
561 |
return false; |
|
ds@1952
|
562 |
} |
|
ds@1952
|
563 |
|
|
ds@1952
|
564 |
*pSerial = pMapList->serial; |
|
ds@1952
|
565 |
*ppArray = pMapList->pArray; |
|
ds@1952
|
566 |
|
|
ds@1952
|
567 |
return true; |
|
ds@1952
|
568 |
} |
|
ds@1952
|
569 |
void DumpCache(List<maplist_info_t *> *compat_list) |
|
ds@1952
|
570 |
{ |
|
ds@1952
|
571 |
m_ListLookup.clear(); |
|
ds@1952
|
572 |
|
|
ds@1952
|
573 |
List<maplist_info_t *>::iterator iter = m_MapLists.begin(); |
|
ds@1952
|
574 |
while (iter != m_MapLists.end()) |
|
ds@1952
|
575 |
{ |
|
ds@1952
|
576 |
if (compat_list != NULL && (*iter)->bIsCompat) |
|
ds@1952
|
577 |
{ |
|
ds@1952
|
578 |
compat_list->push_back((*iter)); |
|
ds@1952
|
579 |
} |
|
ds@1952
|
580 |
else |
|
ds@1952
|
581 |
{ |
|
ds@1952
|
582 |
delete (*iter)->pArray; |
|
ds@1952
|
583 |
delete (*iter); |
|
ds@1952
|
584 |
} |
|
ds@1952
|
585 |
iter = m_MapLists.erase(iter); |
|
ds@1952
|
586 |
} |
|
ds@1952
|
587 |
} |
|
ds@1952
|
588 |
private: |
|
ds@1952
|
589 |
char m_ConfigFile[PLATFORM_MAX_PATH]; |
|
ds@1952
|
590 |
time_t m_ConfigLastChanged; |
|
ds@1952
|
591 |
ConVar *m_pMapCycleFile; |
|
ds@1952
|
592 |
KTrie<maplist_info_t *> m_ListLookup; |
|
ds@1952
|
593 |
List<maplist_info_t *> m_MapLists; |
|
ds@1952
|
594 |
MapListState m_CurState; |
|
ds@1952
|
595 |
unsigned int m_IgnoreLevel; |
|
ds@1952
|
596 |
maplist_info_t *m_pCurMapList; |
|
ds@1952
|
597 |
int m_nSerialChange; |
|
ds@1952
|
598 |
} s_MapLists; |
|
ds@1952
|
599 |
|
|
ds@1952
|
600 |
static cell_t LoadMapList(IPluginContext *pContext, const cell_t *params) |
|
ds@1952
|
601 |
{ |
|
ds@1952
|
602 |
char *str; |
|
ds@1952
|
603 |
Handle_t hndl; |
|
ds@1952
|
604 |
cell_t *addr, flags; |
|
ds@1952
|
605 |
CellArray *pArray, *pNewArray; |
|
ds@1952
|
606 |
|
|
ds@1952
|
607 |
hndl = params[1]; |
|
ds@1952
|
608 |
pContext->LocalToPhysAddr(params[2], &addr); |
|
ds@1952
|
609 |
pContext->LocalToString(params[3], &str); |
|
ds@1952
|
610 |
flags = params[4]; |
|
ds@1952
|
611 |
|
|
ds@1952
|
612 |
/* Make sure the input Handle is valid */ |
|
ds@1952
|
613 |
pArray = NULL; |
|
ds@1952
|
614 |
if (hndl != BAD_HANDLE) |
|
ds@1952
|
615 |
{ |
|
ds@1952
|
616 |
HandleError err; |
|
ds@1952
|
617 |
HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); |
|
ds@1952
|
618 |
|
|
ds@1952
|
619 |
if ((err = g_HandleSys.ReadHandle(hndl, htCellArray, &sec, (void **)&pArray)) |
|
ds@1952
|
620 |
!= HandleError_None) |
|
ds@1952
|
621 |
{ |
|
ds@1952
|
622 |
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err); |
|
ds@1952
|
623 |
} |
|
ds@1952
|
624 |
} |
|
ds@1952
|
625 |
|
|
ds@1952
|
626 |
/* Make sure the map list cache is up to date at the root */ |
|
ds@1952
|
627 |
s_MapLists.UpdateCache(); |
|
ds@1952
|
628 |
|
|
ds@1952
|
629 |
/* Try to get the map list. */ |
|
ds@1952
|
630 |
if ((pNewArray = s_MapLists.UpdateMapList(pArray, str, addr, flags)) == NULL) |
|
ds@1952
|
631 |
{ |
|
ds@1952
|
632 |
return BAD_HANDLE; |
|
ds@1952
|
633 |
} |
|
ds@1952
|
634 |
|
|
ds@1952
|
635 |
/* If the user wanted a new array, create it now. */ |
|
ds@1952
|
636 |
if (hndl == BAD_HANDLE) |
|
ds@1952
|
637 |
{ |
|
ds@1952
|
638 |
if ((hndl = g_HandleSys.CreateHandle(htCellArray, pNewArray, pContext->GetIdentity(), g_pCoreIdent, NULL)) |
|
ds@1952
|
639 |
== BAD_HANDLE) |
|
ds@1952
|
640 |
{ |
|
ds@1952
|
641 |
*addr = -1; |
|
ds@1952
|
642 |
delete pNewArray; |
|
ds@1952
|
643 |
return BAD_HANDLE; |
|
ds@1952
|
644 |
} |
|
ds@1952
|
645 |
} |
|
ds@1952
|
646 |
|
|
ds@1952
|
647 |
return hndl; |
|
ds@1952
|
648 |
} |
|
ds@1952
|
649 |
|
|
ds@1952
|
650 |
static cell_t SetMapListCompatBind(IPluginContext *pContext, const cell_t *params) |
|
ds@1952
|
651 |
{ |
|
ds@1952
|
652 |
char *name, *file; |
|
ds@1952
|
653 |
|
|
ds@1952
|
654 |
pContext->LocalToString(params[1], &name); |
|
ds@1952
|
655 |
pContext->LocalToString(params[2], &file); |
|
ds@1952
|
656 |
|
|
ds@1952
|
657 |
s_MapLists.UpdateCache(); |
|
ds@1952
|
658 |
s_MapLists.AddOrUpdateDefault(name, file); |
|
ds@1952
|
659 |
|
|
ds@1952
|
660 |
return 1; |
|
ds@1952
|
661 |
} |
|
ds@1952
|
662 |
|
|
ds@1952
|
663 |
REGISTER_NATIVES(mapListNatives) |
|
ds@1952
|
664 |
{ |
|
ds@1952
|
665 |
{"ReadMapList", LoadMapList}, |
|
ds@1952
|
666 |
{"SetMapListCompatBind", SetMapListCompatBind}, |
|
ds@1952
|
667 |
{NULL, NULL}, |
|
ds@1952
|
668 |
}; |