/ source / blood / src / common.cpp
common.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2010-2019 EDuke32 developers and contributors
  4  Copyright (C) 2019 Nuke.YKT
  5  
  6  This file is part of NBlood.
  7  
  8  NBlood is free software; you can redistribute it and/or
  9  modify it under the terms of the GNU General Public License version 2
 10  as published by the Free Software Foundation.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  */
 22  //-------------------------------------------------------------------------
 23  
 24  //
 25  // Common non-engine code/data for EDuke32 and Mapster32
 26  //
 27  
 28  #include "compat.h"
 29  #include "build.h"
 30  #include "baselayer.h"
 31  #include "palette.h"
 32  #include "texcache.h"
 33  
 34  #ifdef _WIN32
 35  # include "windows_inc.h"
 36  # include "winbits.h"
 37  #elif defined __APPLE__
 38  # include "osxbits.h"
 39  #endif
 40  
 41  #include "common.h"
 42  #include "common_game.h"
 43  
 44  // g_grpNamePtr can ONLY point to a malloc'd block (length BMAX_PATH)
 45  char *g_grpNamePtr = NULL;
 46  
 47  void clearGrpNamePtr(void)
 48  {
 49      Xfree(g_grpNamePtr);
 50      // g_grpNamePtr assumed to be assigned to right after
 51  }
 52  
 53  const char *G_DefaultGrpFile(void)
 54  {
 55      return APPBASENAME ".pk3";
 56  }
 57  
 58  const char *G_DefaultDefFile(void)
 59  {
 60      return "blood.def";
 61  }
 62  
 63  const char *G_GrpFile(void)
 64  {
 65      return (g_grpNamePtr == NULL) ? G_DefaultGrpFile() : g_grpNamePtr;
 66  }
 67  
 68  const char *G_DefFile(void)
 69  {
 70      return (g_defNamePtr == NULL) ? G_DefaultDefFile() : g_defNamePtr;
 71  }
 72  
 73  
 74  void G_SetupGlobalPsky(void)
 75  {
 76      int skyIdx = 0;
 77  
 78      // NOTE: Loop must be running backwards for the same behavior as the game
 79      // (greatest sector index with matching parallaxed sky takes precedence).
 80      for (bssize_t i = numsectors - 1; i >= 0; i--)
 81      {
 82          if (sector[i].ceilingstat & 1)
 83          {
 84              skyIdx = getpskyidx(sector[i].ceilingpicnum);
 85              if (skyIdx > 0)
 86                  break;
 87          }
 88      }
 89  
 90      g_pskyidx = skyIdx;
 91  }
 92  
 93  static char g_rootDir[BMAX_PATH];
 94  
 95  int g_useCwd;
 96  int32_t g_groupFileHandle;
 97  
 98  static struct strllist *CommandPaths, *CommandGrps;
 99  
100  void G_ExtPreInit(int32_t argc,char const * const * argv)
101  {
102      g_useCwd = G_CheckCmdSwitch(argc, argv, "-usecwd");
103  
104  #ifdef _WIN32
105      GetModuleFileName(NULL,g_rootDir,BMAX_PATH);
106      Bcorrectfilename(g_rootDir,1);
107      //chdir(g_rootDir);
108  #else
109      getcwd(g_rootDir,BMAX_PATH);
110      strcat(g_rootDir,"/");
111  #endif
112  }
113  
114  void G_ExtInit(void)
115  {
116      char cwd[BMAX_PATH];
117  
118  #ifdef EDUKE32_OSX
119      char *appdir = Bgetappdir();
120      addsearchpath(appdir);
121      Xfree(appdir);
122  #endif
123  
124      if (getcwd(cwd,BMAX_PATH) && Bstrcmp(cwd,"/") != 0)
125          addsearchpath(cwd);
126  
127      if (CommandPaths)
128      {
129          int32_t i;
130          struct strllist *s;
131          while (CommandPaths)
132          {
133              s = CommandPaths->next;
134              i = addsearchpath(CommandPaths->str);
135              if (i < 0)
136              {
137                  LOG_F(ERROR, "Failed adding %s for game data: %s", CommandPaths->str,
138                             i==-1 ? "not a directory" : "no such directory");
139              }
140  
141              Xfree(CommandPaths->str);
142              Xfree(CommandPaths);
143              CommandPaths = s;
144          }
145      }
146  
147  #if defined(_WIN32)
148      if (!access("user_profiles_enabled", F_OK))
149  #else
150      if (g_useCwd == 0 && access("user_profiles_disabled", F_OK))
151  #endif
152      {
153          char *homedir;
154          int32_t asperr;
155  
156          if ((homedir = Bgethomedir()))
157          {
158              Bsnprintf(cwd,sizeof(cwd),"%s/"
159  #if defined(_WIN32)
160                        APPNAME
161  #elif defined(GEKKO)
162                        "apps/" APPBASENAME
163  #else
164                        ".config/" APPBASENAME
165  #endif
166                        ,homedir);
167              asperr = addsearchpath(cwd);
168              if (asperr == -2)
169              {
170                  if (Bmkdir(cwd,S_IRWXU) == 0) asperr = addsearchpath(cwd);
171                  else asperr = -1;
172              }
173              if (asperr == 0)
174                  Bchdir(cwd);
175              Xfree(homedir);
176          }
177      }
178  }
179  
180  static int32_t G_TryLoadingGrp(char const * const grpfile)
181  {
182      int32_t i;
183  
184      if ((i = initgroupfile(grpfile)) == -1)
185          LOG_F(WARNING, "Could not find main data file \"%s\"!", grpfile);
186      else
187          LOG_F(INFO, "Using \"%s\" as main game data file.", grpfile);
188  
189      return i;
190  }
191  
192  void G_LoadGroups(int32_t autoload)
193  {
194      if (g_modDir[0] != '/')
195      {
196          char cwd[BMAX_PATH];
197  
198          Bstrcat(g_rootDir, g_modDir);
199          addsearchpath(g_rootDir);
200          //        addsearchpath(mod_dir);
201  
202          char path[BMAX_PATH];
203  
204          if (getcwd(cwd, BMAX_PATH))
205          {
206              Bsnprintf(path, sizeof(path), "%s/%s", cwd, g_modDir);
207              if (!Bstrcmp(g_rootDir, path))
208              {
209                  if (addsearchpath(path) == -2)
210                      if (Bmkdir(path, S_IRWXU) == 0)
211                          addsearchpath(path);
212              }
213          }
214  
215  #ifdef USE_OPENGL
216          Bsnprintf(path, sizeof(path), "%s/%s", g_modDir, TEXCACHEFILE);
217          Bstrcpy(TEXCACHEFILE, path);
218  #endif
219      }
220      const char *grpfile = G_GrpFile();
221      G_TryLoadingGrp(grpfile);
222  
223      if (autoload)
224      {
225          G_LoadGroupsInDir("autoload");
226  
227          //if (i != -1)
228          //    G_DoAutoload(grpfile);
229      }
230  
231      if (g_modDir[0] != '/')
232          G_LoadGroupsInDir(g_modDir);
233  
234      if (g_defNamePtr == NULL)
235      {
236          const char *tmpptr = getenv("BLOODDEF");
237          if (tmpptr)
238          {
239              clearDefNamePtr();
240              g_defNamePtr = dup_filename(tmpptr);
241              LOG_F(INFO, "Using \"%s\" as definitions file", g_defNamePtr);
242          }
243      }
244  
245      loaddefinitions_game(BLOODWIDESCREENDEF, TRUE);
246      loaddefinitions_game(NOTBLOODDEF, TRUE);
247      loaddefinitions_game(G_DefFile(), TRUE);
248  
249      struct strllist *s;
250  
251      int const bakpathsearchmode = pathsearchmode;
252      pathsearchmode = 1;
253  
254      while (CommandGrps)
255      {
256          int32_t j;
257  
258          s = CommandGrps->next;
259  
260          if ((j = initgroupfile(CommandGrps->str)) == -1)
261              LOG_F(WARNING, "Could not find file \"%s\".", CommandGrps->str);
262          else
263          {
264              g_groupFileHandle = j;
265              LOG_F(INFO, "Using file \"%s\" as game data.", CommandGrps->str);
266              if (autoload)
267                  G_DoAutoload(CommandGrps->str);
268          }
269  
270          Xfree(CommandGrps->str);
271          Xfree(CommandGrps);
272          CommandGrps = s;
273      }
274      pathsearchmode = bakpathsearchmode;
275  }
276  
277  void G_AddSearchPaths(void)
278  {
279  #ifndef EDUKE32_TOUCH_DEVICES
280  #if defined __linux__ || defined EDUKE32_BSD
281      char buf[BMAX_PATH];
282      char *homepath = Bgethomedir();
283      const char *xdg_docs_path = getenv("XDG_DOCUMENTS_DIR");
284      const char *xdg_config_path = getenv("XDG_CONFIG_HOME");
285  
286      if (xdg_config_path) {
287          Bsnprintf(buf, sizeof(buf), "%s/" APPBASENAME, xdg_config_path);
288          addsearchpath(buf);
289      }
290  
291      if (xdg_docs_path) {
292          Bsnprintf(buf, sizeof(buf), "%s/" APPNAME, xdg_docs_path);
293          addsearchpath(buf);
294      }
295      else {
296          Bsnprintf(buf, sizeof(buf), "%s/Documents/" APPNAME, homepath);
297          addsearchpath(buf);
298      }
299  
300      Xfree(homepath);
301  
302      addsearchpath("/usr/share/games/" APPBASENAME);
303      addsearchpath("/usr/local/share/games/" APPBASENAME);
304      addsearchpath("/app/extensions/extra");
305  #elif defined EDUKE32_OSX
306      char buf[BMAX_PATH];
307      int32_t i;
308      char *applications[] = { osx_getapplicationsdir(0), osx_getapplicationsdir(1) };
309      char *support[] = { osx_getsupportdir(0), osx_getsupportdir(1) };
310  
311      for (i = 0; i < 2; i++)
312      {
313          Bsnprintf(buf, sizeof(buf), "%s/" APPNAME, support[i]);
314          addsearchpath(buf);
315      }
316  
317      for (i = 0; i < 2; i++)
318      {
319          Xfree(applications[i]);
320          Xfree(support[i]);
321      }
322  #endif
323  #endif
324  }
325  
326  void G_CleanupSearchPaths(void)
327  {
328      removesearchpaths_withuser(SEARCHPATH_REMOVE);
329  }
330  
331  //////////
332  
333  void G_AddGroup(const char *buffer)
334  {
335      char buf[BMAX_PATH];
336  
337      struct strllist *s = (struct strllist *)Xcalloc(1,sizeof(struct strllist));
338  
339      Bstrcpy(buf, buffer);
340  
341      if (Bstrchr(buf,'.') == 0)
342          Bstrcat(buf,".grp");
343  
344      s->str = Xstrdup(buf);
345  
346      if (CommandGrps)
347      {
348          struct strllist *t;
349          for (t = CommandGrps; t->next; t=t->next) ;
350          t->next = s;
351          return;
352      }
353      CommandGrps = s;
354  }
355  
356  void G_AddPath(const char *buffer)
357  {
358      struct strllist *s = (struct strllist *)Xcalloc(1,sizeof(struct strllist));
359      s->str = Xstrdup(buffer);
360  
361      if (CommandPaths)
362      {
363          struct strllist *t;
364          for (t = CommandPaths; t->next; t=t->next) ;
365          t->next = s;
366          return;
367      }
368      CommandPaths = s;
369  }
370  
371  //////////
372  
373  // loads all group (grp, zip, pk3/4) files in the given directory
374  void G_LoadGroupsInDir(const char *dirname)
375  {
376      static const char *extensions[] = { "*.grp", "*.zip", "*.ssi", "*.pk3", "*.pk4" };
377      char buf[BMAX_PATH];
378      fnlist_t fnlist = FNLIST_INITIALIZER;
379  
380      for (auto & extension : extensions)
381      {
382          BUILDVFS_FIND_REC *rec;
383  
384          fnlist_getnames(&fnlist, dirname, extension, -1, 0);
385  
386          for (rec=fnlist.findfiles; rec; rec=rec->next)
387          {
388              Bsnprintf(buf, sizeof(buf), "%s/%s", dirname, rec->name);
389              LOG_F(INFO, "Using group file \"%s\".", buf);
390              initgroupfile(buf);
391          }
392  
393          fnlist_clearnames(&fnlist);
394      }
395  }
396  
397  void G_DoAutoload(const char *dirname)
398  {
399      char buf[BMAX_PATH];
400  
401      Bsnprintf(buf, sizeof(buf), "autoload/%s", dirname);
402      G_LoadGroupsInDir(buf);
403  }
404  
405  //////////
406  
407  #ifdef FORMAT_UPGRADE_ELIGIBLE
408  
409  static int32_t S_TryFormats(char * const testfn, char * const fn_suffix, char const searchfirst)
410  {
411  #ifdef HAVE_FLAC
412      {
413          Bstrcpy(fn_suffix, ".flac");
414          int32_t const fp = kopen4loadfrommod(testfn, searchfirst);
415          if (fp >= 0)
416              return fp;
417      }
418  #endif
419  
420  #ifdef HAVE_VORBIS
421      {
422          Bstrcpy(fn_suffix, ".ogg");
423          int32_t const fp = kopen4loadfrommod(testfn, searchfirst);
424          if (fp >= 0)
425              return fp;
426      }
427  #endif
428  
429      return -1;
430  }
431  
432  static int32_t S_TryExtensionReplacements(char * const testfn, char const searchfirst, uint8_t const ismusic)
433  {
434      char * extension = Bstrrchr(testfn, '.');
435      char * const fn_end = Bstrchr(testfn, '\0');
436  
437      // ex: grabbag.voc --> grabbag_voc.*
438      if (extension != NULL)
439      {
440          *extension = '_';
441  
442          int32_t const fp = S_TryFormats(testfn, fn_end, searchfirst);
443          if (fp >= 0)
444              return fp;
445      }
446      else
447      {
448          extension = fn_end;
449      }
450  
451      // ex: grabbag.mid --> grabbag.*
452      if (ismusic)
453      {
454          int32_t const fp = S_TryFormats(testfn, extension, searchfirst);
455          if (fp >= 0)
456              return fp;
457      }
458  
459      return -1;
460  }
461  
462  int32_t S_OpenAudio(const char *fn, char searchfirst, uint8_t const ismusic)
463  {
464      int32_t const     origfp       = kopen4loadfrommod(fn, searchfirst);
465      char const *const origparent   = origfp != -1 ? kfileparent(origfp) : NULL;
466      uint32_t const    parentlength = origparent != NULL ? Bstrlen(origparent) : 0;
467  
468      auto testfn = (char *)Xmalloc(Bstrlen(fn) + 12 + parentlength); // "music/" + overestimation of parent minus extension + ".flac" + '\0'
469  
470      // look in ./
471      // ex: ./grabbag.mid
472      Bstrcpy(testfn, fn);
473      int32_t fp = S_TryExtensionReplacements(testfn, searchfirst, ismusic);
474      if (fp >= 0)
475          goto success;
476  
477      // look in ./music/<file's parent GRP name>/
478      // ex: ./music/duke3d/grabbag.mid
479      // ex: ./music/nwinter/grabbag.mid
480      if (origparent != NULL)
481      {
482          char const * const parentextension = Bstrrchr(origparent, '.');
483          uint32_t const namelength = parentextension != NULL ? (unsigned)(parentextension - origparent) : parentlength;
484  
485          Bsprintf(testfn, "music/%.*s/%s", namelength, origparent, fn);
486          fp = S_TryExtensionReplacements(testfn, searchfirst, ismusic);
487          if (fp >= 0)
488              goto success;
489      }
490  
491      // look in ./music/
492      // ex: ./music/grabbag.mid
493      Bsprintf(testfn, "music/%s", fn);
494      fp = S_TryExtensionReplacements(testfn, searchfirst, ismusic);
495      if (fp >= 0)
496          goto success;
497  
498      fp = origfp;
499  success:
500      Xfree(testfn);
501      if (fp != origfp)
502          kclose(origfp);
503  
504      return fp;
505  }
506  
507  #endif
508