summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/filesystem
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/filesystem
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/filesystem')
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c530
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/SDL_filesystem_c.h29
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h43
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/android/SDL_sysfilesystem.c62
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/cocoa/SDL_sysfilesystem.m240
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfilesystem.c54
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c62
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/emscripten/SDL_sysfilesystem.c116
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/gdk/SDL_sysfilesystem.cpp150
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/haiku/SDL_sysfilesystem.cc156
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/n3ds/SDL_sysfilesystem.c90
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c246
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/ps2/SDL_sysfilesystem.c119
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/psp/SDL_sysfilesystem.c89
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/riscos/SDL_sysfilesystem.c208
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/unix/SDL_sysfilesystem.c618
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/vita/SDL_sysfilesystem.c92
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfilesystem.c382
-rw-r--r--contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfsops.c231
19 files changed, 3517 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
28bool SDL_RemovePath(const char *path)
29{
30 if (!path) {
31 return SDL_InvalidParamError("path");
32 }
33 return SDL_SYS_RemovePath(path);
34}
35
36bool 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
46bool 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
56bool 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
117bool 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
127bool 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
143static 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.
154static 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.
213static 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
246static 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
289typedef 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
302static 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
359char **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
458static bool GlobDirectoryGetPathInfo(const char *path, SDL_PathInfo *info, void *userdata)
459{
460 return SDL_GetPathInfo(path, info);
461}
462
463static bool GlobDirectoryEnumerator(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata)
464{
465 return SDL_EnumerateDirectory(path, cb, cbuserdata);
466}
467
468char **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
475static char *CachedBasePath = NULL;
476
477const char *SDL_GetBasePath(void)
478{
479 if (!CachedBasePath) {
480 CachedBasePath = SDL_SYS_GetBasePath();
481 }
482 return CachedBasePath;
483}
484
485
486static char *CachedUserFolders[SDL_FOLDER_COUNT];
487
488const 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
503char *SDL_GetPrefPath(const char *org, const char *app)
504{
505 return SDL_SYS_GetPrefPath(org, app);
506}
507
508char *SDL_GetCurrentDirectory(void)
509{
510 return SDL_SYS_GetCurrentDirectory();
511}
512
513void SDL_InitFilesystem(void)
514{
515}
516
517void 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
diff --git a/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem_c.h b/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem_c.h
new file mode 100644
index 0000000..8cfdcab
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/SDL_filesystem_c.h
@@ -0,0 +1,29 @@
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#ifndef SDL_filesystem_c_h_
23#define SDL_filesystem_c_h_
24
25extern void SDL_InitFilesystem(void);
26extern void SDL_QuitFilesystem(void);
27
28#endif
29
diff --git a/contrib/SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h b/contrib/SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h
new file mode 100644
index 0000000..f9f4c59
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h
@@ -0,0 +1,43 @@
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#ifndef SDL_sysfilesystem_h_
23#define SDL_sysfilesystem_h_
24
25// return a string that we can SDL_free(). It will be cached at the higher level.
26extern char *SDL_SYS_GetBasePath(void);
27extern char *SDL_SYS_GetPrefPath(const char *org, const char *app);
28extern char *SDL_SYS_GetUserFolder(SDL_Folder folder);
29extern char *SDL_SYS_GetCurrentDirectory(void);
30
31extern bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata);
32extern bool SDL_SYS_RemovePath(const char *path);
33extern bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath);
34extern bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath);
35extern bool SDL_SYS_CreateDirectory(const char *path);
36extern bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info);
37
38typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata);
39typedef bool (*SDL_GlobGetPathInfoFunc)(const char *path, SDL_PathInfo *info, void *userdata);
40extern char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata);
41
42#endif
43
diff --git a/contrib/SDL-3.2.8/src/filesystem/android/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/android/SDL_sysfilesystem.c
new file mode 100644
index 0000000..bb42409
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/android/SDL_sysfilesystem.c
@@ -0,0 +1,62 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_ANDROID
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <unistd.h>
31
32char *SDL_SYS_GetBasePath(void)
33{
34 // The current working directory is / on Android
35 SDL_Unsupported();
36 return NULL;
37}
38
39char *SDL_SYS_GetPrefPath(const char *org, const char *app)
40{
41 const char *path = SDL_GetAndroidInternalStoragePath();
42 if (path) {
43 size_t pathlen = SDL_strlen(path) + 2;
44 char *fullpath = (char *)SDL_malloc(pathlen);
45 if (!fullpath) {
46 return NULL;
47 }
48 SDL_snprintf(fullpath, pathlen, "%s/", path);
49 return fullpath;
50 }
51 return NULL;
52}
53
54char *SDL_SYS_GetUserFolder(SDL_Folder folder)
55{
56 /* TODO: see https://developer.android.com/reference/android/os/Environment#lfields
57 and https://stackoverflow.com/questions/39332085/get-path-to-pictures-directory */
58 SDL_Unsupported();
59 return NULL;
60}
61
62#endif // SDL_FILESYSTEM_ANDROID
diff --git a/contrib/SDL-3.2.8/src/filesystem/cocoa/SDL_sysfilesystem.m b/contrib/SDL-3.2.8/src/filesystem/cocoa/SDL_sysfilesystem.m
new file mode 100644
index 0000000..5818764
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/cocoa/SDL_sysfilesystem.m
@@ -0,0 +1,240 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_COCOA
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <Foundation/Foundation.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33
34char *SDL_SYS_GetBasePath(void)
35{
36 @autoreleasepool {
37 NSBundle *bundle = [NSBundle mainBundle];
38 const char *baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String];
39 const char *base = NULL;
40 char *result = NULL;
41
42 if (baseType == NULL) {
43 baseType = "resource";
44 }
45 if (SDL_strcasecmp(baseType, "bundle") == 0) {
46 base = [[bundle bundlePath] fileSystemRepresentation];
47 } else if (SDL_strcasecmp(baseType, "parent") == 0) {
48 base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation];
49 } else {
50 // this returns the exedir for non-bundled and the resourceDir for bundled apps
51 base = [[bundle resourcePath] fileSystemRepresentation];
52 }
53
54 if (base) {
55 const size_t len = SDL_strlen(base) + 2;
56 result = (char *)SDL_malloc(len);
57 if (result != NULL) {
58 SDL_snprintf(result, len, "%s/", base);
59 }
60 }
61
62 return result;
63 }
64}
65
66char *SDL_SYS_GetPrefPath(const char *org, const char *app)
67{
68 @autoreleasepool {
69 char *result = NULL;
70 NSArray *array;
71
72 if (!app) {
73 SDL_InvalidParamError("app");
74 return NULL;
75 }
76 if (!org) {
77 org = "";
78 }
79
80#ifndef SDL_PLATFORM_TVOS
81 array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
82#else
83 /* tvOS does not have persistent local storage!
84 * The only place on-device where we can store data is
85 * a cache directory that the OS can empty at any time.
86 *
87 * It's therefore very likely that save data will be erased
88 * between sessions. If you want your app's save data to
89 * actually stick around, you'll need to use iCloud storage.
90 */
91 {
92 static bool shown = false;
93 if (!shown) {
94 shown = true;
95 SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "tvOS does not have persistent local storage! Use iCloud storage if you want your data to persist between sessions.");
96 }
97 }
98
99 array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
100#endif // !SDL_PLATFORM_TVOS
101
102 if ([array count] > 0) { // we only want the first item in the list.
103 NSString *str = [array objectAtIndex:0];
104 const char *base = [str fileSystemRepresentation];
105 if (base) {
106 const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4;
107 result = (char *)SDL_malloc(len);
108 if (result != NULL) {
109 char *ptr;
110 if (*org) {
111 SDL_snprintf(result, len, "%s/%s/%s/", base, org, app);
112 } else {
113 SDL_snprintf(result, len, "%s/%s/", base, app);
114 }
115 for (ptr = result + 1; *ptr; ptr++) {
116 if (*ptr == '/') {
117 *ptr = '\0';
118 mkdir(result, 0700);
119 *ptr = '/';
120 }
121 }
122 mkdir(result, 0700);
123 }
124 }
125 }
126
127 return result;
128 }
129}
130
131char *SDL_SYS_GetUserFolder(SDL_Folder folder)
132{
133 @autoreleasepool {
134#ifdef SDL_PLATFORM_TVOS
135 SDL_SetError("tvOS does not have persistent storage");
136 return NULL;
137#else
138 char *result = NULL;
139 const char* base;
140 NSArray *array;
141 NSSearchPathDirectory dir;
142 NSString *str;
143 char *ptr;
144
145 switch (folder) {
146 case SDL_FOLDER_HOME:
147 base = SDL_getenv("HOME");
148
149 if (!base) {
150 SDL_SetError("No $HOME environment variable available");
151 return NULL;
152 }
153
154 goto append_slash;
155
156 case SDL_FOLDER_DESKTOP:
157 dir = NSDesktopDirectory;
158 break;
159
160 case SDL_FOLDER_DOCUMENTS:
161 dir = NSDocumentDirectory;
162 break;
163
164 case SDL_FOLDER_DOWNLOADS:
165 dir = NSDownloadsDirectory;
166 break;
167
168 case SDL_FOLDER_MUSIC:
169 dir = NSMusicDirectory;
170 break;
171
172 case SDL_FOLDER_PICTURES:
173 dir = NSPicturesDirectory;
174 break;
175
176 case SDL_FOLDER_PUBLICSHARE:
177 dir = NSSharedPublicDirectory;
178 break;
179
180 case SDL_FOLDER_SAVEDGAMES:
181 SDL_SetError("Saved games folder not supported on Cocoa");
182 return NULL;
183
184 case SDL_FOLDER_SCREENSHOTS:
185 SDL_SetError("Screenshots folder not supported on Cocoa");
186 return NULL;
187
188 case SDL_FOLDER_TEMPLATES:
189 SDL_SetError("Templates folder not supported on Cocoa");
190 return NULL;
191
192 case SDL_FOLDER_VIDEOS:
193 dir = NSMoviesDirectory;
194 break;
195
196 default:
197 SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
198 return NULL;
199 };
200
201 array = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);
202
203 if ([array count] <= 0) {
204 SDL_SetError("Directory not found");
205 return NULL;
206 }
207
208 str = [array objectAtIndex:0];
209 base = [str fileSystemRepresentation];
210 if (!base) {
211 SDL_SetError("Couldn't get folder path");
212 return NULL;
213 }
214
215append_slash:
216 result = SDL_malloc(SDL_strlen(base) + 2);
217 if (result == NULL) {
218 return NULL;
219 }
220
221 if (SDL_snprintf(result, SDL_strlen(base) + 2, "%s/", base) < 0) {
222 SDL_SetError("Couldn't snprintf folder path for Cocoa: %s", base);
223 SDL_free(result);
224 return NULL;
225 }
226
227 for (ptr = result + 1; *ptr; ptr++) {
228 if (*ptr == '/') {
229 *ptr = '\0';
230 mkdir(result, 0700);
231 *ptr = '/';
232 }
233 }
234
235 return result;
236#endif // SDL_PLATFORM_TVOS
237 }
238}
239
240#endif // SDL_FILESYSTEM_COCOA
diff --git a/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfilesystem.c
new file mode 100644
index 0000000..5634c70
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfilesystem.c
@@ -0,0 +1,54 @@
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#include "SDL_internal.h"
22
23#if defined(SDL_FILESYSTEM_DUMMY) || defined(SDL_FILESYSTEM_DISABLED)
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30char *SDL_SYS_GetBasePath(void)
31{
32 SDL_Unsupported();
33 return NULL;
34}
35
36char *SDL_SYS_GetPrefPath(const char *org, const char *app)
37{
38 SDL_Unsupported();
39 return NULL;
40}
41
42char *SDL_SYS_GetUserFolder(SDL_Folder folder)
43{
44 SDL_Unsupported();
45 return NULL;
46}
47
48char *SDL_SYS_GetCurrentDirectory(void)
49{
50 SDL_Unsupported();
51 return NULL;
52}
53
54#endif // SDL_FILESYSTEM_DUMMY || SDL_FILESYSTEM_DISABLED
diff --git a/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c b/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c
new file mode 100644
index 0000000..d8553e9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c
@@ -0,0 +1,62 @@
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#if defined(SDL_FSOPS_DUMMY)
25
26/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27// System dependent filesystem routines
28
29#include "../SDL_sysfilesystem.h"
30
31bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
32{
33 return SDL_Unsupported();
34}
35
36bool SDL_SYS_RemovePath(const char *path)
37{
38 return SDL_Unsupported();
39}
40
41bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath)
42{
43 return SDL_Unsupported();
44}
45
46bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath)
47{
48 return SDL_Unsupported();
49}
50
51bool SDL_SYS_CreateDirectory(const char *path)
52{
53 return SDL_Unsupported();
54}
55
56bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
57{
58 return SDL_Unsupported();
59}
60
61#endif // SDL_FSOPS_DUMMY
62
diff --git a/contrib/SDL-3.2.8/src/filesystem/emscripten/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/emscripten/SDL_sysfilesystem.c
new file mode 100644
index 0000000..29dc053
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/emscripten/SDL_sysfilesystem.c
@@ -0,0 +1,116 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_EMSCRIPTEN
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <errno.h>
31#include <sys/stat.h>
32
33#include <emscripten/emscripten.h>
34
35char *SDL_SYS_GetBasePath(void)
36{
37 return SDL_strdup("/");
38}
39
40char *SDL_SYS_GetPrefPath(const char *org, const char *app)
41{
42 const char *append = "/libsdl/";
43 char *result;
44 char *ptr = NULL;
45 size_t len = 0;
46
47 if (!app) {
48 SDL_InvalidParamError("app");
49 return NULL;
50 }
51 if (!org) {
52 org = "";
53 }
54
55 len = SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
56 result = (char *)SDL_malloc(len);
57 if (!result) {
58 return NULL;
59 }
60
61 if (*org) {
62 SDL_snprintf(result, len, "%s%s/%s/", append, org, app);
63 } else {
64 SDL_snprintf(result, len, "%s%s/", append, app);
65 }
66
67 for (ptr = result + 1; *ptr; ptr++) {
68 if (*ptr == '/') {
69 *ptr = '\0';
70 if (mkdir(result, 0700) != 0 && errno != EEXIST) {
71 goto error;
72 }
73 *ptr = '/';
74 }
75 }
76
77 if (mkdir(result, 0700) != 0 && errno != EEXIST) {
78 error:
79 SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno));
80 SDL_free(result);
81 return NULL;
82 }
83
84 return result;
85}
86
87char *SDL_SYS_GetUserFolder(SDL_Folder folder)
88{
89 const char *home = NULL;
90
91 if (folder != SDL_FOLDER_HOME) {
92 SDL_SetError("Emscripten only supports the home folder");
93 return NULL;
94 }
95
96 home = SDL_getenv("HOME");
97 if (!home) {
98 SDL_SetError("No $HOME environment variable available");
99 return NULL;
100 }
101
102 char *result = SDL_malloc(SDL_strlen(home) + 2);
103 if (!result) {
104 return NULL;
105 }
106
107 if (SDL_snprintf(result, SDL_strlen(home) + 2, "%s/", home) < 0) {
108 SDL_SetError("Couldn't snprintf home path for Emscripten: %s", home);
109 SDL_free(result);
110 return NULL;
111 }
112
113 return result;
114}
115
116#endif // SDL_FILESYSTEM_EMSCRIPTEN
diff --git a/contrib/SDL-3.2.8/src/filesystem/gdk/SDL_sysfilesystem.cpp b/contrib/SDL-3.2.8/src/filesystem/gdk/SDL_sysfilesystem.cpp
new file mode 100644
index 0000000..ffafe43
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/gdk/SDL_sysfilesystem.cpp
@@ -0,0 +1,150 @@
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#include "SDL_internal.h"
22
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24// System dependent filesystem routines
25
26extern "C" {
27#include "../SDL_sysfilesystem.h"
28}
29
30#include "../../core/windows/SDL_windows.h"
31#include <SDL3/SDL_hints.h>
32#include <SDL3/SDL_system.h>
33#include <SDL3/SDL_filesystem.h>
34#include <XGameSaveFiles.h>
35
36char *
37SDL_SYS_GetBasePath(void)
38{
39 /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()!
40 * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o
41 */
42 DWORD buflen = 128;
43 CHAR *path = NULL;
44 DWORD len = 0;
45 int i;
46
47 while (true) {
48 void *ptr = SDL_realloc(path, buflen * sizeof(CHAR));
49 if (!ptr) {
50 SDL_free(path);
51 return NULL;
52 }
53
54 path = (CHAR *)ptr;
55
56 len = GetModuleFileNameA(NULL, path, buflen);
57 // if it truncated, then len >= buflen - 1
58 // if there was enough room (or failure), len < buflen - 1
59 if (len < buflen - 1) {
60 break;
61 }
62
63 // buffer too small? Try again.
64 buflen *= 2;
65 }
66
67 if (len == 0) {
68 SDL_free(path);
69 WIN_SetError("Couldn't locate our .exe");
70 return NULL;
71 }
72
73 for (i = len - 1; i > 0; i--) {
74 if (path[i] == '\\') {
75 break;
76 }
77 }
78
79 SDL_assert(i > 0); // Should have been an absolute path.
80 path[i + 1] = '\0'; // chop off filename.
81
82 return path;
83}
84
85char *SDL_SYS_GetPrefPath(const char *org, const char *app)
86{
87 XUserHandle user = NULL;
88 XAsyncBlock block = { 0 };
89 char *folderPath;
90 HRESULT result;
91 const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID");
92
93 if (!app) {
94 SDL_InvalidParamError("app");
95 return NULL;
96 }
97
98 // This should be set before calling SDL_GetPrefPath!
99 if (!csid) {
100 SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!");
101 return SDL_strdup("T:\\");
102 }
103
104 if (!SDL_GetGDKDefaultUser(&user)) {
105 // Error already set, just return
106 return NULL;
107 }
108
109 if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) {
110 WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result);
111 return NULL;
112 }
113
114 folderPath = (char*) SDL_malloc(MAX_PATH);
115 do {
116 result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath);
117 } while (result == E_PENDING);
118 if (FAILED(result)) {
119 WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result);
120 SDL_free(folderPath);
121 return NULL;
122 }
123
124 /* We aren't using 'app' here because the container rules are a lot more
125 * strict than the NTFS rules, so it will most likely be invalid :(
126 */
127 SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH);
128 if (CreateDirectoryA(folderPath, NULL) == FALSE) {
129 if (GetLastError() != ERROR_ALREADY_EXISTS) {
130 WIN_SetError("CreateDirectoryA");
131 SDL_free(folderPath);
132 return NULL;
133 }
134 }
135 return folderPath;
136}
137
138// TODO
139char *SDL_SYS_GetUserFolder(SDL_Folder folder)
140{
141 SDL_Unsupported();
142 return NULL;
143}
144
145// TODO
146char *SDL_SYS_GetCurrentDirectory(void)
147{
148 SDL_Unsupported();
149 return NULL;
150}
diff --git a/contrib/SDL-3.2.8/src/filesystem/haiku/SDL_sysfilesystem.cc b/contrib/SDL-3.2.8/src/filesystem/haiku/SDL_sysfilesystem.cc
new file mode 100644
index 0000000..af8b5ab
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/haiku/SDL_sysfilesystem.cc
@@ -0,0 +1,156 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_HAIKU
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28extern "C" {
29#include "../SDL_sysfilesystem.h"
30}
31
32#include <kernel/image.h>
33#include <storage/Directory.h>
34#include <storage/Entry.h>
35#include <storage/FindDirectory.h>
36#include <storage/Path.h>
37
38
39char *SDL_SYS_GetBasePath(void)
40{
41 char name[MAXPATHLEN];
42
43 if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, name, sizeof(name)) != B_OK) {
44 return NULL;
45 }
46
47 BEntry entry(name, true);
48 BPath path;
49 status_t rc = entry.GetPath(&path); // (path) now has binary's path.
50 SDL_assert(rc == B_OK);
51 rc = path.GetParent(&path); // chop filename, keep directory.
52 SDL_assert(rc == B_OK);
53 const char *str = path.Path();
54 SDL_assert(str != NULL);
55
56 const size_t len = SDL_strlen(str);
57 char *result = (char *) SDL_malloc(len + 2);
58 if (result) {
59 SDL_memcpy(result, str, len);
60 result[len] = '/';
61 result[len+1] = '\0';
62 }
63
64 return result;
65}
66
67
68char *SDL_SYS_GetPrefPath(const char *org, const char *app)
69{
70 // !!! FIXME: is there a better way to do this?
71 const char *home = SDL_getenv("HOME");
72 const char *append = "/config/settings/";
73 size_t len = SDL_strlen(home);
74
75 if (!app) {
76 SDL_InvalidParamError("app");
77 return NULL;
78 }
79 if (!org) {
80 org = "";
81 }
82
83 if (!len || (home[len - 1] == '/')) {
84 ++append; // home empty or ends with separator, skip the one from append
85 }
86 len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
87 char *result = (char *) SDL_malloc(len);
88 if (result) {
89 if (*org) {
90 SDL_snprintf(result, len, "%s%s%s/%s/", home, append, org, app);
91 } else {
92 SDL_snprintf(result, len, "%s%s%s/", home, append, app);
93 }
94 create_directory(result, 0700); // Haiku api: creates missing dirs
95 }
96
97 return result;
98}
99
100char *SDL_SYS_GetUserFolder(SDL_Folder folder)
101{
102 const char *home = NULL;
103 char *result;
104
105 home = SDL_getenv("HOME");
106 if (!home) {
107 SDL_SetError("No $HOME environment variable available");
108 return NULL;
109 }
110
111 switch (folder) {
112 case SDL_FOLDER_HOME:
113 result = (char *) SDL_malloc(SDL_strlen(home) + 2);
114 if (!result) {
115 return NULL;
116 }
117
118 if (SDL_snprintf(result, SDL_strlen(home) + 2, "%s/", home) < 0) {
119 SDL_SetError("Couldn't snprintf home path for Haiku: %s", home);
120 SDL_free(result);
121 return NULL;
122 }
123
124 return result;
125
126 // TODO: Is Haiku's desktop folder always ~/Desktop/ ?
127 case SDL_FOLDER_DESKTOP:
128 result = (char *) SDL_malloc(SDL_strlen(home) + 10);
129 if (!result) {
130 return NULL;
131 }
132
133 if (SDL_snprintf(result, SDL_strlen(home) + 10, "%s/Desktop/", home) < 0) {
134 SDL_SetError("Couldn't snprintf desktop path for Haiku: %s/Desktop/", home);
135 SDL_free(result);
136 return NULL;
137 }
138
139 return result;
140
141 case SDL_FOLDER_DOCUMENTS:
142 case SDL_FOLDER_DOWNLOADS:
143 case SDL_FOLDER_MUSIC:
144 case SDL_FOLDER_PICTURES:
145 case SDL_FOLDER_PUBLICSHARE:
146 case SDL_FOLDER_SAVEDGAMES:
147 case SDL_FOLDER_SCREENSHOTS:
148 case SDL_FOLDER_TEMPLATES:
149 case SDL_FOLDER_VIDEOS:
150 default:
151 SDL_SetError("Only HOME and DESKTOP available on Haiku");
152 return NULL;
153 }
154}
155
156#endif // SDL_FILESYSTEM_HAIKU
diff --git a/contrib/SDL-3.2.8/src/filesystem/n3ds/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/n3ds/SDL_sysfilesystem.c
new file mode 100644
index 0000000..8386a91
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/n3ds/SDL_sysfilesystem.c
@@ -0,0 +1,90 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_N3DS
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <3ds.h>
31#include <dirent.h>
32#include <errno.h>
33
34static char *MakePrefPath(const char *app);
35static bool CreatePrefPathDir(const char *pref);
36
37char *SDL_SYS_GetBasePath(void)
38{
39 char *base_path = SDL_strdup("romfs:/");
40 return base_path;
41}
42
43char *SDL_SYS_GetPrefPath(const char *org, const char *app)
44{
45 char *pref_path = NULL;
46 if (!app) {
47 SDL_InvalidParamError("app");
48 return NULL;
49 }
50
51 pref_path = MakePrefPath(app);
52 if (!pref_path) {
53 return NULL;
54 }
55
56 if (!CreatePrefPathDir(pref_path)) {
57 SDL_free(pref_path);
58 return NULL;
59 }
60
61 return pref_path;
62}
63
64// TODO
65char *SDL_SYS_GetUserFolder(SDL_Folder folder)
66{
67 SDL_Unsupported();
68 return NULL;
69}
70
71static char *MakePrefPath(const char *app)
72{
73 char *pref_path;
74 if (SDL_asprintf(&pref_path, "sdmc:/3ds/%s/", app) < 0) {
75 return NULL;
76 }
77 return pref_path;
78}
79
80static bool CreatePrefPathDir(const char *pref)
81{
82 int result = mkdir(pref, 0666);
83
84 if (result == -1 && errno != EEXIST) {
85 return SDL_SetError("Failed to create '%s' (%s)", pref, strerror(errno));
86 }
87 return true;
88}
89
90#endif // SDL_FILESYSTEM_N3DS
diff --git a/contrib/SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c b/contrib/SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c
new file mode 100644
index 0000000..015b8d4
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c
@@ -0,0 +1,246 @@
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#if defined(SDL_FSOPS_POSIX)
25
26/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27// System dependent filesystem routines
28
29#include "../SDL_sysfilesystem.h"
30
31#include <stdio.h>
32#include <string.h>
33#include <errno.h>
34#include <dirent.h>
35#include <sys/stat.h>
36#include <unistd.h>
37
38bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
39{
40 char *pathwithsep = NULL;
41 int pathwithseplen = SDL_asprintf(&pathwithsep, "%s/", path);
42 if ((pathwithseplen == -1) || (!pathwithsep)) {
43 return false;
44 }
45
46 // trim down to a single path separator at the end, in case the caller added one or more.
47 pathwithseplen--;
48 while ((pathwithseplen >= 0) && (pathwithsep[pathwithseplen] == '/')) {
49 pathwithsep[pathwithseplen--] = '\0';
50 }
51
52 DIR *dir = opendir(pathwithsep);
53 if (!dir) {
54 SDL_free(pathwithsep);
55 return SDL_SetError("Can't open directory: %s", strerror(errno));
56 }
57
58 // make sure there's a path separator at the end now for the actual callback.
59 pathwithsep[++pathwithseplen] = '/';
60 pathwithsep[++pathwithseplen] = '\0';
61
62 SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
63 struct dirent *ent;
64 while ((result == SDL_ENUM_CONTINUE) && ((ent = readdir(dir)) != NULL)) {
65 const char *name = ent->d_name;
66 if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
67 continue;
68 }
69 result = cb(userdata, pathwithsep, name);
70 }
71
72 closedir(dir);
73
74 SDL_free(pathwithsep);
75
76 return (result != SDL_ENUM_FAILURE);
77}
78
79bool SDL_SYS_RemovePath(const char *path)
80{
81 int rc = remove(path);
82 if (rc < 0) {
83 if (errno == ENOENT) {
84 // It's already gone, this is a success
85 return true;
86 }
87 return SDL_SetError("Can't remove path: %s", strerror(errno));
88 }
89 return true;
90}
91
92bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath)
93{
94 if (rename(oldpath, newpath) < 0) {
95 return SDL_SetError("Can't rename path: %s", strerror(errno));
96 }
97 return true;
98}
99
100bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath)
101{
102 char *buffer = NULL;
103 SDL_IOStream *input = NULL;
104 SDL_IOStream *output = NULL;
105 const size_t maxlen = 4096;
106 size_t len;
107 bool result = false;
108
109 input = SDL_IOFromFile(oldpath, "rb");
110 if (!input) {
111 goto done;
112 }
113
114 output = SDL_IOFromFile(newpath, "wb");
115 if (!output) {
116 goto done;
117 }
118
119 buffer = (char *)SDL_malloc(maxlen);
120 if (!buffer) {
121 goto done;
122 }
123
124 while ((len = SDL_ReadIO(input, buffer, maxlen)) > 0) {
125 if (SDL_WriteIO(output, buffer, len) < len) {
126 goto done;
127 }
128 }
129 if (SDL_GetIOStatus(input) != SDL_IO_STATUS_EOF) {
130 goto done;
131 }
132
133 SDL_CloseIO(input);
134 input = NULL;
135
136 if (!SDL_FlushIO(output)) {
137 goto done;
138 }
139
140 result = SDL_CloseIO(output);
141 output = NULL; // it's gone, even if it failed.
142
143done:
144 if (output) {
145 SDL_CloseIO(output);
146 }
147 if (input) {
148 SDL_CloseIO(input);
149 }
150 SDL_free(buffer);
151
152 return result;
153}
154
155bool SDL_SYS_CreateDirectory(const char *path)
156{
157 const int rc = mkdir(path, 0770);
158 if (rc < 0) {
159 const int origerrno = errno;
160 if (origerrno == EEXIST) {
161 struct stat statbuf;
162 if ((stat(path, &statbuf) == 0) && (S_ISDIR(statbuf.st_mode))) {
163 return true; // it already exists and it's a directory, consider it success.
164 }
165 }
166 return SDL_SetError("Can't create directory: %s", strerror(origerrno));
167 }
168 return true;
169}
170
171bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
172{
173 struct stat statbuf;
174 const int rc = stat(path, &statbuf);
175 if (rc < 0) {
176 return SDL_SetError("Can't stat: %s", strerror(errno));
177 } else if (S_ISREG(statbuf.st_mode)) {
178 info->type = SDL_PATHTYPE_FILE;
179 info->size = (Uint64) statbuf.st_size;
180 } else if (S_ISDIR(statbuf.st_mode)) {
181 info->type = SDL_PATHTYPE_DIRECTORY;
182 info->size = 0;
183 } else {
184 info->type = SDL_PATHTYPE_OTHER;
185 info->size = (Uint64) statbuf.st_size;
186 }
187
188#if defined(HAVE_ST_MTIM)
189 // POSIX.1-2008 standard
190 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctim.tv_sec) + statbuf.st_ctim.tv_nsec;
191 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtim.tv_sec) + statbuf.st_mtim.tv_nsec;
192 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atim.tv_sec) + statbuf.st_atim.tv_nsec;
193#elif defined(SDL_PLATFORM_APPLE)
194 /* Apple platform stat structs use 'st_*timespec' naming. */
195 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctimespec.tv_sec) + statbuf.st_ctimespec.tv_nsec;
196 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtimespec.tv_sec) + statbuf.st_mtimespec.tv_nsec;
197 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atimespec.tv_sec) + statbuf.st_atimespec.tv_nsec;
198#else
199 info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctime);
200 info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtime);
201 info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atime);
202#endif
203 return true;
204}
205
206// Note that this isn't actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code.
207char *SDL_SYS_GetCurrentDirectory(void)
208{
209 size_t buflen = 64;
210 char *buf = NULL;
211
212 while (true) {
213 void *ptr = SDL_realloc(buf, buflen);
214 if (!ptr) {
215 SDL_free(buf);
216 return NULL;
217 }
218 buf = (char *) ptr;
219
220 if (getcwd(buf, buflen-1) != NULL) {
221 break; // we got it!
222 }
223
224 if (errno == ERANGE) {
225 buflen *= 2; // try again with a bigger buffer.
226 continue;
227 }
228
229 SDL_free(buf);
230 SDL_SetError("getcwd failed: %s", strerror(errno));
231 return NULL;
232 }
233
234 // make sure there's a path separator at the end.
235 SDL_assert(SDL_strlen(buf) < (buflen + 2));
236 buflen = SDL_strlen(buf);
237 if ((buflen == 0) || (buf[buflen-1] != '/')) {
238 buf[buflen] = '/';
239 buf[buflen + 1] = '\0';
240 }
241
242 return buf;
243}
244
245#endif // SDL_FSOPS_POSIX
246
diff --git a/contrib/SDL-3.2.8/src/filesystem/ps2/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/ps2/SDL_sysfilesystem.c
new file mode 100644
index 0000000..ca69c2b
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/ps2/SDL_sysfilesystem.c
@@ -0,0 +1,119 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_PS2
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <sys/stat.h>
31#include <unistd.h>
32
33char *SDL_SYS_GetBasePath(void)
34{
35 char *result = NULL;
36 size_t len;
37 char cwd[FILENAME_MAX];
38
39 getcwd(cwd, sizeof(cwd));
40 len = SDL_strlen(cwd) + 2;
41 result = (char *)SDL_malloc(len);
42 if (result) {
43 SDL_snprintf(result, len, "%s/", cwd);
44 }
45
46 return result;
47}
48
49// Do a recursive mkdir of parents folders
50static void recursive_mkdir(const char *dir)
51{
52 char tmp[FILENAME_MAX];
53 const char *base = SDL_GetBasePath();
54 char *p = NULL;
55 size_t len;
56
57 SDL_snprintf(tmp, sizeof(tmp), "%s", dir);
58 len = SDL_strlen(tmp);
59 if (tmp[len - 1] == '/') {
60 tmp[len - 1] = 0;
61 }
62
63 for (p = tmp + 1; *p; p++) {
64 if (*p == '/') {
65 *p = 0;
66 // Just creating subfolders from current path
67 if (base && SDL_strstr(tmp, base) != NULL) {
68 mkdir(tmp, S_IRWXU);
69 }
70
71 *p = '/';
72 }
73 }
74
75 mkdir(tmp, S_IRWXU);
76}
77
78char *SDL_SYS_GetPrefPath(const char *org, const char *app)
79{
80 char *result = NULL;
81 size_t len;
82
83 if (!app) {
84 SDL_InvalidParamError("app");
85 return NULL;
86 }
87
88 if (!org) {
89 org = "";
90 }
91
92 const char *base = SDL_GetBasePath();
93 if (!base) {
94 return NULL;
95 }
96
97 len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4;
98 result = (char *)SDL_malloc(len);
99 if (result) {
100 if (*org) {
101 SDL_snprintf(result, len, "%s%s/%s/", base, org, app);
102 } else {
103 SDL_snprintf(result, len, "%s%s/", base, app);
104 }
105
106 recursive_mkdir(result);
107 }
108
109 return result;
110}
111
112// TODO
113char *SDL_SYS_GetUserFolder(SDL_Folder folder)
114{
115 SDL_Unsupported();
116 return NULL;
117}
118
119#endif // SDL_FILESYSTEM_PS2
diff --git a/contrib/SDL-3.2.8/src/filesystem/psp/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/psp/SDL_sysfilesystem.c
new file mode 100644
index 0000000..4b40055
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/psp/SDL_sysfilesystem.c
@@ -0,0 +1,89 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_PSP
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <sys/stat.h>
31#include <unistd.h>
32
33char *SDL_SYS_GetBasePath(void)
34{
35 char *result = NULL;
36 size_t len;
37 char cwd[FILENAME_MAX];
38
39 getcwd(cwd, sizeof(cwd));
40 len = SDL_strlen(cwd) + 2;
41 result = (char *)SDL_malloc(len);
42 if (result) {
43 SDL_snprintf(result, len, "%s/", cwd);
44 }
45
46 return result;
47}
48
49char *SDL_SYS_GetPrefPath(const char *org, const char *app)
50{
51 char *result = NULL;
52 size_t len;
53 if (!app) {
54 SDL_InvalidParamError("app");
55 return NULL;
56 }
57
58 const char *base = SDL_GetBasePath();
59 if (!base) {
60 return NULL;
61 }
62
63 if (!org) {
64 org = "";
65 }
66
67 len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4;
68 result = (char *)SDL_malloc(len);
69 if (result) {
70 if (*org) {
71 SDL_snprintf(result, len, "%s%s/%s/", base, org, app);
72 } else {
73 SDL_snprintf(result, len, "%s%s/", base, app);
74 }
75
76 mkdir(result, 0755);
77 }
78
79 return result;
80}
81
82// TODO
83char *SDL_SYS_GetUserFolder(SDL_Folder folder)
84{
85 SDL_Unsupported();
86 return NULL;
87}
88
89#endif // SDL_FILESYSTEM_PSP
diff --git a/contrib/SDL-3.2.8/src/filesystem/riscos/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/riscos/SDL_sysfilesystem.c
new file mode 100644
index 0000000..95ed80f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/riscos/SDL_sysfilesystem.c
@@ -0,0 +1,208 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_RISCOS
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <kernel.h>
31#include <swis.h>
32#include <unixlib/local.h>
33
34// Wrapper around __unixify_std that uses SDL's memory allocators
35static char *SDL_unixify_std(const char *ro_path, char *buffer, size_t buf_len, int filetype)
36{
37 const char *const in_buf = buffer; // = NULL if we allocate the buffer.
38
39 if (!buffer) {
40 /* This matches the logic in __unixify, with an additional byte for the
41 * extra path separator.
42 */
43 buf_len = SDL_strlen(ro_path) + 14 + 1;
44 buffer = SDL_malloc(buf_len);
45
46 if (!buffer) {
47 return NULL;
48 }
49 }
50
51 if (!__unixify_std(ro_path, buffer, buf_len, filetype)) {
52 if (!in_buf) {
53 SDL_free(buffer);
54 }
55
56 SDL_SetError("Could not convert '%s' to a Unix-style path", ro_path);
57 return NULL;
58 }
59
60 /* HACK: It's necessary to add an extra path separator here since SDL's API
61 * requires it, however paths with trailing separators aren't normally valid
62 * on RISC OS.
63 */
64 if (__get_riscosify_control() & __RISCOSIFY_NO_PROCESS)
65 SDL_strlcat(buffer, ".", buf_len);
66 else
67 SDL_strlcat(buffer, "/", buf_len);
68
69 return buffer;
70}
71
72static char *canonicalisePath(const char *path, const char *pathVar)
73{
74 _kernel_oserror *error;
75 _kernel_swi_regs regs;
76 char *buf;
77
78 regs.r[0] = 37;
79 regs.r[1] = (int)path;
80 regs.r[2] = 0;
81 regs.r[3] = (int)pathVar;
82 regs.r[4] = 0;
83 regs.r[5] = 0;
84 error = _kernel_swi(OS_FSControl, &regs, &regs);
85 if (error) {
86 SDL_SetError("Couldn't canonicalise path: %s", error->errmess);
87 return NULL;
88 }
89
90 regs.r[5] = 1 - regs.r[5];
91 buf = SDL_malloc(regs.r[5]);
92 if (!buf) {
93 return NULL;
94 }
95 regs.r[2] = (int)buf;
96 error = _kernel_swi(OS_FSControl, &regs, &regs);
97 if (error) {
98 SDL_SetError("Couldn't canonicalise path: %s", error->errmess);
99 SDL_free(buf);
100 return NULL;
101 }
102
103 return buf;
104}
105
106static _kernel_oserror *createDirectoryRecursive(char *path)
107{
108 char *ptr = NULL;
109 _kernel_oserror *error;
110 _kernel_swi_regs regs;
111 regs.r[0] = 8;
112 regs.r[1] = (int)path;
113 regs.r[2] = 0;
114
115 for (ptr = path + 1; *ptr; ptr++) {
116 if (*ptr == '.') {
117 *ptr = '\0';
118 error = _kernel_swi(OS_File, &regs, &regs);
119 *ptr = '.';
120 if (error) {
121 return error;
122 }
123 }
124 }
125 return _kernel_swi(OS_File, &regs, &regs);
126}
127
128char *SDL_SYS_GetBasePath(void)
129{
130 _kernel_swi_regs regs;
131 _kernel_oserror *error;
132 char *canon, *ptr, *result;
133
134 error = _kernel_swi(OS_GetEnv, &regs, &regs);
135 if (error) {
136 return NULL;
137 }
138
139 canon = canonicalisePath((const char *)regs.r[0], "Run$Path");
140 if (!canon) {
141 return NULL;
142 }
143
144 // chop off filename.
145 ptr = SDL_strrchr(canon, '.');
146 if (ptr) {
147 *ptr = '\0';
148 }
149
150 result = SDL_unixify_std(canon, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED);
151 SDL_free(canon);
152 return result;
153}
154
155char *SDL_SYS_GetPrefPath(const char *org, const char *app)
156{
157 char *canon, *dir, *result;
158 size_t len;
159 _kernel_oserror *error;
160
161 if (!app) {
162 SDL_InvalidParamError("app");
163 return NULL;
164 }
165 if (!org) {
166 org = "";
167 }
168
169 canon = canonicalisePath("<Choices$Write>", "Run$Path");
170 if (!canon) {
171 return NULL;
172 }
173
174 len = SDL_strlen(canon) + SDL_strlen(org) + SDL_strlen(app) + 4;
175 dir = (char *)SDL_malloc(len);
176 if (!dir) {
177 SDL_free(canon);
178 return NULL;
179 }
180
181 if (*org) {
182 SDL_snprintf(dir, len, "%s.%s.%s", canon, org, app);
183 } else {
184 SDL_snprintf(dir, len, "%s.%s", canon, app);
185 }
186
187 SDL_free(canon);
188
189 error = createDirectoryRecursive(dir);
190 if (error) {
191 SDL_SetError("Couldn't create directory: %s", error->errmess);
192 SDL_free(dir);
193 return NULL;
194 }
195
196 result = SDL_unixify_std(dir, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED);
197 SDL_free(dir);
198 return result;
199}
200
201// TODO
202char *SDL_SYS_GetUserFolder(SDL_Folder folder)
203{
204 SDL_Unsupported();
205 return NULL;
206}
207
208#endif // SDL_FILESYSTEM_RISCOS
diff --git a/contrib/SDL-3.2.8/src/filesystem/unix/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/unix/SDL_sysfilesystem.c
new file mode 100644
index 0000000..b0f2dd5
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/unix/SDL_sysfilesystem.c
@@ -0,0 +1,618 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_UNIX
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <stdio.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <dirent.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <limits.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#if defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD)
42#include <sys/sysctl.h>
43#endif
44
45static char *readSymLink(const char *path)
46{
47 char *result = NULL;
48 ssize_t len = 64;
49 ssize_t rc = -1;
50
51 while (1) {
52 char *ptr = (char *)SDL_realloc(result, (size_t)len);
53 if (!ptr) {
54 break;
55 }
56
57 result = ptr;
58
59 rc = readlink(path, result, len);
60 if (rc == -1) {
61 break; // not a symlink, i/o error, etc.
62 } else if (rc < len) {
63 result[rc] = '\0'; // readlink doesn't null-terminate.
64 return result; // we're good to go.
65 }
66
67 len *= 2; // grow buffer, try again.
68 }
69
70 SDL_free(result);
71 return NULL;
72}
73
74#ifdef SDL_PLATFORM_OPENBSD
75static char *search_path_for_binary(const char *bin)
76{
77 const char *envr_real = SDL_getenv("PATH");
78 char *envr;
79 size_t alloc_size;
80 char *exe = NULL;
81 char *start = envr;
82 char *ptr;
83
84 if (!envr_real) {
85 SDL_SetError("No $PATH set");
86 return NULL;
87 }
88
89 envr = SDL_strdup(envr_real);
90 if (!envr) {
91 return NULL;
92 }
93
94 SDL_assert(bin != NULL);
95
96 alloc_size = SDL_strlen(bin) + SDL_strlen(envr) + 2;
97 exe = (char *)SDL_malloc(alloc_size);
98
99 do {
100 ptr = SDL_strchr(start, ':'); // find next $PATH separator.
101 if (ptr != start) {
102 if (ptr) {
103 *ptr = '\0';
104 }
105
106 // build full binary path...
107 SDL_snprintf(exe, alloc_size, "%s%s%s", start, (ptr && (ptr[-1] == '/')) ? "" : "/", bin);
108
109 if (access(exe, X_OK) == 0) { // Exists as executable? We're done.
110 SDL_free(envr);
111 return exe;
112 }
113 }
114 start = ptr + 1; // start points to beginning of next element.
115 } while (ptr);
116
117 SDL_free(envr);
118 SDL_free(exe);
119
120 SDL_SetError("Process not found in $PATH");
121 return NULL; // doesn't exist in path.
122}
123#endif
124
125char *SDL_SYS_GetBasePath(void)
126{
127 char *result = NULL;
128
129#ifdef SDL_PLATFORM_FREEBSD
130 char fullpath[PATH_MAX];
131 size_t buflen = sizeof(fullpath);
132 const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
133 if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) {
134 result = SDL_strdup(fullpath);
135 if (!result) {
136 return NULL;
137 }
138 }
139#endif
140#ifdef SDL_PLATFORM_OPENBSD
141 // Please note that this will fail if the process was launched with a relative path and $PWD + the cwd have changed, or argv is altered. So don't do that. Or add a new sysctl to OpenBSD.
142 char **cmdline;
143 size_t len;
144 const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
145 if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
146 char *exe, *pwddst;
147 char *realpathbuf = (char *)SDL_malloc(PATH_MAX + 1);
148 if (!realpathbuf) {
149 return NULL;
150 }
151
152 cmdline = SDL_malloc(len);
153 if (!cmdline) {
154 SDL_free(realpathbuf);
155 return NULL;
156 }
157
158 sysctl(mib, 4, cmdline, &len, NULL, 0);
159
160 exe = cmdline[0];
161 pwddst = NULL;
162 if (SDL_strchr(exe, '/') == NULL) { // not a relative or absolute path, check $PATH for it
163 exe = search_path_for_binary(cmdline[0]);
164 } else {
165 if (exe && *exe == '.') {
166 const char *pwd = SDL_getenv("PWD");
167 if (pwd && *pwd) {
168 SDL_asprintf(&pwddst, "%s/%s", pwd, exe);
169 }
170 }
171 }
172
173 if (exe) {
174 if (!pwddst) {
175 if (realpath(exe, realpathbuf) != NULL) {
176 result = realpathbuf;
177 }
178 } else {
179 if (realpath(pwddst, realpathbuf) != NULL) {
180 result = realpathbuf;
181 }
182 SDL_free(pwddst);
183 }
184
185 if (exe != cmdline[0]) {
186 SDL_free(exe);
187 }
188 }
189
190 if (!result) {
191 SDL_free(realpathbuf);
192 }
193
194 SDL_free(cmdline);
195 }
196#endif
197
198 // is a Linux-style /proc filesystem available?
199 if (!result && (access("/proc", F_OK) == 0)) {
200 /* !!! FIXME: after 2.0.6 ships, let's delete this code and just
201 use the /proc/%llu version. There's no reason to have
202 two copies of this plus all the #ifdefs. --ryan. */
203#ifdef SDL_PLATFORM_FREEBSD
204 result = readSymLink("/proc/curproc/file");
205#elif defined(SDL_PLATFORM_NETBSD)
206 result = readSymLink("/proc/curproc/exe");
207#elif defined(SDL_PLATFORM_SOLARIS)
208 result = readSymLink("/proc/self/path/a.out");
209#else
210 result = readSymLink("/proc/self/exe"); // linux.
211 if (!result) {
212 // older kernels don't have /proc/self ... try PID version...
213 char path[64];
214 const int rc = SDL_snprintf(path, sizeof(path),
215 "/proc/%llu/exe",
216 (unsigned long long)getpid());
217 if ((rc > 0) && (rc < sizeof(path))) {
218 result = readSymLink(path);
219 }
220 }
221#endif
222 }
223
224#ifdef SDL_PLATFORM_SOLARIS // try this as a fallback if /proc didn't pan out
225 if (!result) {
226 const char *path = getexecname();
227 if ((path) && (path[0] == '/')) { // must be absolute path...
228 result = SDL_strdup(path);
229 if (!result) {
230 return NULL;
231 }
232 }
233 }
234#endif
235 /* If we had access to argv[0] here, we could check it for a path,
236 or troll through $PATH looking for it, too. */
237
238 if (result) { // chop off filename.
239 char *ptr = SDL_strrchr(result, '/');
240 if (ptr) {
241 *(ptr + 1) = '\0';
242 } else { // shouldn't happen, but just in case...
243 SDL_free(result);
244 result = NULL;
245 }
246 }
247
248 if (result) {
249 // try to shrink buffer...
250 char *ptr = (char *)SDL_realloc(result, SDL_strlen(result) + 1);
251 if (ptr) {
252 result = ptr; // oh well if it failed.
253 }
254 }
255
256 return result;
257}
258
259char *SDL_SYS_GetPrefPath(const char *org, const char *app)
260{
261 /*
262 * We use XDG's base directory spec, even if you're not on Linux.
263 * This isn't strictly correct, but the results are relatively sane
264 * in any case.
265 *
266 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
267 */
268 const char *envr = SDL_getenv("XDG_DATA_HOME");
269 const char *append;
270 char *result = NULL;
271 char *ptr = NULL;
272 size_t len = 0;
273
274 if (!app) {
275 SDL_InvalidParamError("app");
276 return NULL;
277 }
278 if (!org) {
279 org = "";
280 }
281
282 if (!envr) {
283 // You end up with "$HOME/.local/share/Game Name 2"
284 envr = SDL_getenv("HOME");
285 if (!envr) {
286 // we could take heroic measures with /etc/passwd, but oh well.
287 SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
288 return NULL;
289 }
290 append = "/.local/share/";
291 } else {
292 append = "/";
293 }
294
295 len = SDL_strlen(envr);
296 if (envr[len - 1] == '/') {
297 append += 1;
298 }
299
300 len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
301 result = (char *)SDL_malloc(len);
302 if (!result) {
303 return NULL;
304 }
305
306 if (*org) {
307 (void)SDL_snprintf(result, len, "%s%s%s/%s/", envr, append, org, app);
308 } else {
309 (void)SDL_snprintf(result, len, "%s%s%s/", envr, append, app);
310 }
311
312 for (ptr = result + 1; *ptr; ptr++) {
313 if (*ptr == '/') {
314 *ptr = '\0';
315 if (mkdir(result, 0700) != 0 && errno != EEXIST) {
316 goto error;
317 }
318 *ptr = '/';
319 }
320 }
321 if (mkdir(result, 0700) != 0 && errno != EEXIST) {
322 error:
323 SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno));
324 SDL_free(result);
325 return NULL;
326 }
327
328 return result;
329}
330
331/*
332 The two functions below (prefixed with `xdg_`) have been copied from:
333 https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/blob/master/xdg-user-dir-lookup.c
334 and have been adapted to work with SDL. They are licensed under the following
335 terms:
336
337 Copyright (c) 2007 Red Hat, Inc.
338
339 Permission is hereby granted, free of charge, to any person
340 obtaining a copy of this software and associated documentation files
341 (the "Software"), to deal in the Software without restriction,
342 including without limitation the rights to use, copy, modify, merge,
343 publish, distribute, sublicense, and/or sell copies of the Software,
344 and to permit persons to whom the Software is furnished to do so,
345 subject to the following conditions:
346
347 The above copyright notice and this permission notice shall be
348 included in all copies or substantial portions of the Software.
349
350 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
351 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
352 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
353 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
354 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
355 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
356 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
357 SOFTWARE.
358*/
359static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback)
360{
361 FILE *file;
362 const char *home_dir, *config_home;
363 char *config_file;
364 char buffer[512];
365 char *user_dir;
366 char *p, *d;
367 int len;
368 int relative;
369 size_t l;
370
371 home_dir = SDL_getenv("HOME");
372
373 if (!home_dir)
374 goto error;
375
376 config_home = SDL_getenv("XDG_CONFIG_HOME");
377 if (!config_home || config_home[0] == 0)
378 {
379 l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1;
380 config_file = (char*) SDL_malloc (l);
381 if (!config_file)
382 goto error;
383
384 SDL_strlcpy (config_file, home_dir, l);
385 SDL_strlcat (config_file, "/.config/user-dirs.dirs", l);
386 }
387 else
388 {
389 l = SDL_strlen (config_home) + SDL_strlen ("/user-dirs.dirs") + 1;
390 config_file = (char*) SDL_malloc (l);
391 if (!config_file)
392 goto error;
393
394 SDL_strlcpy (config_file, config_home, l);
395 SDL_strlcat (config_file, "/user-dirs.dirs", l);
396 }
397
398 file = fopen (config_file, "r");
399 SDL_free (config_file);
400 if (!file)
401 goto error;
402
403 user_dir = NULL;
404 while (fgets (buffer, sizeof (buffer), file))
405 {
406 // Remove newline at end
407 len = SDL_strlen (buffer);
408 if (len > 0 && buffer[len-1] == '\n')
409 buffer[len-1] = 0;
410
411 p = buffer;
412 while (*p == ' ' || *p == '\t')
413 p++;
414
415 if (SDL_strncmp (p, "XDG_", 4) != 0)
416 continue;
417 p += 4;
418 if (SDL_strncmp (p, type, SDL_strlen (type)) != 0)
419 continue;
420 p += SDL_strlen (type);
421 if (SDL_strncmp (p, "_DIR", 4) != 0)
422 continue;
423 p += 4;
424
425 while (*p == ' ' || *p == '\t')
426 p++;
427
428 if (*p != '=')
429 continue;
430 p++;
431
432 while (*p == ' ' || *p == '\t')
433 p++;
434
435 if (*p != '"')
436 continue;
437 p++;
438
439 relative = 0;
440 if (SDL_strncmp (p, "$HOME/", 6) == 0)
441 {
442 p += 6;
443 relative = 1;
444 }
445 else if (*p != '/')
446 continue;
447
448 SDL_free (user_dir);
449 if (relative)
450 {
451 l = SDL_strlen (home_dir) + 1 + SDL_strlen (p) + 1;
452 user_dir = (char*) SDL_malloc (l);
453 if (!user_dir)
454 goto error2;
455
456 SDL_strlcpy (user_dir, home_dir, l);
457 SDL_strlcat (user_dir, "/", l);
458 }
459 else
460 {
461 user_dir = (char*) SDL_malloc (SDL_strlen (p) + 1);
462 if (!user_dir)
463 goto error2;
464
465 *user_dir = 0;
466 }
467
468 d = user_dir + SDL_strlen (user_dir);
469 while (*p && *p != '"')
470 {
471 if ((*p == '\\') && (*(p+1) != 0))
472 p++;
473 *d++ = *p++;
474 }
475 *d = 0;
476 }
477error2:
478 fclose (file);
479
480 if (user_dir)
481 return user_dir;
482
483 error:
484 if (fallback)
485 return SDL_strdup (fallback);
486 return NULL;
487}
488
489static char *xdg_user_dir_lookup (const char *type)
490{
491 const char *home_dir;
492 char *dir, *user_dir;
493
494 dir = xdg_user_dir_lookup_with_fallback(type, NULL);
495 if (dir)
496 return dir;
497
498 home_dir = SDL_getenv("HOME");
499
500 if (!home_dir)
501 return NULL;
502
503 // Special case desktop for historical compatibility
504 if (SDL_strcmp(type, "DESKTOP") == 0) {
505 size_t length = SDL_strlen(home_dir) + SDL_strlen("/Desktop") + 1;
506 user_dir = (char*) SDL_malloc(length);
507 if (!user_dir)
508 return NULL;
509
510 SDL_strlcpy(user_dir, home_dir, length);
511 SDL_strlcat(user_dir, "/Desktop", length);
512 return user_dir;
513 }
514
515 return NULL;
516}
517
518char *SDL_SYS_GetUserFolder(SDL_Folder folder)
519{
520 const char *param = NULL;
521 char *result;
522 char *newresult;
523
524 /* According to `man xdg-user-dir`, the possible values are:
525 DESKTOP
526 DOWNLOAD
527 TEMPLATES
528 PUBLICSHARE
529 DOCUMENTS
530 MUSIC
531 PICTURES
532 VIDEOS
533 */
534 switch(folder) {
535 case SDL_FOLDER_HOME:
536 param = SDL_getenv("HOME");
537
538 if (!param) {
539 SDL_SetError("No $HOME environment variable available");
540 return NULL;
541 }
542
543 result = SDL_strdup(param);
544 goto append_slash;
545
546 case SDL_FOLDER_DESKTOP:
547 param = "DESKTOP";
548 break;
549
550 case SDL_FOLDER_DOCUMENTS:
551 param = "DOCUMENTS";
552 break;
553
554 case SDL_FOLDER_DOWNLOADS:
555 param = "DOWNLOAD";
556 break;
557
558 case SDL_FOLDER_MUSIC:
559 param = "MUSIC";
560 break;
561
562 case SDL_FOLDER_PICTURES:
563 param = "PICTURES";
564 break;
565
566 case SDL_FOLDER_PUBLICSHARE:
567 param = "PUBLICSHARE";
568 break;
569
570 case SDL_FOLDER_SAVEDGAMES:
571 SDL_SetError("Saved Games folder unavailable on XDG");
572 return NULL;
573
574 case SDL_FOLDER_SCREENSHOTS:
575 SDL_SetError("Screenshots folder unavailable on XDG");
576 return NULL;
577
578 case SDL_FOLDER_TEMPLATES:
579 param = "TEMPLATES";
580 break;
581
582 case SDL_FOLDER_VIDEOS:
583 param = "VIDEOS";
584 break;
585
586 default:
587 SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
588 return NULL;
589 }
590
591 /* param *should* to be set to something at this point, but just in case */
592 if (!param) {
593 SDL_SetError("No corresponding XDG user directory");
594 return NULL;
595 }
596
597 result = xdg_user_dir_lookup(param);
598
599 if (!result) {
600 SDL_SetError("XDG directory not available");
601 return NULL;
602 }
603
604append_slash:
605 newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2);
606
607 if (!newresult) {
608 SDL_free(result);
609 return NULL;
610 }
611
612 result = newresult;
613 SDL_strlcat(result, "/", SDL_strlen(result) + 2);
614
615 return result;
616}
617
618#endif // SDL_FILESYSTEM_UNIX
diff --git a/contrib/SDL-3.2.8/src/filesystem/vita/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/vita/SDL_sysfilesystem.c
new file mode 100644
index 0000000..8b65e8a
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/vita/SDL_sysfilesystem.c
@@ -0,0 +1,92 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_VITA
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include <errno.h>
31#include <stdio.h>
32#include <unistd.h>
33#include <stdlib.h>
34#include <psp2/io/stat.h>
35#include <sys/types.h>
36#include <limits.h>
37#include <fcntl.h>
38
39char *SDL_SYS_GetBasePath(void)
40{
41 return SDL_strdup("app0:/");
42}
43
44char *SDL_SYS_GetPrefPath(const char *org, const char *app)
45{
46 const char *envr = "ux0:/data/";
47 char *result = NULL;
48 char *ptr = NULL;
49 size_t len = 0;
50
51 if (!app) {
52 SDL_InvalidParamError("app");
53 return NULL;
54 }
55 if (!org) {
56 org = "";
57 }
58
59 len = SDL_strlen(envr);
60
61 len += SDL_strlen(org) + SDL_strlen(app) + 3;
62 result = (char *)SDL_malloc(len);
63 if (!result) {
64 return NULL;
65 }
66
67 if (*org) {
68 SDL_snprintf(result, len, "%s%s/%s/", envr, org, app);
69 } else {
70 SDL_snprintf(result, len, "%s%s/", envr, app);
71 }
72
73 for (ptr = result + 1; *ptr; ptr++) {
74 if (*ptr == '/') {
75 *ptr = '\0';
76 sceIoMkdir(result, 0777);
77 *ptr = '/';
78 }
79 }
80 sceIoMkdir(result, 0777);
81
82 return result;
83}
84
85// TODO
86char *SDL_SYS_GetUserFolder(SDL_Folder folder)
87{
88 SDL_Unsupported();
89 return NULL;
90}
91
92#endif // SDL_FILESYSTEM_VITA
diff --git a/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfilesystem.c b/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfilesystem.c
new file mode 100644
index 0000000..39ba414
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfilesystem.c
@@ -0,0 +1,382 @@
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#include "SDL_internal.h"
22
23#ifdef SDL_FILESYSTEM_WINDOWS
24
25/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
26// System dependent filesystem routines
27
28#include "../SDL_sysfilesystem.h"
29
30#include "../../core/windows/SDL_windows.h"
31#include <shlobj.h>
32#include <initguid.h>
33
34// These aren't all defined in older SDKs, so define them here
35DEFINE_GUID(SDL_FOLDERID_Profile, 0x5E6C858F, 0x0E22, 0x4760, 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73);
36DEFINE_GUID(SDL_FOLDERID_Desktop, 0xB4BFCC3A, 0xDB2C, 0x424C, 0xB0, 0x29, 0x7F, 0xE9, 0x9A, 0x87, 0xC6, 0x41);
37DEFINE_GUID(SDL_FOLDERID_Documents, 0xFDD39AD0, 0x238F, 0x46AF, 0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7);
38DEFINE_GUID(SDL_FOLDERID_Downloads, 0x374de290, 0x123f, 0x4565, 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b);
39DEFINE_GUID(SDL_FOLDERID_Music, 0x4BD8D571, 0x6D19, 0x48D3, 0xBE, 0x97, 0x42, 0x22, 0x20, 0x08, 0x0E, 0x43);
40DEFINE_GUID(SDL_FOLDERID_Pictures, 0x33E28130, 0x4E1E, 0x4676, 0x83, 0x5A, 0x98, 0x39, 0x5C, 0x3B, 0xC3, 0xBB);
41DEFINE_GUID(SDL_FOLDERID_SavedGames, 0x4c5c32ff, 0xbb9d, 0x43b0, 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4);
42DEFINE_GUID(SDL_FOLDERID_Screenshots, 0xb7bede81, 0xdf94, 0x4682, 0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f);
43DEFINE_GUID(SDL_FOLDERID_Templates, 0xA63293E8, 0x664E, 0x48DB, 0xA0, 0x79, 0xDF, 0x75, 0x9E, 0x05, 0x09, 0xF7);
44DEFINE_GUID(SDL_FOLDERID_Videos, 0x18989B1D, 0x99B5, 0x455B, 0x84, 0x1C, 0xAB, 0x7C, 0x74, 0xE4, 0xDD, 0xFC);
45
46char *SDL_SYS_GetBasePath(void)
47{
48 DWORD buflen = 128;
49 WCHAR *path = NULL;
50 char *result = NULL;
51 DWORD len = 0;
52 int i;
53
54 while (true) {
55 void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR));
56 if (!ptr) {
57 SDL_free(path);
58 return NULL;
59 }
60
61 path = (WCHAR *)ptr;
62
63 len = GetModuleFileNameW(NULL, path, buflen);
64 // if it truncated, then len >= buflen - 1
65 // if there was enough room (or failure), len < buflen - 1
66 if (len < buflen - 1) {
67 break;
68 }
69
70 // buffer too small? Try again.
71 buflen *= 2;
72 }
73
74 if (len == 0) {
75 SDL_free(path);
76 WIN_SetError("Couldn't locate our .exe");
77 return NULL;
78 }
79
80 for (i = len - 1; i > 0; i--) {
81 if (path[i] == '\\') {
82 break;
83 }
84 }
85
86 SDL_assert(i > 0); // Should have been an absolute path.
87 path[i + 1] = '\0'; // chop off filename.
88
89 result = WIN_StringToUTF8W(path);
90 SDL_free(path);
91
92 return result;
93}
94
95char *SDL_SYS_GetPrefPath(const char *org, const char *app)
96{
97 /*
98 * Vista and later has a new API for this, but SHGetFolderPath works there,
99 * and apparently just wraps the new API. This is the new way to do it:
100 *
101 * SHGetKnownFolderPath(SDL_FOLDERID_RoamingAppData, KF_FLAG_CREATE,
102 * NULL, &wszPath);
103 */
104
105 HRESULT hr = E_FAIL;
106 WCHAR path[MAX_PATH];
107 char *result = NULL;
108 WCHAR *worg = NULL;
109 WCHAR *wapp = NULL;
110 size_t new_wpath_len = 0;
111 BOOL api_result = FALSE;
112
113 if (!app) {
114 SDL_InvalidParamError("app");
115 return NULL;
116 }
117 if (!org) {
118 org = "";
119 }
120
121 hr = SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path);
122 if (!SUCCEEDED(hr)) {
123 WIN_SetErrorFromHRESULT("Couldn't locate our prefpath", hr);
124 return NULL;
125 }
126
127 worg = WIN_UTF8ToStringW(org);
128 if (!worg) {
129 return NULL;
130 }
131
132 wapp = WIN_UTF8ToStringW(app);
133 if (!wapp) {
134 SDL_free(worg);
135 return NULL;
136 }
137
138 new_wpath_len = SDL_wcslen(worg) + SDL_wcslen(wapp) + SDL_wcslen(path) + 3;
139
140 if ((new_wpath_len + 1) > MAX_PATH) {
141 SDL_free(worg);
142 SDL_free(wapp);
143 WIN_SetError("Path too long.");
144 return NULL;
145 }
146
147 if (*worg) {
148 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
149 SDL_wcslcat(path, worg, SDL_arraysize(path));
150 }
151 SDL_free(worg);
152
153 api_result = CreateDirectoryW(path, NULL);
154 if (api_result == FALSE) {
155 if (GetLastError() != ERROR_ALREADY_EXISTS) {
156 SDL_free(wapp);
157 WIN_SetError("Couldn't create a prefpath.");
158 return NULL;
159 }
160 }
161
162 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
163 SDL_wcslcat(path, wapp, SDL_arraysize(path));
164 SDL_free(wapp);
165
166 api_result = CreateDirectoryW(path, NULL);
167 if (api_result == FALSE) {
168 if (GetLastError() != ERROR_ALREADY_EXISTS) {
169 WIN_SetError("Couldn't create a prefpath.");
170 return NULL;
171 }
172 }
173
174 SDL_wcslcat(path, L"\\", SDL_arraysize(path));
175
176 result = WIN_StringToUTF8W(path);
177
178 return result;
179}
180
181char *SDL_SYS_GetUserFolder(SDL_Folder folder)
182{
183 typedef HRESULT (WINAPI *pfnSHGetKnownFolderPath)(REFGUID /* REFKNOWNFOLDERID */, DWORD, HANDLE, PWSTR*);
184 HMODULE lib = LoadLibrary(L"Shell32.dll");
185 pfnSHGetKnownFolderPath pSHGetKnownFolderPath = NULL;
186 char *result = NULL;
187
188 if (lib) {
189 pSHGetKnownFolderPath = (pfnSHGetKnownFolderPath)GetProcAddress(lib, "SHGetKnownFolderPath");
190 }
191
192 if (pSHGetKnownFolderPath) {
193 GUID type; // KNOWNFOLDERID
194 HRESULT hr;
195 wchar_t *path;
196
197 switch (folder) {
198 case SDL_FOLDER_HOME:
199 type = SDL_FOLDERID_Profile;
200 break;
201
202 case SDL_FOLDER_DESKTOP:
203 type = SDL_FOLDERID_Desktop;
204 break;
205
206 case SDL_FOLDER_DOCUMENTS:
207 type = SDL_FOLDERID_Documents;
208 break;
209
210 case SDL_FOLDER_DOWNLOADS:
211 type = SDL_FOLDERID_Downloads;
212 break;
213
214 case SDL_FOLDER_MUSIC:
215 type = SDL_FOLDERID_Music;
216 break;
217
218 case SDL_FOLDER_PICTURES:
219 type = SDL_FOLDERID_Pictures;
220 break;
221
222 case SDL_FOLDER_PUBLICSHARE:
223 SDL_SetError("Public share unavailable on Windows");
224 goto done;
225
226 case SDL_FOLDER_SAVEDGAMES:
227 type = SDL_FOLDERID_SavedGames;
228 break;
229
230 case SDL_FOLDER_SCREENSHOTS:
231 type = SDL_FOLDERID_Screenshots;
232 break;
233
234 case SDL_FOLDER_TEMPLATES:
235 type = SDL_FOLDERID_Templates;
236 break;
237
238 case SDL_FOLDER_VIDEOS:
239 type = SDL_FOLDERID_Videos;
240 break;
241
242 default:
243 SDL_SetError("Invalid SDL_Folder: %d", (int)folder);
244 goto done;
245 };
246
247 hr = pSHGetKnownFolderPath(&type, 0x00008000 /* KF_FLAG_CREATE */, NULL, &path);
248 if (SUCCEEDED(hr)) {
249 result = WIN_StringToUTF8W(path);
250 } else {
251 WIN_SetErrorFromHRESULT("Couldn't get folder", hr);
252 }
253
254 } else {
255 int type;
256 HRESULT hr;
257 wchar_t path[MAX_PATH];
258
259 switch (folder) {
260 case SDL_FOLDER_HOME:
261 type = CSIDL_PROFILE;
262 break;
263
264 case SDL_FOLDER_DESKTOP:
265 type = CSIDL_DESKTOP;
266 break;
267
268 case SDL_FOLDER_DOCUMENTS:
269 type = CSIDL_MYDOCUMENTS;
270 break;
271
272 case SDL_FOLDER_DOWNLOADS:
273 SDL_SetError("Downloads folder unavailable before Vista");
274 goto done;
275
276 case SDL_FOLDER_MUSIC:
277 type = CSIDL_MYMUSIC;
278 break;
279
280 case SDL_FOLDER_PICTURES:
281 type = CSIDL_MYPICTURES;
282 break;
283
284 case SDL_FOLDER_PUBLICSHARE:
285 SDL_SetError("Public share unavailable on Windows");
286 goto done;
287
288 case SDL_FOLDER_SAVEDGAMES:
289 SDL_SetError("Saved games unavailable before Vista");
290 goto done;
291
292 case SDL_FOLDER_SCREENSHOTS:
293 SDL_SetError("Screenshots folder unavailable before Vista");
294 goto done;
295
296 case SDL_FOLDER_TEMPLATES:
297 type = CSIDL_TEMPLATES;
298 break;
299
300 case SDL_FOLDER_VIDEOS:
301 type = CSIDL_MYVIDEO;
302 break;
303
304 default:
305 SDL_SetError("Unsupported SDL_Folder on Windows before Vista: %d", (int)folder);
306 goto done;
307 };
308
309 // Create the OS-specific folder if it doesn't already exist
310 type |= CSIDL_FLAG_CREATE;
311
312#if 0
313 // Apparently the oldest, but not supported in modern Windows
314 HRESULT hr = SHGetSpecialFolderPath(NULL, path, type, TRUE);
315#endif
316
317 /* Windows 2000/XP and later, deprecated as of Windows 10 (still
318 available), available in Wine (tested 6.0.3) */
319 hr = SHGetFolderPathW(NULL, type, NULL, SHGFP_TYPE_CURRENT, path);
320
321 // use `== TRUE` for SHGetSpecialFolderPath
322 if (SUCCEEDED(hr)) {
323 result = WIN_StringToUTF8W(path);
324 } else {
325 WIN_SetErrorFromHRESULT("Couldn't get folder", hr);
326 }
327 }
328
329 if (result) {
330 char *newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2);
331
332 if (!newresult) {
333 SDL_free(result);
334 result = NULL; // will be returned
335 goto done;
336 }
337
338 result = newresult;
339 SDL_strlcat(result, "\\", SDL_strlen(result) + 2);
340 }
341
342done:
343 if (lib) {
344 FreeLibrary(lib);
345 }
346 return result;
347}
348
349char *SDL_SYS_GetCurrentDirectory(void)
350{
351 WCHAR *wstr = NULL;
352 DWORD buflen = 0;
353 while (true) {
354 const DWORD bw = GetCurrentDirectoryW(buflen, wstr);
355 if (bw == 0) {
356 WIN_SetError("GetCurrentDirectoryW failed");
357 return NULL;
358 } else if (bw < buflen) { // we got it!
359 // make sure there's a path separator at the end.
360 SDL_assert(bw < (buflen + 2));
361 if ((bw == 0) || (wstr[bw-1] != '\\')) {
362 wstr[bw] = '\\';
363 wstr[bw + 1] = '\0';
364 }
365 break;
366 }
367
368 void *ptr = SDL_realloc(wstr, (bw + 1) * sizeof (WCHAR));
369 if (!ptr) {
370 SDL_free(wstr);
371 return NULL;
372 }
373 wstr = (WCHAR *) ptr;
374 buflen = bw;
375 }
376
377 char *retval = WIN_StringToUTF8W(wstr);
378 SDL_free(wstr);
379 return retval;
380}
381
382#endif // SDL_FILESYSTEM_WINDOWS
diff --git a/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfsops.c b/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfsops.c
new file mode 100644
index 0000000..9c48ba9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfsops.c
@@ -0,0 +1,231 @@
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#if defined(SDL_FSOPS_WINDOWS)
25
26/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27// System dependent filesystem routines
28
29#include "../../core/windows/SDL_windows.h"
30#include "../SDL_sysfilesystem.h"
31
32bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
33{
34 SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
35 if (*path == '\0') { // if empty (completely at the root), we need to enumerate drive letters.
36 const DWORD drives = GetLogicalDrives();
37 char name[] = { 0, ':', '\\', '\0' };
38 for (int i = 'A'; (result == SDL_ENUM_CONTINUE) && (i <= 'Z'); i++) {
39 if (drives & (1 << (i - 'A'))) {
40 name[0] = (char) i;
41 result = cb(userdata, "", name);
42 }
43 }
44 } else {
45 // you need a wildcard to enumerate through FindFirstFileEx(), but the wildcard is only checked in the
46 // filename element at the end of the path string, so always tack on a "\\*" to get everything, and
47 // also prevent any wildcards inserted by the app from being respected.
48 char *pattern = NULL;
49 int patternlen = SDL_asprintf(&pattern, "%s\\\\", path); // we'll replace that second '\\' in the trimdown.
50 if ((patternlen == -1) || (!pattern)) {
51 return false;
52 }
53
54 // trim down to a single path separator at the end, in case the caller added one or more.
55 patternlen--;
56 while ((patternlen >= 0) && ((pattern[patternlen] == '\\') || (pattern[patternlen] == '/'))) {
57 pattern[patternlen--] ='\0';
58 }
59 pattern[++patternlen] = '\\';
60 pattern[++patternlen] = '*';
61 pattern[++patternlen] = '\0';
62
63 WCHAR *wpattern = WIN_UTF8ToStringW(pattern);
64 if (!wpattern) {
65 SDL_free(pattern);
66 return false;
67 }
68
69 pattern[--patternlen] = '\0'; // chop off the '*' so we just have the dirname with a path separator.
70
71 WIN32_FIND_DATAW entw;
72 HANDLE dir = FindFirstFileExW(wpattern, FindExInfoStandard, &entw, FindExSearchNameMatch, NULL, 0);
73 SDL_free(wpattern);
74 if (dir == INVALID_HANDLE_VALUE) {
75 SDL_free(pattern);
76 return WIN_SetError("Failed to enumerate directory");
77 }
78
79 do {
80 const WCHAR *fn = entw.cFileName;
81
82 if (fn[0] == '.') { // ignore "." and ".."
83 if ((fn[1] == '\0') || ((fn[1] == '.') && (fn[2] == '\0'))) {
84 continue;
85 }
86 }
87
88 char *utf8fn = WIN_StringToUTF8W(fn);
89 if (!utf8fn) {
90 result = SDL_ENUM_FAILURE;
91 } else {
92 result = cb(userdata, pattern, utf8fn);
93 SDL_free(utf8fn);
94 }
95 } while ((result == SDL_ENUM_CONTINUE) && (FindNextFileW(dir, &entw) != 0));
96
97 FindClose(dir);
98 SDL_free(pattern);
99 }
100
101 return (result != SDL_ENUM_FAILURE);
102}
103
104bool SDL_SYS_RemovePath(const char *path)
105{
106 WCHAR *wpath = WIN_UTF8ToStringW(path);
107 if (!wpath) {
108 return false;
109 }
110
111 WIN32_FILE_ATTRIBUTE_DATA info;
112 if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &info)) {
113 SDL_free(wpath);
114 if (GetLastError() == ERROR_FILE_NOT_FOUND) {
115 // Note that ERROR_PATH_NOT_FOUND means a parent dir is missing, and we consider that an error.
116 return true; // thing is already gone, call it a success.
117 }
118 return WIN_SetError("Couldn't get path's attributes");
119 }
120
121 const int isdir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
122 const BOOL rc = isdir ? RemoveDirectoryW(wpath) : DeleteFileW(wpath);
123 SDL_free(wpath);
124 if (!rc) {
125 return WIN_SetError("Couldn't remove path");
126 }
127 return true;
128}
129
130bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath)
131{
132 WCHAR *woldpath = WIN_UTF8ToStringW(oldpath);
133 if (!woldpath) {
134 return false;
135 }
136
137 WCHAR *wnewpath = WIN_UTF8ToStringW(newpath);
138 if (!wnewpath) {
139 SDL_free(woldpath);
140 return false;
141 }
142
143 const BOOL rc = MoveFileExW(woldpath, wnewpath, MOVEFILE_REPLACE_EXISTING);
144 SDL_free(wnewpath);
145 SDL_free(woldpath);
146 if (!rc) {
147 return WIN_SetError("Couldn't rename path");
148 }
149 return true;
150}
151
152bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath)
153{
154 WCHAR *woldpath = WIN_UTF8ToStringW(oldpath);
155 if (!woldpath) {
156 return false;
157 }
158
159 WCHAR *wnewpath = WIN_UTF8ToStringW(newpath);
160 if (!wnewpath) {
161 SDL_free(woldpath);
162 return false;
163 }
164
165 const BOOL rc = CopyFileExW(woldpath, wnewpath, NULL, NULL, NULL, COPY_FILE_ALLOW_DECRYPTED_DESTINATION|COPY_FILE_NO_BUFFERING);
166 SDL_free(wnewpath);
167 SDL_free(woldpath);
168 if (!rc) {
169 return WIN_SetError("Couldn't copy path");
170 }
171 return true;
172}
173
174bool SDL_SYS_CreateDirectory(const char *path)
175{
176 WCHAR *wpath = WIN_UTF8ToStringW(path);
177 if (!wpath) {
178 return false;
179 }
180
181 DWORD rc = CreateDirectoryW(wpath, NULL);
182 if (!rc && (GetLastError() == ERROR_ALREADY_EXISTS)) {
183 WIN32_FILE_ATTRIBUTE_DATA winstat;
184 if (GetFileAttributesExW(wpath, GetFileExInfoStandard, &winstat)) {
185 if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
186 rc = 1; // exists and is already a directory: cool.
187 }
188 }
189 }
190
191 SDL_free(wpath);
192 if (!rc) {
193 return WIN_SetError("Couldn't create directory");
194 }
195 return true;
196}
197
198bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
199{
200 WCHAR *wpath = WIN_UTF8ToStringW(path);
201 if (!wpath) {
202 return false;
203 }
204
205 WIN32_FILE_ATTRIBUTE_DATA winstat;
206 const BOOL rc = GetFileAttributesExW(wpath, GetFileExInfoStandard, &winstat);
207 SDL_free(wpath);
208 if (!rc) {
209 return WIN_SetError("Can't stat");
210 }
211
212 if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
213 info->type = SDL_PATHTYPE_DIRECTORY;
214 info->size = 0;
215 } else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) {
216 info->type = SDL_PATHTYPE_OTHER;
217 info->size = ((((Uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow);
218 } else {
219 info->type = SDL_PATHTYPE_FILE;
220 info->size = ((((Uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow);
221 }
222
223 info->create_time = SDL_TimeFromWindows(winstat.ftCreationTime.dwLowDateTime, winstat.ftCreationTime.dwHighDateTime);
224 info->modify_time = SDL_TimeFromWindows(winstat.ftLastWriteTime.dwLowDateTime, winstat.ftLastWriteTime.dwHighDateTime);
225 info->access_time = SDL_TimeFromWindows(winstat.ftLastAccessTime.dwLowDateTime, winstat.ftLastAccessTime.dwHighDateTime);
226
227 return true;
228}
229
230#endif // SDL_FSOPS_WINDOWS
231