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 | } |