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