1
2 #include "compat.h"
3 #include "build.h"
4 #include "scriptfile.h"
5 #include "cache1d.h"
6 #include "kplib.h"
7 #include "baselayer.h"
8
9 #include "common.h"
10
11 #include "vfs.h"
12
PrintBuildInfo(void)13 void PrintBuildInfo(void)
14 {
15 buildprint(
16 "Built ", s_buildTimestamp, ", "
17
18 #if defined __INTEL_COMPILER
19 "ICC ", __INTEL_COMPILER / 100, ".", __INTEL_COMPILER % 100, " " __INTEL_COMPILER_BUILD_DATE " (" __VERSION__ ")"
20 #elif defined __clang__
21 "clang "
22 # ifdef DEBUGGINGAIDS
23 __clang_version__
24 # else
25 , __clang_major__, ".", __clang_minor__, ".", __clang_patchlevel__,
26 # endif
27 #elif defined _MSC_VER
28 "MSVC ",
29 # if defined _MSC_FULL_VER
30 _MSC_FULL_VER / 10000000, ".", _MSC_FULL_VER % 10000000 / 100000, ".", _MSC_FULL_VER % 100000, ".", _MSC_BUILD,
31 # else
32 _MSC_VER / 100, ".", _MSC_VER % 100,
33 # endif
34 #elif defined __GNUC__
35 "GCC "
36 # ifdef DEBUGGINGAIDS
37 __VERSION__
38 # else
39 , __GNUC__, ".", __GNUC_MINOR__,
40 # if defined __GNUC_PATCHLEVEL__
41 ".", __GNUC_PATCHLEVEL__,
42 # endif
43 # endif
44 #else
45 "Unknown"
46 #endif
47 ", "
48 #ifdef BITNESS64
49 "64"
50 #else
51 "32"
52 #endif
53 "-bit "
54 #if B_BIG_ENDIAN == 1
55 "big-endian"
56 #endif
57 "\n");
58
59 // TODO: architecture, OS, maybe build and feature settings
60 }
61
62 // def/clipmap handling
63
64 // g_defNamePtr can ONLY point to a malloc'd block (length BMAX_PATH)
65 char *g_defNamePtr = NULL;
66
clearDefNamePtr(void)67 void clearDefNamePtr(void)
68 {
69 Xfree(g_defNamePtr);
70 // g_defNamePtr assumed to be assigned to right after
71 }
72
73 GrowArray<char *> g_defModules;
74
75 #ifdef HAVE_CLIPSHAPE_FEATURE
76 GrowArray<char *> g_clipMapFiles;
77 #endif
78
G_AddDef(const char * buffer)79 void G_AddDef(const char *buffer)
80 {
81 clearDefNamePtr();
82 g_defNamePtr = dup_filename(buffer);
83 initprintf("Using DEF file \"%s\".\n",g_defNamePtr);
84 }
85
G_AddDefModule(const char * buffer)86 void G_AddDefModule(const char *buffer)
87 {
88 g_defModules.append(Xstrdup(buffer));
89 }
90
91 #ifdef HAVE_CLIPSHAPE_FEATURE
G_AddClipMap(const char * buffer)92 void G_AddClipMap(const char *buffer)
93 {
94 g_clipMapFiles.append(Xstrdup(buffer));
95 }
96 #endif
97
98 //////////
99
getatoken(scriptfile * sf,const tokenlist * tl,int32_t ntokens)100 int32_t getatoken(scriptfile *sf, const tokenlist *tl, int32_t ntokens)
101 {
102 char *tok;
103 int32_t i;
104
105 if (!sf) return T_ERROR;
106 tok = scriptfile_gettoken(sf);
107 if (!tok) return T_EOF;
108
109 for (i=ntokens-1; i>=0; i--)
110 {
111 if (!Bstrcasecmp(tok, tl[i].text))
112 return tl[i].tokenid;
113 }
114 return T_ERROR;
115 }
116
117 //////////
118
G_CheckCmdSwitch(int32_t argc,char const * const * argv,const char * str)119 int32_t G_CheckCmdSwitch(int32_t argc, char const * const * argv, const char *str)
120 {
121 int32_t i;
122 for (i=0; i<argc; i++)
123 {
124 if (str && !Bstrcasecmp(argv[i], str))
125 return 1;
126 }
127
128 return 0;
129 }
130
131 // returns: 1 if file could be opened, 0 else
testkopen(const char * filename,char searchfirst)132 int32_t testkopen(const char *filename, char searchfirst)
133 {
134 buildvfs_kfd fd = kopen4load(filename, searchfirst);
135 if (fd != buildvfs_kfd_invalid)
136 kclose(fd);
137 return (fd != buildvfs_kfd_invalid);
138 }
139
140 // checks from path and in ZIPs, returns 1 if NOT found
check_file_exist(const char * fn)141 int32_t check_file_exist(const char *fn)
142 {
143 #ifdef USE_PHYSFS
144 return !PHYSFS_exists(fn);
145 #else
146 int32_t opsm = pathsearchmode;
147 char *tfn;
148
149 pathsearchmode = 1;
150 if (findfrompath(fn,&tfn) < 0)
151 {
152 char buf[BMAX_PATH];
153
154 Bstrcpy(buf,fn);
155 kzfindfilestart(buf);
156 if (!kzfindfile(buf))
157 {
158 initprintf("Error: file \"%s\" does not exist\n",fn);
159 pathsearchmode = opsm;
160 return 1;
161 }
162 }
163 else Xfree(tfn);
164 pathsearchmode = opsm;
165
166 return 0;
167 #endif
168 }
169
170
171 //// FILE NAME / DIRECTORY LISTS ////
fnlist_clearnames(fnlist_t * fnl)172 void fnlist_clearnames(fnlist_t *fnl)
173 {
174 klistfree(fnl->finddirs);
175 klistfree(fnl->findfiles);
176
177 fnl->finddirs = fnl->findfiles = NULL;
178 fnl->numfiles = fnl->numdirs = 0;
179 }
180
181 // dirflags, fileflags:
182 // -1 means "don't get dirs/files",
183 // otherwise ORed to flags for respective klistpath
fnlist_getnames(fnlist_t * fnl,const char * dirname,const char * pattern,int32_t dirflags,int32_t fileflags)184 int32_t fnlist_getnames(fnlist_t *fnl, const char *dirname, const char *pattern,
185 int32_t dirflags, int32_t fileflags)
186 {
187 BUILDVFS_FIND_REC *r;
188
189 fnlist_clearnames(fnl);
190
191 if (dirflags != -1)
192 fnl->finddirs = klistpath(dirname, "*", BUILDVFS_FIND_DIR|dirflags);
193 if (fileflags != -1)
194 fnl->findfiles = klistpath(dirname, pattern, BUILDVFS_FIND_FILE|fileflags);
195
196 for (r=fnl->finddirs; r; r=r->next)
197 fnl->numdirs++;
198 for (r=fnl->findfiles; r; r=r->next)
199 fnl->numfiles++;
200
201 return 0;
202 }
203
204
205 ////
206
207 // Copy FN to WBUF and append an extension if it's not there, which is checked
208 // case-insensitively.
209 // Returns: 1 if not all characters could be written to WBUF, 0 else.
maybe_append_ext(char * wbuf,int32_t wbufsiz,const char * fn,const char * ext)210 int32_t maybe_append_ext(char *wbuf, int32_t wbufsiz, const char *fn, const char *ext)
211 {
212 const int32_t slen=Bstrlen(fn), extslen=Bstrlen(ext);
213 const int32_t haveext = (slen>=extslen && Bstrcasecmp(&fn[slen-extslen], ext)==0);
214
215 Bassert((intptr_t)wbuf != (intptr_t)fn); // no aliasing
216
217 // If 'fn' has no extension suffixed, append one.
218 return (Bsnprintf(wbuf, wbufsiz, "%s%s", fn, haveext ? "" : ext) >= wbufsiz);
219 }
220
221
ldist(const void * s1,const void * s2)222 int32_t ldist(const void *s1, const void *s2)
223 {
224 auto sp1 = (vec2_t const *)s1;
225 auto sp2 = (vec2_t const *)s2;
226 return sepldist(sp1->x - sp2->x, sp1->y - sp2->y)
227 + (enginecompatibilitymode != ENGINE_EDUKE32);
228 }
229
dist(const void * s1,const void * s2)230 int32_t dist(const void *s1, const void *s2)
231 {
232 auto sp1 = (vec3_t const *)s1;
233 auto sp2 = (vec3_t const *)s2;
234 return sepdist(sp1->x - sp2->x, sp1->y - sp2->y, sp1->z - sp2->z);
235 }
236
FindDistance2D(int32_t x,int32_t y)237 int32_t FindDistance2D(int32_t x, int32_t y)
238 {
239 return sepldist(x, y);
240 }
241
FindDistance3D(int32_t x,int32_t y,int32_t z)242 int32_t FindDistance3D(int32_t x, int32_t y, int32_t z)
243 {
244 return sepdist(x, y, z);
245 }
246
247
248 // Clear OSD background
COMMON_clearbackground(int numcols,int numrows)249 void COMMON_clearbackground(int numcols, int numrows)
250 {
251 UNREFERENCED_PARAMETER(numcols);
252
253 # ifdef USE_OPENGL
254 if (videoGetRenderMode() >= REND_POLYMOST && in3dmode())
255 {
256 // glPushAttrib(GL_FOG_BIT);
257 polymost_setFogEnabled(false);
258 polymost_useColorOnly(true);
259
260 polymostSet2dView();
261 glColor4f(0.f, 0.f, 0.f, 0.67f);
262 glEnable(GL_BLEND);
263 glRecti(0, 0, xdim, 8*numrows+8);
264 glColor4f(0.f, 0.f, 0.f, 1.f);
265 glRecti(0, 8*numrows+4, xdim, 8*numrows+8);
266
267 // glPopAttrib();
268 polymost_useColorOnly(false);
269 polymost_setFogEnabled(true);
270
271 return;
272 }
273 # endif
274
275 CLEARLINES2D(0, min(ydim, numrows*8+8), blackcol*0x01010101);
276 }
277
278 #if defined _WIN32 && !defined EDUKE32_STANDALONE
279 # define NEED_SHLWAPI_H
280 # include "windows_inc.h"
281 # ifndef KEY_WOW64_64KEY
282 # define KEY_WOW64_64KEY 0x0100
283 # endif
284 # ifndef KEY_WOW64_32KEY
285 # define KEY_WOW64_32KEY 0x0200
286 # endif
287
Paths_ReadRegistryValue(char const * const SubKey,char const * const Value,char * const Output,DWORD * OutputSize)288 int Paths_ReadRegistryValue(char const * const SubKey, char const * const Value, char * const Output, DWORD * OutputSize)
289 {
290 // KEY_WOW64_32KEY gets us around Wow6432Node on 64-bit builds
291 REGSAM const wow64keys[] = { KEY_WOW64_32KEY, KEY_WOW64_64KEY };
292
293 for (auto &wow64key : wow64keys)
294 {
295 HKEY hkey;
296 LONG keygood = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NULL, 0, KEY_READ | wow64key, &hkey);
297
298 if (keygood != ERROR_SUCCESS)
299 continue;
300
301 LONG retval = SHGetValueA(hkey, SubKey, Value, NULL, Output, OutputSize);
302
303 RegCloseKey(hkey);
304
305 if (retval == ERROR_SUCCESS)
306 return 1;
307 }
308
309 return 0;
310 }
311 #endif
312
313 // A bare-bones "parser" for Valve's KeyValues VDF format.
314 // There is no guarantee this will function properly with ill-formed files.
KeyValues_SkipWhitespace(char * & buf,char * const bufend)315 static void KeyValues_SkipWhitespace(char *& buf, char * const bufend)
316 {
317 while (buf < bufend && (buf[0] == ' ' || buf[0] == '\n' || buf[0] == '\r' || buf[0] == '\t' || buf[0] == '\0'))
318 ++buf;
319
320 // comments
321 if (buf + 2 < bufend && buf[0] == '/' && buf[1] == '/')
322 {
323 while (buf < bufend && buf[0] != '\n' && buf[0] != '\r')
324 ++buf;
325
326 KeyValues_SkipWhitespace(buf, bufend);
327 }
328 }
KeyValues_SkipToEndOfQuotedToken(char * & buf,char * const bufend)329 static void KeyValues_SkipToEndOfQuotedToken(char *& buf, char * const bufend)
330 {
331 ++buf;
332 while (buf < bufend && buf[0] != '\"' && buf[-1] != '\\')
333 ++buf;
334 }
KeyValues_SkipToEndOfUnquotedToken(char * & buf,char * const bufend)335 static void KeyValues_SkipToEndOfUnquotedToken(char *& buf, char * const bufend)
336 {
337 while (buf < bufend && buf[0] != ' ' && buf[0] != '\n' && buf[0] != '\r' && buf[0] != '\t' && buf[0] != '\0')
338 ++buf;
339 }
KeyValues_SkipNextWhatever(char * & buf,char * const bufend)340 static void KeyValues_SkipNextWhatever(char *& buf, char * const bufend)
341 {
342 KeyValues_SkipWhitespace(buf, bufend);
343
344 if (buf == bufend)
345 return;
346
347 if (buf[0] == '{')
348 {
349 ++buf;
350 do
351 KeyValues_SkipNextWhatever(buf, bufend);
352 while (buf[0] != '}');
353 ++buf;
354 }
355 else if (buf[0] == '\"')
356 KeyValues_SkipToEndOfQuotedToken(buf, bufend);
357 else if (buf[0] != '}')
358 KeyValues_SkipToEndOfUnquotedToken(buf, bufend);
359
360 KeyValues_SkipWhitespace(buf, bufend);
361 }
KeyValues_NormalizeToken(char * & buf,char * const bufend)362 static char* KeyValues_NormalizeToken(char *& buf, char * const bufend)
363 {
364 char * token = buf;
365
366 if (buf < bufend && buf[0] == '\"')
367 {
368 ++token;
369
370 KeyValues_SkipToEndOfQuotedToken(buf, bufend);
371 buf[0] = '\0';
372
373 // account for escape sequences
374 const char * readseeker = token;
375 char * writeseeker = token;
376 while (readseeker <= buf)
377 {
378 if (readseeker[0] == '\\')
379 ++readseeker;
380
381 writeseeker[0] = readseeker[0];
382
383 ++writeseeker;
384 ++readseeker;
385 }
386
387 return token;
388 }
389
390 KeyValues_SkipToEndOfUnquotedToken(buf, bufend);
391 buf[0] = '\0';
392
393 return token;
394 }
KeyValues_FindKey(char * & buf,char * const bufend,const char * token)395 static void KeyValues_FindKey(char *& buf, char * const bufend, const char * token)
396 {
397 char *ParentKey = KeyValues_NormalizeToken(buf, bufend);
398 if (token != NULL) // pass in NULL to find the next key instead of a specific one
399 while (buf < bufend && Bstrcmp(ParentKey, token) != 0)
400 {
401 KeyValues_SkipNextWhatever(buf, bufend);
402 ParentKey = KeyValues_NormalizeToken(buf, bufend);
403 }
404
405 KeyValues_SkipWhitespace(buf, bufend);
406 }
KeyValues_FindParentKey(char * & buf,char * const bufend,const char * token)407 static int32_t KeyValues_FindParentKey(char *& buf, char * const bufend, const char * token)
408 {
409 KeyValues_SkipWhitespace(buf, bufend);
410
411 // end of scope
412 if (buf[0] == '}')
413 return 0;
414
415 KeyValues_FindKey(buf, bufend, token);
416
417 // ignore the wrong type
418 while (buf < bufend && buf[0] != '{')
419 {
420 KeyValues_SkipNextWhatever(buf, bufend);
421 KeyValues_FindKey(buf, bufend, token);
422 }
423
424 if (buf == bufend)
425 return 0;
426
427 return 1;
428 }
KeyValues_FindKeyValue(char * & buf,char * const bufend,const char * token)429 static char* KeyValues_FindKeyValue(char *& buf, char * const bufend, const char * token)
430 {
431 KeyValues_SkipWhitespace(buf, bufend);
432
433 // end of scope
434 if (buf[0] == '}')
435 return NULL;
436
437 KeyValues_FindKey(buf, bufend, token);
438
439 // ignore the wrong type
440 while (buf < bufend && buf[0] == '{')
441 {
442 KeyValues_SkipNextWhatever(buf, bufend);
443 KeyValues_FindKey(buf, bufend, token);
444 }
445
446 KeyValues_SkipWhitespace(buf, bufend);
447
448 if (buf == bufend)
449 return NULL;
450
451 return KeyValues_NormalizeToken(buf, bufend);
452 }
453
Paths_ParseSteamLibraryVDF(const char * fn,PathsParseFunc func)454 void Paths_ParseSteamLibraryVDF(const char * fn, PathsParseFunc func)
455 {
456 buildvfs_fd const fd = buildvfs_open_read(fn);
457 if (fd == buildvfs_fd_invalid)
458 return;
459
460 int32_t size = buildvfs_length(fd);
461 if (size <= 0)
462 return;
463
464 auto const bufstart = (char *)Xmalloc(size+1);
465 char * buf = bufstart;
466 size = (int32_t)buildvfs_read(fd, buf, size);
467 buildvfs_close(fd);
468 char * const bufend = buf + size;
469 bufend[0] = '\0';
470
471 if (KeyValues_FindParentKey(buf, bufend, "LibraryFolders"))
472 {
473 char *result;
474 ++buf;
475 while ((result = KeyValues_FindKeyValue(buf, bufend, NULL)) != NULL)
476 func(result);
477 }
478
479 Xfree(bufstart);
480 }
481
Paths_ParseXDGDesktopFile(const char * fn,PathsParseFunc func)482 void Paths_ParseXDGDesktopFile(const char * fn, PathsParseFunc func)
483 {
484 buildvfs_fd const fd = buildvfs_open_read(fn);
485 if (fd == buildvfs_fd_invalid)
486 return;
487
488 int32_t size = buildvfs_length(fd);
489 if (size <= 0)
490 return;
491
492 auto const bufstart = (char *)Xmalloc(size+1);
493 char * buf = bufstart;
494 size = (int32_t)buildvfs_read(fd, buf, size);
495 buildvfs_close(fd);
496 char * const bufend = buf + size;
497 bufend[0] = '\0';
498
499 static char const s_PathEquals[] = "Path=";
500
501 while (buf < bufend)
502 {
503 if (Bstrncmp(buf, s_PathEquals, ARRAY_SIZE(s_PathEquals)-1) == 0)
504 {
505 const char * path = buf += ARRAY_SIZE(s_PathEquals)-1;
506
507 while (buf < bufend && *buf != '\n')
508 ++buf;
509 *buf = '\0';
510
511 func(path);
512
513 break;
514 }
515
516 while (buf < bufend && *buf++ != '\n') { }
517 }
518
519 Xfree(bufstart);
520 }
521
Paths_ParseXDGDesktopFilesFromGOG(const char * homepath,const char * game,PathsParseFunc func)522 void Paths_ParseXDGDesktopFilesFromGOG(const char * homepath, const char * game, PathsParseFunc func)
523 {
524 static char const * const locations[] = { ".local/share/applications", "Desktop" };
525 char buf[BMAX_PATH];
526
527 for (char const * location : locations)
528 {
529 Bsnprintf(buf, sizeof(buf), "%s/%s/gog_com-%s_1.desktop", homepath, location, game);
530 Paths_ParseXDGDesktopFile(buf, func);
531 }
532 }
533