1 /*
2 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3 * It is copyright by its individual contributors, as recorded in the
4 * project's Git history. See COPYING.txt at the top level for license
5 * terms and a link to the Git history.
6 */
7 /*
8 *
9 * Some simple physfs extensions
10 *
11 */
12
13 #include <cstdlib>
14 #if !defined(macintosh) && !defined(_MSC_VER)
15 #include <sys/param.h>
16 #endif
17 #if defined(__APPLE__) && defined(__MACH__)
18 #include <sys/mount.h>
19 #include <unistd.h> // for chdir hack
20 #include <ApplicationServices/ApplicationServices.h>
21 #endif
22
23 #include "args.h"
24 #include "newdemo.h"
25 #include "console.h"
26 #include "strutil.h"
27 #include "ignorecase.h"
28 #include "physfs_list.h"
29
30 #include "compiler-range_for.h"
31 #include "compiler-poison.h"
32 #include "partial_range.h"
33
34 namespace dcx {
35
36 const std::array<file_extension_t, 1> archive_exts{{"dxa"}};
37
get(char * const buf,std::size_t n,PHYSFS_File * const fp)38 char *PHYSFSX_fgets_t::get(char *const buf, std::size_t n, PHYSFS_File *const fp)
39 {
40 PHYSFS_sint64 r = PHYSFS_read(fp, buf, sizeof(*buf), n - 1);
41 if (r <= 0)
42 return DXX_POISON_MEMORY(buf, buf + n, 0xcc), nullptr;
43 char *p = buf;
44 const auto cleanup = [&]{
45 return *p = 0, DXX_POISON_MEMORY(p + 1, buf + n, 0xcc), p;
46 };
47 char *const e = &buf[r];
48 for (;;)
49 {
50 if (p == e)
51 {
52 return cleanup();
53 }
54 char c = *p;
55 if (c == 0)
56 break;
57 if (c == '\n')
58 {
59 break;
60 }
61 else if (c == '\r')
62 {
63 *p = 0;
64 if (++p != e && *p != '\n')
65 --p;
66 break;
67 }
68 ++p;
69 }
70 PHYSFS_seek(fp, PHYSFS_tell(fp) + p - e + 1);
71 return cleanup();
72 }
73
PHYSFSX_checkMatchingExtension(const char * filename,const partial_range_t<const file_extension_t * > range)74 int PHYSFSX_checkMatchingExtension(const char *filename, const partial_range_t<const file_extension_t *> range)
75 {
76 const char *ext = strrchr(filename, '.');
77 if (!ext)
78 return 0;
79 ++ext;
80 // see if the file is of a type we want
81 range_for (auto &k, range)
82 {
83 if (!d_stricmp(ext, k))
84 return 1;
85 }
86 return 0;
87 }
88
89 }
90
91 namespace dsx {
92
93 // Initialise PhysicsFS, set up basic search paths and add arguments from .ini file.
94 // The .ini file can be in either the user directory or the same directory as the program.
95 // The user directory is searched first.
PHYSFSX_init(int argc,char * argv[])96 bool PHYSFSX_init(int argc, char *argv[])
97 {
98 #if defined(__unix__) || defined(__APPLE__) || defined(__MACH__)
99 char fullPath[PATH_MAX + 5];
100 #endif
101 #ifdef macintosh // Mac OS 9
102 char base_dir[PATH_MAX];
103 int bundle = 0;
104 #else
105 #define base_dir PHYSFS_getBaseDir()
106 #endif
107
108 if (!PHYSFS_init(argv[0]))
109 Error("Failed to init PhysFS: %s", PHYSFS_getLastError());
110 PHYSFS_permitSymbolicLinks(1);
111
112 #ifdef macintosh
113 strcpy(base_dir, PHYSFS_getBaseDir());
114 if (strstr(base_dir, ".app:Contents:MacOSClassic")) // the Mac OS 9 program is still in the .app bundle
115 {
116 char *p;
117
118 bundle = 1;
119 if (base_dir[strlen(base_dir) - 1] == ':')
120 base_dir[strlen(base_dir) - 1] = '\0';
121 p = strrchr(base_dir, ':'); *p = '\0'; // path to 'Contents'
122 p = strrchr(base_dir, ':'); *p = '\0'; // path to bundle
123 p = strrchr(base_dir, ':'); *p = '\0'; // path to directory containing bundle
124 }
125 #endif
126
127 #if (defined(__APPLE__) && defined(__MACH__)) // others?
128 chdir(base_dir); // make sure relative hogdir paths work
129 #endif
130
131 const char *writedir;
132 #if defined(__unix__)
133 #if defined(DXX_BUILD_DESCENT_I)
134 #define DESCENT_PATH_NUMBER "1"
135 #elif defined(DXX_BUILD_DESCENT_II)
136 #define DESCENT_PATH_NUMBER "2"
137 #endif
138 const auto &home_environ_var = "D" DESCENT_PATH_NUMBER "X_REBIRTH_HOME";
139 const char *path = getenv(home_environ_var);
140 if (!path)
141 {
142 path = getenv(&home_environ_var[4]);
143 if (!path)
144 # if !(defined(__APPLE__) && defined(__MACH__))
145 path = "~/.d" DESCENT_PATH_NUMBER "x-rebirth/";
146 # else
147 path = "~/Library/Preferences/D" DESCENT_PATH_NUMBER "X Rebirth/";
148 # endif
149 }
150
151 if (path[0] == '~') // yes, this tilde can be put before non-unix paths.
152 {
153 const char *home = PHYSFS_getUserDir();
154 path++;
155 // prepend home to the path
156 if (*path == *PHYSFS_getDirSeparator())
157 path++;
158 snprintf(fullPath, sizeof(fullPath), "%s%s", home, path);
159 }
160 else
161 {
162 fullPath[sizeof(fullPath) - 1] = 0;
163 strncpy(fullPath, path, sizeof(fullPath) - 1);
164 }
165
166 PHYSFS_setWriteDir(fullPath);
167 if (!(writedir = PHYSFS_getWriteDir()))
168 { // need to make it
169 char *p;
170 char ancestor[PATH_MAX + 5]; // the directory which actually exists
171 char child[PATH_MAX + 5]; // the directory relative to the above we're trying to make
172
173 strcpy(ancestor, fullPath);
174 const auto separator = *PHYSFS_getDirSeparator();
175 while (!PHYSFS_getWriteDir() && (p = strrchr(ancestor, separator)))
176 {
177 if (p[1] == 0)
178 { // separator at the end (intended here, for safety)
179 *p = 0; // kill this separator
180 if (!(p = strrchr(ancestor, separator)))
181 break; // give up, this is (usually) the root directory
182 }
183
184 p[1] = 0; // go to parent
185 PHYSFS_setWriteDir(ancestor);
186 }
187
188 strcpy(child, fullPath + strlen(ancestor));
189 if (separator != '/')
190 for (p = child; (p = strchr(p, separator)); p++)
191 *p = '/';
192 PHYSFS_mkdir(child);
193 PHYSFS_setWriteDir(fullPath);
194 writedir = PHYSFS_getWriteDir();
195 }
196 con_printf(CON_DEBUG, "PHYSFS: append write directory \"%s\" to search path", writedir);
197 PHYSFS_mount(writedir, nullptr, 1);
198 #endif
199 con_printf(CON_DEBUG, "PHYSFS: temporarily append base directory \"%s\" to search path", base_dir);
200 PHYSFS_mount(base_dir, nullptr, 1);
201 if (!InitArgs( argc,argv ))
202 return false;
203 PHYSFS_unmount(base_dir);
204
205 if (!PHYSFS_getWriteDir())
206 {
207 PHYSFS_setWriteDir(base_dir);
208 if (!(writedir = PHYSFS_getWriteDir()))
209 Error("can't set write dir: %s\n", PHYSFS_getLastError());
210 PHYSFS_mount(writedir, nullptr, 0);
211 }
212
213 //tell PHYSFS where hogdir is
214 if (!CGameArg.SysHogDir.empty())
215 {
216 const auto p = CGameArg.SysHogDir.c_str();
217 con_printf(CON_DEBUG, "PHYSFS: append argument hog directory \"%s\" to search path", p);
218 PHYSFS_mount(p, nullptr, 1);
219 }
220 #if DXX_USE_SHAREPATH
221 else if (!GameArg.SysNoHogDir)
222 {
223 con_puts(CON_DEBUG, "PHYSFS: append sharepath directory \"" DXX_SHAREPATH "\" to search path");
224 PHYSFS_mount(DXX_SHAREPATH, nullptr, 1);
225 }
226 else
227 {
228 con_puts(CON_DEBUG, "PHYSFS: skipping built-in sharepath \"" DXX_SHAREPATH "\"");
229 }
230 #else
231 else
232 {
233 con_puts(CON_DEBUG, "PHYSFS: no built-in sharepath");
234 }
235 #endif
236
237 std::array<char, PATH_MAX> pathname;
238 PHYSFSX_addRelToSearchPath("data", pathname, physfs_search_path::append); // 'Data' subdirectory
239
240 // For Macintosh, add the 'Resources' directory in the .app bundle to the searchpaths
241 #if defined(__APPLE__) && defined(__MACH__)
242 {
243 CFBundleRef mainBundle = CFBundleGetMainBundle();
244 if (mainBundle)
245 {
246 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
247 if (resourcesURL)
248 {
249 if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, reinterpret_cast<uint8_t *>(fullPath), sizeof(fullPath)))
250 {
251 con_printf(CON_DEBUG, "PHYSFS: append resources directory \"%s\" to search path", fullPath);
252 PHYSFS_mount(fullPath, nullptr, 1);
253 }
254
255 CFRelease(resourcesURL);
256 }
257 }
258 }
259 #elif defined(macintosh)
260 if (bundle)
261 {
262 base_dir[strlen(base_dir)] = ':'; // go back in the bundle
263 base_dir[strlen(base_dir)] = ':'; // go back in 'Contents'
264 strncat(base_dir, ":Resources:", PATH_MAX - 1 - strlen(base_dir));
265 base_dir[PATH_MAX - 1] = '\0';
266 con_printf(CON_DEBUG, "PHYSFS: append bundle directory \"%s\" to search path", base_dir);
267 PHYSFS_mount(base_dir, nullptr, 1);
268 }
269 #endif
270 return true;
271 }
272
273 #if defined(DXX_BUILD_DESCENT_II)
make_PHYSFSX_ComputedPathMount(const char * const name1,const char * const name2,physfs_search_path position)274 RAIIPHYSFS_ComputedPathMount make_PHYSFSX_ComputedPathMount(const char *const name1, const char *const name2, physfs_search_path position)
275 {
276 auto pathname = std::make_unique<std::array<char, PATH_MAX>>();
277 if (PHYSFSX_addRelToSearchPath(name1, *pathname.get(), position) == PHYSFS_ERR_OK ||
278 PHYSFSX_addRelToSearchPath(name2, *pathname.get(), position) == PHYSFS_ERR_OK)
279 return RAIIPHYSFS_ComputedPathMount(std::move(pathname));
280 return nullptr;
281 }
282 #endif
283
284 }
285
286 namespace dcx {
287
288 // Add a searchpath, but that searchpath is relative to an existing searchpath
289 // It will add the first one it finds and return 1, if it doesn't find any it returns 0
PHYSFSX_addRelToSearchPath(const char * relname,std::array<char,PATH_MAX> & pathname,physfs_search_path add_to_end)290 PHYSFS_ErrorCode PHYSFSX_addRelToSearchPath(const char *relname, std::array<char, PATH_MAX> &pathname, physfs_search_path add_to_end)
291 {
292 char relname2[PATH_MAX];
293
294 snprintf(relname2, sizeof(relname2), "%s", relname);
295 PHYSFSEXT_locateCorrectCase(relname2);
296
297 if (!PHYSFSX_getRealPath(relname2, pathname))
298 {
299 /* This failure is not reported as an error, because callers
300 * probe for files that users may not have, and do not need.
301 */
302 con_printf(CON_DEBUG, "PHYSFS: ignoring map request: no canonical path for relative name \"%s\"", relname2);
303 return PHYSFS_ERR_OK;
304 }
305
306 auto r = PHYSFS_mount(pathname.data(), nullptr, static_cast<int>(add_to_end));
307 const auto action = add_to_end != physfs_search_path::prepend ? "append" : "insert";
308 if (r)
309 {
310 con_printf(CON_DEBUG, "PHYSFS: %s canonical directory \"%s\" to search path from relative name \"%s\"", action, pathname.data(), relname);
311 return PHYSFS_ERR_OK;
312 }
313 else
314 {
315 const auto err = PHYSFS_getLastErrorCode();
316 con_printf(CON_VERBOSE, "PHYSFS: failed to %s canonical directory \"%s\" to search path from relative name \"%s\": \"%s\"", action, pathname.data(), relname, PHYSFS_getErrorByCode(err));
317 return err;
318 }
319 }
320
PHYSFSX_removeRelFromSearchPath(const char * relname)321 void PHYSFSX_removeRelFromSearchPath(const char *relname)
322 {
323 char relname2[PATH_MAX];
324
325 snprintf(relname2, sizeof(relname2), "%s", relname);
326 PHYSFSEXT_locateCorrectCase(relname2);
327
328 std::array<char, PATH_MAX> pathname;
329 if (!PHYSFSX_getRealPath(relname2, pathname))
330 {
331 con_printf(CON_DEBUG, "PHYSFS: ignoring unmap request: no canonical path for relative name \"%s\"", relname2);
332 return;
333 }
334 auto r = PHYSFS_unmount(pathname.data());
335 if (r)
336 con_printf(CON_DEBUG, "PHYSFS: unmap canonical directory \"%s\" (relative name \"%s\")", pathname.data(), relname);
337 else
338 con_printf(CON_VERBOSE, "PHYSFS: failed to unmap canonical directory \"%s\" (relative name \"%s\"): \"%s\"", pathname.data(), relname, PHYSFS_getLastError());
339 }
340
PHYSFSX_fsize(const char * hogname)341 int PHYSFSX_fsize(const char *hogname)
342 {
343 char hogname2[PATH_MAX];
344
345 snprintf(hogname2, sizeof(hogname2), "%s", hogname);
346 PHYSFSEXT_locateCorrectCase(hogname2);
347
348 if (RAIIPHYSFS_File fp{PHYSFS_openRead(hogname2)})
349 return PHYSFS_fileLength(fp);
350 return -1;
351 }
352
PHYSFSX_listSearchPathContent()353 void PHYSFSX_listSearchPathContent()
354 {
355 con_puts(CON_DEBUG, "PHYSFS: Listing contents of Search Path.");
356 PHYSFSX_uncounted_list list{PHYSFS_getSearchPath()};
357 range_for (const auto i, list)
358 con_printf(CON_DEBUG, "PHYSFS: [%s] is in the Search Path.", i);
359 list.reset();
360 list.reset(PHYSFS_enumerateFiles(""));
361 range_for (const auto i, list)
362 con_printf(CON_DEBUG, "PHYSFS: * We've got [%s].", i);
363 }
364
365 }
366
367 namespace dsx {
368
369 // checks which archives are supported by PhysFS. Return 0 if some essential (HOG) is not supported
PHYSFSX_checkSupportedArchiveTypes()370 int PHYSFSX_checkSupportedArchiveTypes()
371 {
372 int hog_sup = 0;
373 #ifdef DXX_BUILD_DESCENT_II
374 int mvl_sup = 0;
375 #endif
376
377 con_puts(CON_DEBUG, "PHYSFS: Checking supported archive types.");
378 range_for (const auto i, make_null_sentinel_array(PHYSFS_supportedArchiveTypes()))
379 {
380 const auto iextension = i->extension;
381 con_printf(CON_DEBUG, "PHYSFS: Supported archive: [%s], which is [%s].", iextension, i->description);
382 if (!d_stricmp(iextension, "HOG"))
383 hog_sup = 1;
384 #ifdef DXX_BUILD_DESCENT_II
385 else if (!d_stricmp(iextension, "MVL"))
386 mvl_sup = 1;
387 #endif
388 }
389
390 if (!hog_sup)
391 con_puts(CON_CRITICAL, "PHYSFS: HOG not supported. The game will not work without!");
392 #ifdef DXX_BUILD_DESCENT_II
393 if (!mvl_sup)
394 con_puts(CON_URGENT, "PHYSFS: MVL not supported. Won't be able to play movies!");
395 #endif
396
397 return hog_sup;
398 }
399
400 }
401
402 namespace dcx {
403
PHYSFSX_getRealPath(const char * stdPath,std::array<char,PATH_MAX> & realPath)404 int PHYSFSX_getRealPath(const char *stdPath, std::array<char, PATH_MAX> &realPath)
405 {
406 DXX_POISON_MEMORY(realPath.data(), realPath.size(), 0xdd);
407 const char *realDir = PHYSFS_getRealDir(stdPath);
408 if (!realDir)
409 {
410 realDir = PHYSFS_getWriteDir();
411 if (!realDir)
412 return 0;
413 }
414 const auto realDirSize = strlen(realDir);
415 if (realDirSize >= realPath.size())
416 return 0;
417 auto mountpoint = PHYSFS_getMountPoint(realDir);
418 if (!mountpoint)
419 return 0;
420 std::copy_n(realDir, realDirSize + 1, realPath.begin());
421 #ifdef _unix__
422 auto &sep = "/";
423 assert(!strcmp(PHYSFS_getDirSeparator(), sep));
424 #else
425 const auto sep = PHYSFS_getDirSeparator();
426 #endif
427 const auto sepSize = strlen(sep);
428 auto realPathUsed = realDirSize;
429 if (realDirSize >= sepSize)
430 {
431 const auto p = std::next(realPath.begin(), realDirSize - sepSize);
432 if (strcmp(p, sep)) // no sep at end of realPath
433 {
434 realPathUsed += sepSize;
435 std::copy_n(sep, sepSize, &realPath[realDirSize]);
436 }
437 }
438 if (*mountpoint == '/')
439 ++mountpoint;
440 if (*stdPath == '/')
441 ++stdPath;
442 const auto ml = strlen(mountpoint);
443 if (!strncmp(mountpoint, stdPath, ml))
444 stdPath += ml;
445 else
446 {
447 /* Virtual path is not under the virtual mount point that
448 * provides the path.
449 */
450 assert(false);
451 }
452 const auto stdPathLen = strlen(stdPath) + 1;
453 if (realPathUsed + stdPathLen >= realPath.size())
454 return 0;
455 #ifdef __unix__
456 /* Separator is "/" and physfs internal separator is "/". Copy
457 * through.
458 */
459 std::copy_n(stdPath, stdPathLen, &realPath[realPathUsed]);
460 #else
461 /* Separator might be / on non-unix, but the fallback path works
462 * regardless of whether separator is "/".
463 */
464 const auto csep = *sep;
465 const auto a = [csep](char c) {
466 return c == '/' ? csep : c;
467 };
468 std::transform(stdPath, &stdPath[stdPathLen], &realPath[realPathUsed], a);
469 #endif
470 return 1;
471 }
472
PHYSFSX_rename(const char * oldpath,const char * newpath)473 int PHYSFSX_rename(const char *oldpath, const char *newpath)
474 {
475 std::array<char, PATH_MAX> old, n;
476 PHYSFSX_getRealPath(oldpath, old);
477 PHYSFSX_getRealPath(newpath, n);
478 return (rename(old.data(), n.data()) == 0);
479 }
480
481 template <typename F>
PHYSFSX_findPredicateFiles(const char * path,F f)482 static inline PHYSFSX_uncounted_list PHYSFSX_findPredicateFiles(const char *path, F f)
483 {
484 PHYSFSX_uncounted_list list{PHYSFS_enumerateFiles(path)};
485 if (!list)
486 return nullptr; // out of memory: not so good
487 char **j = list.get();
488 range_for (const auto i, list)
489 {
490 if (f(i))
491 *j++ = i;
492 else
493 free(i);
494 }
495 *j = NULL;
496 char **r = reinterpret_cast<char **>(realloc(list.get(), (j - list.get() + 1)*sizeof(char *))); // save a bit of memory (or a lot?)
497 if (r)
498 {
499 list.release();
500 list.reset(r);
501 }
502 return list;
503 }
504
505 // Find files at path that have an extension listed in exts
506 // The extension list exts must be NULL-terminated, with each ext beginning with a '.'
PHYSFSX_findFiles(const char * path,const partial_range_t<const file_extension_t * > exts)507 PHYSFSX_uncounted_list PHYSFSX_findFiles(const char *path, const partial_range_t<const file_extension_t *> exts)
508 {
509 const auto predicate = [&](const char *i) {
510 return PHYSFSX_checkMatchingExtension(i, exts);
511 };
512 return PHYSFSX_findPredicateFiles(path, predicate);
513 }
514
515 // Same function as above but takes a real directory as second argument, only adding files originating from this directory.
516 // This can be used to further seperate files in search path but it must be made sure realpath is properly formatted.
PHYSFSX_findabsoluteFiles(const char * path,const char * realpath,const partial_range_t<const file_extension_t * > exts)517 PHYSFSX_uncounted_list PHYSFSX_findabsoluteFiles(const char *path, const char *realpath, const partial_range_t<const file_extension_t *> exts)
518 {
519 const auto predicate = [&](const char *i) {
520 return PHYSFSX_checkMatchingExtension(i, exts) && (!strcmp(PHYSFS_getRealDir(i), realpath));
521 };
522 return PHYSFSX_findPredicateFiles(path, predicate);
523 }
524
PHYSFSX_exists_ignorecase(const char * filename)525 int PHYSFSX_exists_ignorecase(const char *filename)
526 {
527 char filename2[PATH_MAX];
528 snprintf(filename2, sizeof(filename2), "%s", filename);
529 return !PHYSFSEXT_locateCorrectCase(filename2);
530 }
531
532 //Open a file for reading, set up a buffer
PHYSFSX_openReadBuffered(const char * filename)533 std::pair<RAIIPHYSFS_File, PHYSFS_ErrorCode> PHYSFSX_openReadBuffered(const char *filename)
534 {
535 PHYSFS_uint64 bufSize;
536 char filename2[PATH_MAX];
537 #if 0
538 if (filename[0] == '\x01')
539 {
540 //FIXME: don't look in dir, only in hogfile
541 filename++;
542 }
543 #endif
544 snprintf(filename2, sizeof(filename2), "%s", filename);
545 PHYSFSEXT_locateCorrectCase(filename2);
546
547 RAIIPHYSFS_File fp{PHYSFS_openRead(filename2)};
548 if (!fp)
549 return {nullptr, PHYSFS_getLastErrorCode()};
550
551 bufSize = PHYSFS_fileLength(fp);
552 while (!PHYSFS_setBuffer(fp, bufSize) && bufSize)
553 bufSize /= 2; // even if the error isn't memory full, for a 20MB file it'll only do this 8 times
554 return {std::move(fp), PHYSFS_ERR_OK};
555 }
556
557 //Open a file for writing, set up a buffer
PHYSFSX_openWriteBuffered(const char * filename)558 std::pair<RAIIPHYSFS_File, PHYSFS_ErrorCode> PHYSFSX_openWriteBuffered(const char *filename)
559 {
560 PHYSFS_uint64 bufSize = 1024*1024; // hmm, seems like an OK size.
561
562 RAIIPHYSFS_File fp{PHYSFS_openWrite(filename)};
563 if (!fp)
564 return {nullptr, PHYSFS_getLastErrorCode()};
565 while (!PHYSFS_setBuffer(fp, bufSize) && bufSize)
566 bufSize /= 2;
567 return {std::move(fp), PHYSFS_ERR_OK};
568 }
569
570 /*
571 * Add archives to the game.
572 * 1) archives from Sharepath/Data to extend/replace builtin game content
573 * 2) archived demos
574 */
PHYSFSX_addArchiveContent()575 void PHYSFSX_addArchiveContent()
576 {
577 int content_updated = 0;
578
579 con_puts(CON_DEBUG, "PHYSFS: Adding archives to the game.");
580 // find files in Searchpath ...
581 auto list = PHYSFSX_findFiles("", archive_exts);
582 // if found, add them...
583 range_for (const auto i, list)
584 {
585 std::array<char, PATH_MAX> realfile;
586 PHYSFSX_getRealPath(i,realfile);
587 if (PHYSFS_mount(realfile.data(), nullptr, 0))
588 {
589 con_printf(CON_DEBUG, "PHYSFS: Added %s to Search Path",realfile.data());
590 content_updated = 1;
591 }
592 }
593 #if PHYSFS_VER_MAJOR >= 2
594 list.reset();
595 // find files in DEMO_DIR ...
596 list = PHYSFSX_findFiles(DEMO_DIR, archive_exts);
597 // if found, add them...
598 range_for (const auto i, list)
599 {
600 char demofile[PATH_MAX];
601 snprintf(demofile, sizeof(demofile), DEMO_DIR "%s", i);
602 std::array<char, PATH_MAX> realfile;
603 PHYSFSX_getRealPath(demofile,realfile);
604 if (PHYSFS_mount(realfile.data(), DEMO_DIR, 0))
605 {
606 con_printf(CON_DEBUG, "PHYSFS: Added %s to " DEMO_DIR, realfile.data());
607 content_updated = 1;
608 }
609 }
610 #endif
611 list.reset();
612
613 if (content_updated)
614 {
615 con_puts(CON_DEBUG, "Game content updated!");
616 PHYSFSX_listSearchPathContent();
617 }
618 }
619
620 // Removes content added above when quitting game
PHYSFSX_removeArchiveContent()621 void PHYSFSX_removeArchiveContent()
622 {
623 // find files in Searchpath ...
624 auto list = PHYSFSX_findFiles("", archive_exts);
625 // if found, remove them...
626 range_for (const auto i, list)
627 {
628 std::array<char, PATH_MAX> realfile;
629 PHYSFSX_getRealPath(i, realfile);
630 PHYSFS_unmount(realfile.data());
631 }
632 list.reset();
633 // find files in DEMO_DIR ...
634 list = PHYSFSX_findFiles(DEMO_DIR, archive_exts);
635 // if found, remove them...
636 range_for (const auto i, list)
637 {
638 char demofile[PATH_MAX];
639 snprintf(demofile, sizeof(demofile), DEMO_DIR "%s", i);
640 std::array<char, PATH_MAX> realfile;
641 PHYSFSX_getRealPath(demofile,realfile);
642 PHYSFS_unmount(realfile.data());
643 }
644 }
645
PHYSFSX_read_helper_report_error(const char * const filename,const unsigned line,const char * const func,PHYSFS_File * const file)646 void PHYSFSX_read_helper_report_error(const char *const filename, const unsigned line, const char *const func, PHYSFS_File *const file)
647 {
648 (Error)(filename, line, func, "reading at %lu", static_cast<unsigned long>((PHYSFS_tell)(file)));
649 }
650
make_PHYSFSX_ComputedPathMount(const char * const name,physfs_search_path position)651 RAIIPHYSFS_ComputedPathMount make_PHYSFSX_ComputedPathMount(const char *const name, physfs_search_path position)
652 {
653 auto pathname = std::make_unique<std::array<char, PATH_MAX>>();
654 if (PHYSFSX_addRelToSearchPath(name, *pathname.get(), position) == PHYSFS_ERR_OK)
655 return RAIIPHYSFS_ComputedPathMount(std::move(pathname));
656 return nullptr;
657 }
658
659 }
660