diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
| commit | 5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch) | |
| tree | 8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c | |
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c')
| -rw-r--r-- | contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c b/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c new file mode 100644 index 0000000..b115019 --- /dev/null +++ b/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c | |||
| @@ -0,0 +1,530 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #include "SDL_filesystem_c.h" | ||
| 25 | #include "SDL_sysfilesystem.h" | ||
| 26 | #include "../stdlib/SDL_sysstdlib.h" | ||
| 27 | |||
| 28 | bool SDL_RemovePath(const char *path) | ||
| 29 | { | ||
| 30 | if (!path) { | ||
| 31 | return SDL_InvalidParamError("path"); | ||
| 32 | } | ||
| 33 | return SDL_SYS_RemovePath(path); | ||
| 34 | } | ||
| 35 | |||
| 36 | bool SDL_RenamePath(const char *oldpath, const char *newpath) | ||
| 37 | { | ||
| 38 | if (!oldpath) { | ||
| 39 | return SDL_InvalidParamError("oldpath"); | ||
| 40 | } else if (!newpath) { | ||
| 41 | return SDL_InvalidParamError("newpath"); | ||
| 42 | } | ||
| 43 | return SDL_SYS_RenamePath(oldpath, newpath); | ||
| 44 | } | ||
| 45 | |||
| 46 | bool SDL_CopyFile(const char *oldpath, const char *newpath) | ||
| 47 | { | ||
| 48 | if (!oldpath) { | ||
| 49 | return SDL_InvalidParamError("oldpath"); | ||
| 50 | } else if (!newpath) { | ||
| 51 | return SDL_InvalidParamError("newpath"); | ||
| 52 | } | ||
| 53 | return SDL_SYS_CopyFile(oldpath, newpath); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool SDL_CreateDirectory(const char *path) | ||
| 57 | { | ||
| 58 | if (!path) { | ||
| 59 | return SDL_InvalidParamError("path"); | ||
| 60 | } | ||
| 61 | |||
| 62 | bool retval = SDL_SYS_CreateDirectory(path); | ||
| 63 | if (!retval && *path) { // maybe we're missing parent directories? | ||
| 64 | char *parents = SDL_strdup(path); | ||
| 65 | if (!parents) { | ||
| 66 | return false; // oh well. | ||
| 67 | } | ||
| 68 | |||
| 69 | // in case there was a separator at the end of the path and it was | ||
| 70 | // upsetting something, chop it off. | ||
| 71 | const size_t slen = SDL_strlen(parents); | ||
| 72 | #ifdef SDL_PLATFORM_WINDOWS | ||
| 73 | if ((parents[slen - 1] == '/') || (parents[slen - 1] == '\\')) | ||
| 74 | #else | ||
| 75 | if (parents[slen - 1] == '/') | ||
| 76 | #endif | ||
| 77 | { | ||
| 78 | parents[slen - 1] = '\0'; | ||
| 79 | retval = SDL_SYS_CreateDirectory(parents); | ||
| 80 | } | ||
| 81 | |||
| 82 | if (!retval) { | ||
| 83 | for (char *ptr = parents; *ptr; ptr++) { | ||
| 84 | const char ch = *ptr; | ||
| 85 | #ifdef SDL_PLATFORM_WINDOWS | ||
| 86 | const bool issep = (ch == '/') || (ch == '\\'); | ||
| 87 | if (issep && ((ptr - parents) == 2) && (parents[1] == ':')) { | ||
| 88 | continue; // it's just the drive letter, skip it. | ||
| 89 | } | ||
| 90 | #else | ||
| 91 | const bool issep = (ch == '/'); | ||
| 92 | if (issep && ((ptr - parents) == 0)) { | ||
| 93 | continue; // it's just the root directory, skip it. | ||
| 94 | } | ||
| 95 | #endif | ||
| 96 | |||
| 97 | if (issep) { | ||
| 98 | *ptr = '\0'; | ||
| 99 | // (this does not fail if the path already exists as a directory.) | ||
| 100 | retval = SDL_SYS_CreateDirectory(parents); | ||
| 101 | if (!retval) { // still failing when making parents? Give up. | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | *ptr = ch; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // last chance: did it work this time? | ||
| 109 | retval = SDL_SYS_CreateDirectory(parents); | ||
| 110 | } | ||
| 111 | |||
| 112 | SDL_free(parents); | ||
| 113 | } | ||
| 114 | return retval; | ||
| 115 | } | ||
| 116 | |||
| 117 | bool SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata) | ||
| 118 | { | ||
| 119 | if (!path) { | ||
| 120 | return SDL_InvalidParamError("path"); | ||
| 121 | } else if (!callback) { | ||
| 122 | return SDL_InvalidParamError("callback"); | ||
| 123 | } | ||
| 124 | return SDL_SYS_EnumerateDirectory(path, callback, userdata); | ||
| 125 | } | ||
| 126 | |||
| 127 | bool SDL_GetPathInfo(const char *path, SDL_PathInfo *info) | ||
| 128 | { | ||
| 129 | SDL_PathInfo dummy; | ||
| 130 | |||
| 131 | if (!info) { | ||
| 132 | info = &dummy; | ||
| 133 | } | ||
| 134 | SDL_zerop(info); | ||
| 135 | |||
| 136 | if (!path) { | ||
| 137 | return SDL_InvalidParamError("path"); | ||
| 138 | } | ||
| 139 | |||
| 140 | return SDL_SYS_GetPathInfo(path, info); | ||
| 141 | } | ||
| 142 | |||
| 143 | static bool EverythingMatch(const char *pattern, const char *str, bool *matched_to_dir) | ||
| 144 | { | ||
| 145 | SDL_assert(pattern == NULL); | ||
| 146 | SDL_assert(str != NULL); | ||
| 147 | SDL_assert(matched_to_dir != NULL); | ||
| 148 | |||
| 149 | *matched_to_dir = true; | ||
| 150 | return true; // everything matches! | ||
| 151 | } | ||
| 152 | |||
| 153 | // this is just '*' and '?', with '/' matching nothing. | ||
| 154 | static bool WildcardMatch(const char *pattern, const char *str, bool *matched_to_dir) | ||
| 155 | { | ||
| 156 | SDL_assert(pattern != NULL); | ||
| 157 | SDL_assert(str != NULL); | ||
| 158 | SDL_assert(matched_to_dir != NULL); | ||
| 159 | |||
| 160 | const char *str_backtrack = NULL; | ||
| 161 | const char *pattern_backtrack = NULL; | ||
| 162 | char sch_backtrack = 0; | ||
| 163 | char sch = *str; | ||
| 164 | char pch = *pattern; | ||
| 165 | |||
| 166 | while (sch) { | ||
| 167 | if (pch == '*') { | ||
| 168 | str_backtrack = str; | ||
| 169 | pattern_backtrack = ++pattern; | ||
| 170 | sch_backtrack = sch; | ||
| 171 | pch = *pattern; | ||
| 172 | } else if (pch == sch) { | ||
| 173 | if (pch == '/') { | ||
| 174 | str_backtrack = pattern_backtrack = NULL; | ||
| 175 | } | ||
| 176 | sch = *(++str); | ||
| 177 | pch = *(++pattern); | ||
| 178 | } else if ((pch == '?') && (sch != '/')) { // end of string (checked at `while`) or path separator do not match '?'. | ||
| 179 | sch = *(++str); | ||
| 180 | pch = *(++pattern); | ||
| 181 | } else if (!pattern_backtrack || (sch_backtrack == '/')) { // we didn't have a match. Are we in a '*' and NOT on a path separator? Keep going. Otherwise, fail. | ||
| 182 | *matched_to_dir = false; | ||
| 183 | return false; | ||
| 184 | } else { // still here? Wasn't a match, but we're definitely in a '*' pattern. | ||
| 185 | str = ++str_backtrack; | ||
| 186 | pattern = pattern_backtrack; | ||
| 187 | sch_backtrack = sch; | ||
| 188 | sch = *str; | ||
| 189 | pch = *pattern; | ||
| 190 | } | ||
| 191 | |||
| 192 | #ifdef SDL_PLATFORM_WINDOWS | ||
| 193 | if (sch == '\\') { | ||
| 194 | sch = '/'; | ||
| 195 | } | ||
| 196 | #endif | ||
| 197 | } | ||
| 198 | |||
| 199 | // '*' at the end can be ignored, they are allowed to match nothing. | ||
| 200 | while (pch == '*') { | ||
| 201 | pch = *(++pattern); | ||
| 202 | } | ||
| 203 | |||
| 204 | *matched_to_dir = ((pch == '/') || (pch == '\0')); // end of string and the pattern is complete or failed at a '/'? We should descend into this directory. | ||
| 205 | |||
| 206 | return (pch == '\0'); // survived the whole pattern? That's a match! | ||
| 207 | } | ||
| 208 | |||
| 209 | |||
| 210 | // Note that this will currently encode illegal codepoints: UTF-16 surrogates, 0xFFFE, and 0xFFFF. | ||
| 211 | // and a codepoint > 0x10FFFF will fail the same as if there wasn't enough memory. | ||
| 212 | // clean this up if you want to move this to SDL_string.c. | ||
| 213 | static size_t EncodeCodepointToUtf8(char *ptr, Uint32 cp, size_t remaining) | ||
| 214 | { | ||
| 215 | if (cp < 0x80) { // fits in a single UTF-8 byte. | ||
| 216 | if (remaining) { | ||
| 217 | *ptr = (char) cp; | ||
| 218 | return 1; | ||
| 219 | } | ||
| 220 | } else if (cp < 0x800) { // fits in 2 bytes. | ||
| 221 | if (remaining >= 2) { | ||
| 222 | ptr[0] = (char) ((cp >> 6) | 128 | 64); | ||
| 223 | ptr[1] = (char) (cp & 0x3F) | 128; | ||
| 224 | return 2; | ||
| 225 | } | ||
| 226 | } else if (cp < 0x10000) { // fits in 3 bytes. | ||
| 227 | if (remaining >= 3) { | ||
| 228 | ptr[0] = (char) ((cp >> 12) | 128 | 64 | 32); | ||
| 229 | ptr[1] = (char) ((cp >> 6) & 0x3F) | 128; | ||
| 230 | ptr[2] = (char) (cp & 0x3F) | 128; | ||
| 231 | return 3; | ||
| 232 | } | ||
| 233 | } else if (cp <= 0x10FFFF) { // fits in 4 bytes. | ||
| 234 | if (remaining >= 4) { | ||
| 235 | ptr[0] = (char) ((cp >> 18) | 128 | 64 | 32 | 16); | ||
| 236 | ptr[1] = (char) ((cp >> 12) & 0x3F) | 128; | ||
| 237 | ptr[2] = (char) ((cp >> 6) & 0x3F) | 128; | ||
| 238 | ptr[3] = (char) (cp & 0x3F) | 128; | ||
| 239 | return 4; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static char *CaseFoldUtf8String(const char *fname) | ||
| 247 | { | ||
| 248 | SDL_assert(fname != NULL); | ||
| 249 | const size_t allocation = (SDL_strlen(fname) + 1) * 3 * 4; | ||
| 250 | char *result = (char *) SDL_malloc(allocation); // lazy: just allocating the max needed. | ||
| 251 | if (!result) { | ||
| 252 | return NULL; | ||
| 253 | } | ||
| 254 | |||
| 255 | Uint32 codepoint; | ||
| 256 | char *ptr = result; | ||
| 257 | size_t remaining = allocation; | ||
| 258 | while ((codepoint = SDL_StepUTF8(&fname, NULL)) != 0) { | ||
| 259 | Uint32 folded[3]; | ||
| 260 | const int num_folded = SDL_CaseFoldUnicode(codepoint, folded); | ||
| 261 | SDL_assert(num_folded > 0); | ||
| 262 | SDL_assert(num_folded <= SDL_arraysize(folded)); | ||
| 263 | for (int i = 0; i < num_folded; i++) { | ||
| 264 | SDL_assert(remaining > 0); | ||
| 265 | const size_t rc = EncodeCodepointToUtf8(ptr, folded[i], remaining); | ||
| 266 | SDL_assert(rc > 0); | ||
| 267 | SDL_assert(rc < remaining); | ||
| 268 | remaining -= rc; | ||
| 269 | ptr += rc; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | SDL_assert(remaining > 0); | ||
| 274 | remaining--; | ||
| 275 | *ptr = '\0'; | ||
| 276 | |||
| 277 | if (remaining > 0) { | ||
| 278 | SDL_assert(allocation > remaining); | ||
| 279 | ptr = (char *)SDL_realloc(result, allocation - remaining); // shrink it down. | ||
| 280 | if (ptr) { // shouldn't fail, but if it does, `result` is still valid. | ||
| 281 | result = ptr; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | return result; | ||
| 286 | } | ||
| 287 | |||
| 288 | |||
| 289 | typedef struct GlobDirCallbackData | ||
| 290 | { | ||
| 291 | bool (*matcher)(const char *pattern, const char *str, bool *matched_to_dir); | ||
| 292 | const char *pattern; | ||
| 293 | int num_entries; | ||
| 294 | SDL_GlobFlags flags; | ||
| 295 | SDL_GlobEnumeratorFunc enumerator; | ||
| 296 | SDL_GlobGetPathInfoFunc getpathinfo; | ||
| 297 | void *fsuserdata; | ||
| 298 | size_t basedirlen; | ||
| 299 | SDL_IOStream *string_stream; | ||
| 300 | } GlobDirCallbackData; | ||
| 301 | |||
| 302 | static SDL_EnumerationResult SDLCALL GlobDirectoryCallback(void *userdata, const char *dirname, const char *fname) | ||
| 303 | { | ||
| 304 | SDL_assert(userdata != NULL); | ||
| 305 | SDL_assert(dirname != NULL); | ||
| 306 | SDL_assert(fname != NULL); | ||
| 307 | |||
| 308 | //SDL_Log("GlobDirectoryCallback('%s', '%s')", dirname, fname); | ||
| 309 | |||
| 310 | GlobDirCallbackData *data = (GlobDirCallbackData *) userdata; | ||
| 311 | |||
| 312 | // !!! FIXME: if we're careful, we can keep a single buffer in `data` that we push and pop paths off the end of as we walk the tree, | ||
| 313 | // !!! FIXME: and only casefold the new pieces instead of allocating and folding full paths for all of this. | ||
| 314 | |||
| 315 | char *fullpath = NULL; | ||
| 316 | if (SDL_asprintf(&fullpath, "%s%s", dirname, fname) < 0) { | ||
| 317 | return SDL_ENUM_FAILURE; | ||
| 318 | } | ||
| 319 | |||
| 320 | char *folded = NULL; | ||
| 321 | if (data->flags & SDL_GLOB_CASEINSENSITIVE) { | ||
| 322 | folded = CaseFoldUtf8String(fullpath); | ||
| 323 | if (!folded) { | ||
| 324 | return SDL_ENUM_FAILURE; | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | bool matched_to_dir = false; | ||
| 329 | const bool matched = data->matcher(data->pattern, (folded ? folded : fullpath) + data->basedirlen, &matched_to_dir); | ||
| 330 | //SDL_Log("GlobDirectoryCallback: Considered %spath='%s' vs pattern='%s': %smatched (matched_to_dir=%s)", folded ? "(folded) " : "", (folded ? folded : fullpath) + data->basedirlen, data->pattern, matched ? "" : "NOT ", matched_to_dir ? "TRUE" : "FALSE"); | ||
| 331 | SDL_free(folded); | ||
| 332 | |||
| 333 | if (matched) { | ||
| 334 | const char *subpath = fullpath + data->basedirlen; | ||
| 335 | const size_t slen = SDL_strlen(subpath) + 1; | ||
| 336 | if (SDL_WriteIO(data->string_stream, subpath, slen) != slen) { | ||
| 337 | SDL_free(fullpath); | ||
| 338 | return SDL_ENUM_FAILURE; // stop enumerating, return failure to the app. | ||
| 339 | } | ||
| 340 | data->num_entries++; | ||
| 341 | } | ||
| 342 | |||
| 343 | SDL_EnumerationResult result = SDL_ENUM_CONTINUE; // keep enumerating by default. | ||
| 344 | if (matched_to_dir) { | ||
| 345 | SDL_PathInfo info; | ||
| 346 | if (data->getpathinfo(fullpath, &info, data->fsuserdata) && (info.type == SDL_PATHTYPE_DIRECTORY)) { | ||
| 347 | //SDL_Log("GlobDirectoryCallback: Descending into subdir '%s'", fname); | ||
| 348 | if (!data->enumerator(fullpath, GlobDirectoryCallback, data, data->fsuserdata)) { | ||
| 349 | result = SDL_ENUM_FAILURE; | ||
| 350 | } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | SDL_free(fullpath); | ||
| 355 | |||
| 356 | return result; | ||
| 357 | } | ||
| 358 | |||
| 359 | char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata) | ||
| 360 | { | ||
| 361 | int dummycount; | ||
| 362 | if (!count) { | ||
| 363 | count = &dummycount; | ||
| 364 | } | ||
| 365 | *count = 0; | ||
| 366 | |||
| 367 | if (!path) { | ||
| 368 | SDL_InvalidParamError("path"); | ||
| 369 | return NULL; | ||
| 370 | } | ||
| 371 | |||
| 372 | // if path ends with any slash, chop them off, so we don't confuse the pattern matcher later. | ||
| 373 | char *pathcpy = NULL; | ||
| 374 | size_t pathlen = SDL_strlen(path); | ||
| 375 | if ((pathlen > 1) && ((path[pathlen-1] == '/') || (path[pathlen-1] == '\\'))) { | ||
| 376 | pathcpy = SDL_strdup(path); | ||
| 377 | if (!pathcpy) { | ||
| 378 | return NULL; | ||
| 379 | } | ||
| 380 | char *ptr = &pathcpy[pathlen-1]; | ||
| 381 | while ((ptr >= pathcpy) && ((*ptr == '/') || (*ptr == '\\'))) { | ||
| 382 | *(ptr--) = '\0'; | ||
| 383 | } | ||
| 384 | path = pathcpy; | ||
| 385 | } | ||
| 386 | |||
| 387 | if (!pattern) { | ||
| 388 | flags &= ~SDL_GLOB_CASEINSENSITIVE; // avoid some unnecessary allocations and work later. | ||
| 389 | } | ||
| 390 | |||
| 391 | char *folded = NULL; | ||
| 392 | if (flags & SDL_GLOB_CASEINSENSITIVE) { | ||
| 393 | SDL_assert(pattern != NULL); | ||
| 394 | folded = CaseFoldUtf8String(pattern); | ||
| 395 | if (!folded) { | ||
| 396 | SDL_free(pathcpy); | ||
| 397 | return NULL; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | GlobDirCallbackData data; | ||
| 402 | SDL_zero(data); | ||
| 403 | data.string_stream = SDL_IOFromDynamicMem(); | ||
| 404 | if (!data.string_stream) { | ||
| 405 | SDL_free(folded); | ||
| 406 | SDL_free(pathcpy); | ||
| 407 | return NULL; | ||
| 408 | } | ||
| 409 | |||
| 410 | if (!pattern) { | ||
| 411 | data.matcher = EverythingMatch; // no pattern? Everything matches. | ||
| 412 | |||
| 413 | // !!! FIXME | ||
| 414 | //} else if (flags & SDL_GLOB_GITIGNORE) { | ||
| 415 | // data.matcher = GitIgnoreMatch; | ||
| 416 | |||
| 417 | } else { | ||
| 418 | data.matcher = WildcardMatch; | ||
| 419 | } | ||
| 420 | |||
| 421 | data.pattern = folded ? folded : pattern; | ||
| 422 | data.flags = flags; | ||
| 423 | data.enumerator = enumerator; | ||
| 424 | data.getpathinfo = getpathinfo; | ||
| 425 | data.fsuserdata = userdata; | ||
| 426 | data.basedirlen = *path ? (SDL_strlen(path) + 1) : 0; // +1 for the '/' we'll be adding. | ||
| 427 | |||
| 428 | |||
| 429 | char **result = NULL; | ||
| 430 | if (data.enumerator(path, GlobDirectoryCallback, &data, data.fsuserdata)) { | ||
| 431 | const size_t streamlen = (size_t) SDL_GetIOSize(data.string_stream); | ||
| 432 | const size_t buflen = streamlen + ((data.num_entries + 1) * sizeof (char *)); // +1 for NULL terminator at end of array. | ||
| 433 | result = (char **) SDL_malloc(buflen); | ||
| 434 | if (result) { | ||
| 435 | if (data.num_entries > 0) { | ||
| 436 | Sint64 iorc = SDL_SeekIO(data.string_stream, 0, SDL_IO_SEEK_SET); | ||
| 437 | SDL_assert(iorc == 0); // this should never fail for a memory stream! | ||
| 438 | char *ptr = (char *) (result + (data.num_entries + 1)); | ||
| 439 | iorc = SDL_ReadIO(data.string_stream, ptr, streamlen); | ||
| 440 | SDL_assert(iorc == (Sint64) streamlen); // this should never fail for a memory stream! | ||
| 441 | for (int i = 0; i < data.num_entries; i++) { | ||
| 442 | result[i] = ptr; | ||
| 443 | ptr += SDL_strlen(ptr) + 1; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | result[data.num_entries] = NULL; // NULL terminate the list. | ||
| 447 | *count = data.num_entries; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | SDL_CloseIO(data.string_stream); | ||
| 452 | SDL_free(folded); | ||
| 453 | SDL_free(pathcpy); | ||
| 454 | |||
| 455 | return result; | ||
| 456 | } | ||
| 457 | |||
| 458 | static bool GlobDirectoryGetPathInfo(const char *path, SDL_PathInfo *info, void *userdata) | ||
| 459 | { | ||
| 460 | return SDL_GetPathInfo(path, info); | ||
| 461 | } | ||
| 462 | |||
| 463 | static bool GlobDirectoryEnumerator(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata) | ||
| 464 | { | ||
| 465 | return SDL_EnumerateDirectory(path, cb, cbuserdata); | ||
| 466 | } | ||
| 467 | |||
| 468 | char **SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count) | ||
| 469 | { | ||
| 470 | //SDL_Log("SDL_GlobDirectory('%s', '%s') ...", path, pattern); | ||
| 471 | return SDL_InternalGlobDirectory(path, pattern, flags, count, GlobDirectoryEnumerator, GlobDirectoryGetPathInfo, NULL); | ||
| 472 | } | ||
| 473 | |||
| 474 | |||
| 475 | static char *CachedBasePath = NULL; | ||
| 476 | |||
| 477 | const char *SDL_GetBasePath(void) | ||
| 478 | { | ||
| 479 | if (!CachedBasePath) { | ||
| 480 | CachedBasePath = SDL_SYS_GetBasePath(); | ||
| 481 | } | ||
| 482 | return CachedBasePath; | ||
| 483 | } | ||
| 484 | |||
| 485 | |||
| 486 | static char *CachedUserFolders[SDL_FOLDER_COUNT]; | ||
| 487 | |||
| 488 | const char *SDL_GetUserFolder(SDL_Folder folder) | ||
| 489 | { | ||
| 490 | const int idx = (int) folder; | ||
| 491 | if ((idx < 0) || (idx >= SDL_arraysize(CachedUserFolders))) { | ||
| 492 | SDL_InvalidParamError("folder"); | ||
| 493 | return NULL; | ||
| 494 | } | ||
| 495 | |||
| 496 | if (!CachedUserFolders[idx]) { | ||
| 497 | CachedUserFolders[idx] = SDL_SYS_GetUserFolder(folder); | ||
| 498 | } | ||
| 499 | return CachedUserFolders[idx]; | ||
| 500 | } | ||
| 501 | |||
| 502 | |||
| 503 | char *SDL_GetPrefPath(const char *org, const char *app) | ||
| 504 | { | ||
| 505 | return SDL_SYS_GetPrefPath(org, app); | ||
| 506 | } | ||
| 507 | |||
| 508 | char *SDL_GetCurrentDirectory(void) | ||
| 509 | { | ||
| 510 | return SDL_SYS_GetCurrentDirectory(); | ||
| 511 | } | ||
| 512 | |||
| 513 | void SDL_InitFilesystem(void) | ||
| 514 | { | ||
| 515 | } | ||
| 516 | |||
| 517 | void SDL_QuitFilesystem(void) | ||
| 518 | { | ||
| 519 | if (CachedBasePath) { | ||
| 520 | SDL_free(CachedBasePath); | ||
| 521 | CachedBasePath = NULL; | ||
| 522 | } | ||
| 523 | for (int i = 0; i < SDL_arraysize(CachedUserFolders); i++) { | ||
| 524 | if (CachedUserFolders[i]) { | ||
| 525 | SDL_free(CachedUserFolders[i]); | ||
| 526 | CachedUserFolders[i] = NULL; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
