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