Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 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 | #include <unistd.h> // Pierre-Marie Baty -- for getuid() |
||
23 | #include <pwd.h> // Pierre-Marie Baty -- for getpwuid() |
||
24 | |||
25 | #include "args.h" |
||
26 | #include "newdemo.h" |
||
27 | #include "console.h" |
||
28 | #include "strutil.h" |
||
29 | #include "ignorecase.h" |
||
30 | #include "physfs_list.h" |
||
31 | |||
32 | #include "compiler-range_for.h" |
||
33 | #include "compiler-poison.h" |
||
34 | #include "partial_range.h" |
||
35 | |||
36 | namespace dcx { |
||
37 | |||
38 | const std::array<file_extension_t, 1> archive_exts{{"dxa"}}; |
||
39 | |||
40 | char *PHYSFSX_fgets_t::get(char *const buf, std::size_t n, PHYSFS_File *const fp) |
||
41 | { |
||
42 | PHYSFS_sint64 r = PHYSFS_read(fp, buf, sizeof(*buf), n - 1); |
||
43 | if (r <= 0) |
||
44 | return DXX_POISON_MEMORY(buf, buf + n, 0xcc), nullptr; |
||
45 | char *p = buf; |
||
46 | const auto cleanup = [&]{ |
||
47 | return *p = 0, DXX_POISON_MEMORY(p + 1, buf + n, 0xcc), p; |
||
48 | }; |
||
49 | char *const e = &buf[r]; |
||
50 | for (;;) |
||
51 | { |
||
52 | if (p == e) |
||
53 | { |
||
54 | return cleanup(); |
||
55 | } |
||
56 | char c = *p; |
||
57 | if (c == 0) |
||
58 | break; |
||
59 | if (c == '\n') |
||
60 | { |
||
61 | break; |
||
62 | } |
||
63 | else if (c == '\r') |
||
64 | { |
||
65 | *p = 0; |
||
66 | if (++p != e && *p != '\n') |
||
67 | --p; |
||
68 | break; |
||
69 | } |
||
70 | ++p; |
||
71 | } |
||
72 | PHYSFS_seek(fp, PHYSFS_tell(fp) + p - e + 1); |
||
73 | return cleanup(); |
||
74 | } |
||
75 | |||
76 | int PHYSFSX_checkMatchingExtension(const char *filename, const partial_range_t<const file_extension_t *> range) |
||
77 | { |
||
78 | const char *ext = strrchr(filename, '.'); |
||
79 | if (!ext) |
||
80 | return 0; |
||
81 | ++ext; |
||
82 | // see if the file is of a type we want |
||
83 | range_for (auto &k, range) |
||
84 | { |
||
85 | if (!d_stricmp(ext, k)) |
||
86 | return 1; |
||
87 | } |
||
88 | return 0; |
||
89 | } |
||
90 | |||
91 | } |
||
92 | |||
93 | namespace dsx { |
||
94 | |||
95 | // Initialise PhysicsFS, set up basic search paths and add arguments from .ini file. |
||
96 | // The .ini file can be in either the user directory or the same directory as the program. |
||
97 | // The user directory is searched first. |
||
98 | bool PHYSFSX_init(int argc, char *argv[]) |
||
99 | { |
||
100 | #if defined(__unix__) || defined(__APPLE__) || defined(__MACH__) |
||
101 | char fullPath[PATH_MAX + 5]; |
||
102 | #endif |
||
103 | #ifdef macintosh // Mac OS 9 |
||
104 | char base_dir[PATH_MAX]; |
||
105 | int bundle = 0; |
||
106 | #else |
||
107 | #define base_dir PHYSFS_getBaseDir() |
||
108 | #endif |
||
109 | |||
110 | if (!PHYSFS_init(argv[0])) |
||
111 | Error("Failed to init PhysFS: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); // Pierre-Marie Baty -- work around PHYSFS_getLastError() deprecation |
||
112 | PHYSFS_permitSymbolicLinks(1); |
||
113 | |||
114 | #ifdef macintosh |
||
115 | strcpy(base_dir, PHYSFS_getBaseDir()); |
||
116 | if (strstr(base_dir, ".app:Contents:MacOSClassic")) // the Mac OS 9 program is still in the .app bundle |
||
117 | { |
||
118 | char *p; |
||
119 | |||
120 | bundle = 1; |
||
121 | if (base_dir[strlen(base_dir) - 1] == ':') |
||
122 | base_dir[strlen(base_dir) - 1] = '\0'; |
||
123 | p = strrchr(base_dir, ':'); *p = '\0'; // path to 'Contents' |
||
124 | p = strrchr(base_dir, ':'); *p = '\0'; // path to bundle |
||
125 | p = strrchr(base_dir, ':'); *p = '\0'; // path to directory containing bundle |
||
126 | } |
||
127 | #endif |
||
128 | |||
129 | #if (defined(__APPLE__) && defined(__MACH__)) // others? |
||
130 | chdir(base_dir); // make sure relative hogdir paths work |
||
131 | #endif |
||
132 | |||
133 | const char *writedir; |
||
134 | #if defined(__unix__) |
||
135 | #if defined(DXX_BUILD_DESCENT_I) |
||
136 | #define DESCENT_PATH_NUMBER "1" |
||
137 | #elif defined(DXX_BUILD_DESCENT_II) |
||
138 | #define DESCENT_PATH_NUMBER "2" |
||
139 | #endif |
||
140 | // const auto &home_environ_var = "D" DESCENT_PATH_NUMBER "X_REBIRTH_HOME"; |
||
141 | const char *path;// = getenv(home_environ_var); |
||
142 | // if (!path) |
||
143 | // { |
||
144 | // path = getenv(&home_environ_var[4]); |
||
145 | // if (!path) |
||
146 | # if !(defined(__APPLE__) && defined(__MACH__)) |
||
147 | path = "~/.d" DESCENT_PATH_NUMBER "x-rebirth/"; |
||
148 | # else |
||
149 | path = "~/Library/Preferences/Descent" DESCENT_PATH_NUMBER "/"; |
||
150 | # endif |
||
151 | // } |
||
152 | |||
153 | if (path[0] == '~') // yes, this tilde can be put before non-unix paths. |
||
154 | { |
||
155 | const char *home = /*PHYSFS_getUserDir()*/getpwuid(getuid())->pw_dir; // Pierre-Marie Baty -- work around PHYSFS_getUserDir() deprecation |
||
156 | path++; |
||
157 | // prepend home to the path |
||
158 | //if (*path == *PHYSFS_getDirSeparator()) |
||
159 | // path++; |
||
160 | snprintf(fullPath, sizeof(fullPath), "%s%s", home, path); |
||
161 | } |
||
162 | else |
||
163 | { |
||
164 | fullPath[sizeof(fullPath) - 1] = 0; |
||
165 | strncpy(fullPath, path, sizeof(fullPath) - 1); |
||
166 | } |
||
167 | |||
168 | PHYSFS_setWriteDir(fullPath); |
||
169 | if (!(writedir = PHYSFS_getWriteDir())) |
||
170 | { // need to make it |
||
171 | char *p; |
||
172 | char ancestor[PATH_MAX + 5]; // the directory which actually exists |
||
173 | char child[PATH_MAX + 5]; // the directory relative to the above we're trying to make |
||
174 | |||
175 | strcpy(ancestor, fullPath); |
||
176 | const auto separator = *PHYSFS_getDirSeparator(); |
||
177 | while (!PHYSFS_getWriteDir() && (p = strrchr(ancestor, separator))) |
||
178 | { |
||
179 | if (p[1] == 0) |
||
180 | { // separator at the end (intended here, for safety) |
||
181 | *p = 0; // kill this separator |
||
182 | if (!(p = strrchr(ancestor, separator))) |
||
183 | break; // give up, this is (usually) the root directory |
||
184 | } |
||
185 | |||
186 | p[1] = 0; // go to parent |
||
187 | PHYSFS_setWriteDir(ancestor); |
||
188 | } |
||
189 | |||
190 | strcpy(child, fullPath + strlen(ancestor)); |
||
191 | if (separator != '/') |
||
192 | for (p = child; (p = strchr(p, separator)); p++) |
||
193 | *p = '/'; |
||
194 | PHYSFS_mkdir(child); |
||
195 | PHYSFS_setWriteDir(fullPath); |
||
196 | writedir = PHYSFS_getWriteDir(); |
||
197 | } |
||
198 | con_printf(CON_DEBUG, "PHYSFS: append write directory \"%s\" to search path", writedir); |
||
199 | printf("PHYSFS: append write directory \"%s\" to search path: %s", writedir, fullPath); |
||
200 | PHYSFS_mount(writedir, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
201 | #endif |
||
202 | con_printf(CON_DEBUG, "PHYSFS: temporarily append base directory \"%s\" to search path", base_dir); |
||
203 | PHYSFS_mount(base_dir, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
204 | if (!InitArgs( argc,argv )) |
||
205 | return false; |
||
206 | PHYSFS_unmount(base_dir); // Pierre-Marie Baty -- work around PHYSFS_removeFromSearchPath() deprecation |
||
207 | |||
208 | if (!PHYSFS_getWriteDir()) |
||
209 | { |
||
210 | PHYSFS_setWriteDir(base_dir); |
||
211 | if (!(writedir = PHYSFS_getWriteDir())) |
||
212 | Error("can't set write dir: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); // Pierre-Marie Baty -- work around PHYSFS_getLastError() deprecation |
||
213 | PHYSFS_mount(writedir, NULL, 0); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
214 | } |
||
215 | |||
216 | //tell PHYSFS where hogdir is |
||
217 | if (!CGameArg.SysHogDir.empty()) |
||
218 | { |
||
219 | const auto p = CGameArg.SysHogDir.c_str(); |
||
220 | con_printf(CON_DEBUG, "PHYSFS: append argument hog directory \"%s\" to search path", p); |
||
221 | PHYSFS_mount(p, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
222 | } |
||
223 | #if DXX_USE_SHAREPATH |
||
224 | else if (!GameArg.SysNoHogDir) |
||
225 | { |
||
226 | con_puts(CON_DEBUG, "PHYSFS: append sharepath directory \"" DXX_SHAREPATH "\" to search path"); |
||
227 | PHYSFS_mount(DXX_SHAREPATH, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
228 | } |
||
229 | else |
||
230 | { |
||
231 | con_puts(CON_DEBUG, "PHYSFS: skipping built-in sharepath \"" DXX_SHAREPATH "\""); |
||
232 | } |
||
233 | #else |
||
234 | else |
||
235 | { |
||
236 | con_puts(CON_DEBUG, "PHYSFS: no built-in sharepath"); |
||
237 | } |
||
238 | #endif |
||
239 | |||
240 | PHYSFSX_addRelToSearchPath("data", 1); // 'Data' subdirectory |
||
241 | |||
242 | // For Macintosh, add the 'Resources' directory in the .app bundle to the searchpaths |
||
243 | #if defined(__APPLE__) && defined(__MACH__) |
||
244 | { |
||
245 | CFBundleRef mainBundle = CFBundleGetMainBundle(); |
||
246 | if (mainBundle) |
||
247 | { |
||
248 | CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); |
||
249 | if (resourcesURL) |
||
250 | { |
||
251 | if (CFURLGetFileSystemRepresentation(resourcesURL, TRUE, reinterpret_cast<uint8_t *>(fullPath), sizeof(fullPath))) |
||
252 | { |
||
253 | con_printf(CON_DEBUG, "PHYSFS: append resources directory \"%s\" to search path", fullPath); |
||
254 | PHYSFS_mount(fullPath, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
255 | } |
||
256 | |||
257 | CFRelease(resourcesURL); |
||
258 | } |
||
259 | } |
||
260 | } |
||
261 | #elif defined(macintosh) |
||
262 | if (bundle) |
||
263 | { |
||
264 | base_dir[strlen(base_dir)] = ':'; // go back in the bundle |
||
265 | base_dir[strlen(base_dir)] = ':'; // go back in 'Contents' |
||
266 | strncat(base_dir, ":Resources:", PATH_MAX - 1 - strlen(base_dir)); |
||
267 | base_dir[PATH_MAX - 1] = '\0'; |
||
268 | con_printf(CON_DEBUG, "PHYSFS: append bundle directory \"%s\" to search path", base_dir); |
||
269 | PHYSFS_mount(base_dir, NULL, 1); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
270 | } |
||
271 | #endif |
||
272 | return true; |
||
273 | } |
||
274 | |||
275 | } |
||
276 | |||
277 | namespace dcx { |
||
278 | |||
279 | // Add a searchpath, but that searchpath is relative to an existing searchpath |
||
280 | // It will add the first one it finds and return 1, if it doesn't find any it returns 0 |
||
281 | int PHYSFSX_addRelToSearchPath(const char *relname, int add_to_end) |
||
282 | { |
||
283 | char relname2[PATH_MAX]; |
||
284 | |||
285 | snprintf(relname2, sizeof(relname2), "%s", relname); |
||
286 | PHYSFSEXT_locateCorrectCase(relname2); |
||
287 | |||
288 | std::array<char, PATH_MAX> pathname; |
||
289 | if (!PHYSFSX_getRealPath(relname2, pathname)) |
||
290 | { |
||
291 | con_printf(CON_DEBUG, "PHYSFS: ignoring map request: no canonical path for relative name \"%s\"", relname2); |
||
292 | return 0; |
||
293 | } |
||
294 | |||
295 | auto r = PHYSFS_mount(pathname.data(), NULL, add_to_end); // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
296 | const auto action = add_to_end ? "append" : "insert"; |
||
297 | if (r) |
||
298 | con_printf(CON_DEBUG, "PHYSFS: %s canonical directory \"%s\" to search path from relative name \"%s\"", action, pathname.data(), relname); |
||
299 | else |
||
300 | 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(PHYSFS_getLastErrorCode())); // Pierre-Marie Baty -- work around PHYSFS_getLastError() deprecation |
||
301 | return r; |
||
302 | } |
||
303 | |||
304 | int PHYSFSX_removeRelFromSearchPath(const char *relname) |
||
305 | { |
||
306 | char relname2[PATH_MAX]; |
||
307 | |||
308 | snprintf(relname2, sizeof(relname2), "%s", relname); |
||
309 | PHYSFSEXT_locateCorrectCase(relname2); |
||
310 | |||
311 | std::array<char, PATH_MAX> pathname; |
||
312 | if (!PHYSFSX_getRealPath(relname2, pathname)) |
||
313 | { |
||
314 | con_printf(CON_DEBUG, "PHYSFS: ignoring unmap request: no canonical path for relative name \"%s\"", relname2); |
||
315 | return 0; |
||
316 | } |
||
317 | auto r = PHYSFS_unmount(pathname.data()); // Pierre-Marie Baty -- work around PHYSFS_removeFromSearchPath() deprecation |
||
318 | if (r) |
||
319 | con_printf(CON_DEBUG, "PHYSFS: unmap canonical directory \"%s\" (relative name \"%s\")", pathname.data(), relname); |
||
320 | else |
||
321 | con_printf(CON_VERBOSE, "PHYSFS: failed to unmap canonical directory \"%s\" (relative name \"%s\"): \"%s\"", pathname.data(), relname, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); // Pierre-Marie Baty -- work around PHYSFS_getLastError() deprecation |
||
322 | return r; |
||
323 | } |
||
324 | |||
325 | int PHYSFSX_fsize(const char *hogname) |
||
326 | { |
||
327 | char hogname2[PATH_MAX]; |
||
328 | |||
329 | snprintf(hogname2, sizeof(hogname2), "%s", hogname); |
||
330 | PHYSFSEXT_locateCorrectCase(hogname2); |
||
331 | |||
332 | if (RAIIPHYSFS_File fp{PHYSFS_openRead(hogname2)}) |
||
333 | return PHYSFS_fileLength(fp); |
||
334 | return -1; |
||
335 | } |
||
336 | |||
337 | void PHYSFSX_listSearchPathContent() |
||
338 | { |
||
339 | con_puts(CON_DEBUG, "PHYSFS: Listing contents of Search Path."); |
||
340 | PHYSFSX_uncounted_list list{PHYSFS_getSearchPath()}; |
||
341 | range_for (const auto i, list) |
||
342 | con_printf(CON_DEBUG, "PHYSFS: [%s] is in the Search Path.", i); |
||
343 | list.reset(); |
||
344 | list.reset(PHYSFS_enumerateFiles("")); |
||
345 | range_for (const auto i, list) |
||
346 | con_printf(CON_DEBUG, "PHYSFS: * We've got [%s].", i); |
||
347 | } |
||
348 | |||
349 | } |
||
350 | |||
351 | namespace dsx { |
||
352 | |||
353 | // checks which archives are supported by PhysFS. Return 0 if some essential (HOG) is not supported |
||
354 | int PHYSFSX_checkSupportedArchiveTypes() |
||
355 | { |
||
356 | int hog_sup = 0; |
||
357 | #ifdef DXX_BUILD_DESCENT_II |
||
358 | int mvl_sup = 0; |
||
359 | #endif |
||
360 | |||
361 | con_puts(CON_DEBUG, "PHYSFS: Checking supported archive types."); |
||
362 | range_for (const auto i, make_null_sentinel_array(PHYSFS_supportedArchiveTypes())) |
||
363 | { |
||
364 | const auto iextension = i->extension; |
||
365 | con_printf(CON_DEBUG, "PHYSFS: Supported archive: [%s], which is [%s].", iextension, i->description); |
||
366 | if (!d_stricmp(iextension, "HOG")) |
||
367 | hog_sup = 1; |
||
368 | #ifdef DXX_BUILD_DESCENT_II |
||
369 | else if (!d_stricmp(iextension, "MVL")) |
||
370 | mvl_sup = 1; |
||
371 | #endif |
||
372 | } |
||
373 | |||
374 | if (!hog_sup) |
||
375 | con_puts(CON_CRITICAL, "PHYSFS: HOG not supported. The game will not work without!"); |
||
376 | #ifdef DXX_BUILD_DESCENT_II |
||
377 | if (!mvl_sup) |
||
378 | con_puts(CON_URGENT, "PHYSFS: MVL not supported. Won't be able to play movies!"); |
||
379 | #endif |
||
380 | |||
381 | return hog_sup; |
||
382 | } |
||
383 | |||
384 | } |
||
385 | |||
386 | namespace dcx { |
||
387 | |||
388 | int PHYSFSX_getRealPath(const char *stdPath, char *realPath, const std::size_t outSize) |
||
389 | { |
||
390 | DXX_POISON_MEMORY(realPath, outSize, 0xdd); |
||
391 | const char *realDir = PHYSFS_getRealDir(stdPath); |
||
392 | if (!realDir) |
||
393 | { |
||
394 | realDir = PHYSFS_getWriteDir(); |
||
395 | if (!realDir) |
||
396 | return 0; |
||
397 | } |
||
398 | const auto realDirSize = strlen(realDir); |
||
399 | if (realDirSize >= outSize) |
||
400 | return 0; |
||
401 | auto mountpoint = PHYSFS_getMountPoint(realDir); |
||
402 | if (!mountpoint) |
||
403 | return 0; |
||
404 | std::copy_n(realDir, realDirSize + 1, realPath); |
||
405 | #ifdef _unix__ |
||
406 | auto &sep = "/"; |
||
407 | assert(!strcmp(PHYSFS_getDirSeparator(), sep)); |
||
408 | #else |
||
409 | const auto sep = PHYSFS_getDirSeparator(); |
||
410 | #endif |
||
411 | const auto sepSize = strlen(sep); |
||
412 | auto realPathUsed = realDirSize; |
||
413 | if (realDirSize >= sepSize) |
||
414 | { |
||
415 | const auto p = realPath + realDirSize - sepSize; |
||
416 | if (strcmp(p, sep)) // no sep at end of realPath |
||
417 | { |
||
418 | realPathUsed += sepSize; |
||
419 | std::copy_n(sep, sepSize, &realPath[realDirSize]); |
||
420 | } |
||
421 | } |
||
422 | if (*mountpoint == '/') |
||
423 | ++mountpoint; |
||
424 | if (*stdPath == '/') |
||
425 | ++stdPath; |
||
426 | const auto ml = strlen(mountpoint); |
||
427 | if (!strncmp(mountpoint, stdPath, ml)) |
||
428 | stdPath += ml; |
||
429 | else |
||
430 | { |
||
431 | /* Virtual path is not under the virtual mount point that |
||
432 | * provides the path. |
||
433 | */ |
||
434 | assert(false); |
||
435 | } |
||
436 | const auto stdPathLen = strlen(stdPath) + 1; |
||
437 | if (realPathUsed + stdPathLen >= outSize) |
||
438 | return 0; |
||
439 | #ifdef __unix__ |
||
440 | /* Separator is "/" and physfs internal separator is "/". Copy |
||
441 | * through. |
||
442 | */ |
||
443 | std::copy_n(stdPath, stdPathLen, &realPath[realPathUsed]); |
||
444 | #else |
||
445 | /* Separator might be / on non-unix, but the fallback path works |
||
446 | * regardless of whether separator is "/". |
||
447 | */ |
||
448 | const auto csep = *sep; |
||
449 | const auto a = [csep](char c) { |
||
450 | return c == '/' ? csep : c; |
||
451 | }; |
||
452 | std::transform(stdPath, &stdPath[stdPathLen], &realPath[realPathUsed], a); |
||
453 | #endif |
||
454 | return 1; |
||
455 | } |
||
456 | |||
457 | // checks if path is already added to Searchpath. Returns 0 if yes, 1 if not. |
||
458 | int PHYSFSX_isNewPath(const char *path) |
||
459 | { |
||
460 | int is_new_path = 1; |
||
461 | PHYSFSX_uncounted_list list{PHYSFS_getSearchPath()}; |
||
462 | range_for (const auto i, list) |
||
463 | { |
||
464 | if (!strcmp(path, i)) |
||
465 | { |
||
466 | is_new_path = 0; |
||
467 | break; |
||
468 | } |
||
469 | } |
||
470 | return is_new_path; |
||
471 | } |
||
472 | |||
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> |
||
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 '.' |
||
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. |
||
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 | |||
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 |
||
533 | RAIIPHYSFS_File 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; |
||
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 fp; |
||
555 | } |
||
556 | |||
557 | //Open a file for writing, set up a buffer |
||
558 | RAIIPHYSFS_File 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; |
||
565 | while (!PHYSFS_setBuffer(fp, bufSize) && bufSize) |
||
566 | bufSize /= 2; |
||
567 | return fp; |
||
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 | */ |
||
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(), NULL, 0)) // Pierre-Marie Baty -- work around PHYSFS_addToSearchPath() deprecation |
||
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 |
||
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()); // Pierre-Marie Baty -- work around PHYSFS_removeFromSearchPath() deprecation |
||
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()); // Pierre-Marie Baty -- work around PHYSFS_removeFromSearchPath() deprecation |
||
643 | } |
||
644 | } |
||
645 | |||
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 | |||
651 | } |