From 5a079a2d114f96d4847d1ee305d5b7c16eeec50e Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 27 Dec 2025 12:03:39 -0800 Subject: Initial commit --- contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c | 530 ++++++++++++++++++ .../SDL-3.2.8/src/filesystem/SDL_filesystem_c.h | 29 + .../SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h | 43 ++ .../src/filesystem/android/SDL_sysfilesystem.c | 62 +++ .../src/filesystem/cocoa/SDL_sysfilesystem.m | 240 ++++++++ .../src/filesystem/dummy/SDL_sysfilesystem.c | 54 ++ .../SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c | 62 +++ .../src/filesystem/emscripten/SDL_sysfilesystem.c | 116 ++++ .../src/filesystem/gdk/SDL_sysfilesystem.cpp | 150 +++++ .../src/filesystem/haiku/SDL_sysfilesystem.cc | 156 ++++++ .../src/filesystem/n3ds/SDL_sysfilesystem.c | 90 +++ .../SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c | 246 ++++++++ .../src/filesystem/ps2/SDL_sysfilesystem.c | 119 ++++ .../src/filesystem/psp/SDL_sysfilesystem.c | 89 +++ .../src/filesystem/riscos/SDL_sysfilesystem.c | 208 +++++++ .../src/filesystem/unix/SDL_sysfilesystem.c | 618 +++++++++++++++++++++ .../src/filesystem/vita/SDL_sysfilesystem.c | 92 +++ .../src/filesystem/windows/SDL_sysfilesystem.c | 382 +++++++++++++ .../src/filesystem/windows/SDL_sysfsops.c | 231 ++++++++ 19 files changed, 3517 insertions(+) create mode 100644 contrib/SDL-3.2.8/src/filesystem/SDL_filesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/SDL_filesystem_c.h create mode 100644 contrib/SDL-3.2.8/src/filesystem/SDL_sysfilesystem.h create mode 100644 contrib/SDL-3.2.8/src/filesystem/android/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/cocoa/SDL_sysfilesystem.m create mode 100644 contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/dummy/SDL_sysfsops.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/emscripten/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/gdk/SDL_sysfilesystem.cpp create mode 100644 contrib/SDL-3.2.8/src/filesystem/haiku/SDL_sysfilesystem.cc create mode 100644 contrib/SDL-3.2.8/src/filesystem/n3ds/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/posix/SDL_sysfsops.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/ps2/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/psp/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/riscos/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/unix/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/vita/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfilesystem.c create mode 100644 contrib/SDL-3.2.8/src/filesystem/windows/SDL_sysfsops.c (limited to 'contrib/SDL-3.2.8/src/filesystem') 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#include "SDL_filesystem_c.h" +#include "SDL_sysfilesystem.h" +#include "../stdlib/SDL_sysstdlib.h" + +bool SDL_RemovePath(const char *path) +{ + if (!path) { + return SDL_InvalidParamError("path"); + } + return SDL_SYS_RemovePath(path); +} + +bool SDL_RenamePath(const char *oldpath, const char *newpath) +{ + if (!oldpath) { + return SDL_InvalidParamError("oldpath"); + } else if (!newpath) { + return SDL_InvalidParamError("newpath"); + } + return SDL_SYS_RenamePath(oldpath, newpath); +} + +bool SDL_CopyFile(const char *oldpath, const char *newpath) +{ + if (!oldpath) { + return SDL_InvalidParamError("oldpath"); + } else if (!newpath) { + return SDL_InvalidParamError("newpath"); + } + return SDL_SYS_CopyFile(oldpath, newpath); +} + +bool SDL_CreateDirectory(const char *path) +{ + if (!path) { + return SDL_InvalidParamError("path"); + } + + bool retval = SDL_SYS_CreateDirectory(path); + if (!retval && *path) { // maybe we're missing parent directories? + char *parents = SDL_strdup(path); + if (!parents) { + return false; // oh well. + } + + // in case there was a separator at the end of the path and it was + // upsetting something, chop it off. + const size_t slen = SDL_strlen(parents); + #ifdef SDL_PLATFORM_WINDOWS + if ((parents[slen - 1] == '/') || (parents[slen - 1] == '\\')) + #else + if (parents[slen - 1] == '/') + #endif + { + parents[slen - 1] = '\0'; + retval = SDL_SYS_CreateDirectory(parents); + } + + if (!retval) { + for (char *ptr = parents; *ptr; ptr++) { + const char ch = *ptr; + #ifdef SDL_PLATFORM_WINDOWS + const bool issep = (ch == '/') || (ch == '\\'); + if (issep && ((ptr - parents) == 2) && (parents[1] == ':')) { + continue; // it's just the drive letter, skip it. + } + #else + const bool issep = (ch == '/'); + if (issep && ((ptr - parents) == 0)) { + continue; // it's just the root directory, skip it. + } + #endif + + if (issep) { + *ptr = '\0'; + // (this does not fail if the path already exists as a directory.) + retval = SDL_SYS_CreateDirectory(parents); + if (!retval) { // still failing when making parents? Give up. + break; + } + *ptr = ch; + } + } + + // last chance: did it work this time? + retval = SDL_SYS_CreateDirectory(parents); + } + + SDL_free(parents); + } + return retval; +} + +bool SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata) +{ + if (!path) { + return SDL_InvalidParamError("path"); + } else if (!callback) { + return SDL_InvalidParamError("callback"); + } + return SDL_SYS_EnumerateDirectory(path, callback, userdata); +} + +bool SDL_GetPathInfo(const char *path, SDL_PathInfo *info) +{ + SDL_PathInfo dummy; + + if (!info) { + info = &dummy; + } + SDL_zerop(info); + + if (!path) { + return SDL_InvalidParamError("path"); + } + + return SDL_SYS_GetPathInfo(path, info); +} + +static bool EverythingMatch(const char *pattern, const char *str, bool *matched_to_dir) +{ + SDL_assert(pattern == NULL); + SDL_assert(str != NULL); + SDL_assert(matched_to_dir != NULL); + + *matched_to_dir = true; + return true; // everything matches! +} + +// this is just '*' and '?', with '/' matching nothing. +static bool WildcardMatch(const char *pattern, const char *str, bool *matched_to_dir) +{ + SDL_assert(pattern != NULL); + SDL_assert(str != NULL); + SDL_assert(matched_to_dir != NULL); + + const char *str_backtrack = NULL; + const char *pattern_backtrack = NULL; + char sch_backtrack = 0; + char sch = *str; + char pch = *pattern; + + while (sch) { + if (pch == '*') { + str_backtrack = str; + pattern_backtrack = ++pattern; + sch_backtrack = sch; + pch = *pattern; + } else if (pch == sch) { + if (pch == '/') { + str_backtrack = pattern_backtrack = NULL; + } + sch = *(++str); + pch = *(++pattern); + } else if ((pch == '?') && (sch != '/')) { // end of string (checked at `while`) or path separator do not match '?'. + sch = *(++str); + pch = *(++pattern); + } 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. + *matched_to_dir = false; + return false; + } else { // still here? Wasn't a match, but we're definitely in a '*' pattern. + str = ++str_backtrack; + pattern = pattern_backtrack; + sch_backtrack = sch; + sch = *str; + pch = *pattern; + } + + #ifdef SDL_PLATFORM_WINDOWS + if (sch == '\\') { + sch = '/'; + } + #endif + } + + // '*' at the end can be ignored, they are allowed to match nothing. + while (pch == '*') { + pch = *(++pattern); + } + + *matched_to_dir = ((pch == '/') || (pch == '\0')); // end of string and the pattern is complete or failed at a '/'? We should descend into this directory. + + return (pch == '\0'); // survived the whole pattern? That's a match! +} + + +// Note that this will currently encode illegal codepoints: UTF-16 surrogates, 0xFFFE, and 0xFFFF. +// and a codepoint > 0x10FFFF will fail the same as if there wasn't enough memory. +// clean this up if you want to move this to SDL_string.c. +static size_t EncodeCodepointToUtf8(char *ptr, Uint32 cp, size_t remaining) +{ + if (cp < 0x80) { // fits in a single UTF-8 byte. + if (remaining) { + *ptr = (char) cp; + return 1; + } + } else if (cp < 0x800) { // fits in 2 bytes. + if (remaining >= 2) { + ptr[0] = (char) ((cp >> 6) | 128 | 64); + ptr[1] = (char) (cp & 0x3F) | 128; + return 2; + } + } else if (cp < 0x10000) { // fits in 3 bytes. + if (remaining >= 3) { + ptr[0] = (char) ((cp >> 12) | 128 | 64 | 32); + ptr[1] = (char) ((cp >> 6) & 0x3F) | 128; + ptr[2] = (char) (cp & 0x3F) | 128; + return 3; + } + } else if (cp <= 0x10FFFF) { // fits in 4 bytes. + if (remaining >= 4) { + ptr[0] = (char) ((cp >> 18) | 128 | 64 | 32 | 16); + ptr[1] = (char) ((cp >> 12) & 0x3F) | 128; + ptr[2] = (char) ((cp >> 6) & 0x3F) | 128; + ptr[3] = (char) (cp & 0x3F) | 128; + return 4; + } + } + + return 0; +} + +static char *CaseFoldUtf8String(const char *fname) +{ + SDL_assert(fname != NULL); + const size_t allocation = (SDL_strlen(fname) + 1) * 3 * 4; + char *result = (char *) SDL_malloc(allocation); // lazy: just allocating the max needed. + if (!result) { + return NULL; + } + + Uint32 codepoint; + char *ptr = result; + size_t remaining = allocation; + while ((codepoint = SDL_StepUTF8(&fname, NULL)) != 0) { + Uint32 folded[3]; + const int num_folded = SDL_CaseFoldUnicode(codepoint, folded); + SDL_assert(num_folded > 0); + SDL_assert(num_folded <= SDL_arraysize(folded)); + for (int i = 0; i < num_folded; i++) { + SDL_assert(remaining > 0); + const size_t rc = EncodeCodepointToUtf8(ptr, folded[i], remaining); + SDL_assert(rc > 0); + SDL_assert(rc < remaining); + remaining -= rc; + ptr += rc; + } + } + + SDL_assert(remaining > 0); + remaining--; + *ptr = '\0'; + + if (remaining > 0) { + SDL_assert(allocation > remaining); + ptr = (char *)SDL_realloc(result, allocation - remaining); // shrink it down. + if (ptr) { // shouldn't fail, but if it does, `result` is still valid. + result = ptr; + } + } + + return result; +} + + +typedef struct GlobDirCallbackData +{ + bool (*matcher)(const char *pattern, const char *str, bool *matched_to_dir); + const char *pattern; + int num_entries; + SDL_GlobFlags flags; + SDL_GlobEnumeratorFunc enumerator; + SDL_GlobGetPathInfoFunc getpathinfo; + void *fsuserdata; + size_t basedirlen; + SDL_IOStream *string_stream; +} GlobDirCallbackData; + +static SDL_EnumerationResult SDLCALL GlobDirectoryCallback(void *userdata, const char *dirname, const char *fname) +{ + SDL_assert(userdata != NULL); + SDL_assert(dirname != NULL); + SDL_assert(fname != NULL); + + //SDL_Log("GlobDirectoryCallback('%s', '%s')", dirname, fname); + + GlobDirCallbackData *data = (GlobDirCallbackData *) userdata; + + // !!! 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, + // !!! FIXME: and only casefold the new pieces instead of allocating and folding full paths for all of this. + + char *fullpath = NULL; + if (SDL_asprintf(&fullpath, "%s%s", dirname, fname) < 0) { + return SDL_ENUM_FAILURE; + } + + char *folded = NULL; + if (data->flags & SDL_GLOB_CASEINSENSITIVE) { + folded = CaseFoldUtf8String(fullpath); + if (!folded) { + return SDL_ENUM_FAILURE; + } + } + + bool matched_to_dir = false; + const bool matched = data->matcher(data->pattern, (folded ? folded : fullpath) + data->basedirlen, &matched_to_dir); + //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"); + SDL_free(folded); + + if (matched) { + const char *subpath = fullpath + data->basedirlen; + const size_t slen = SDL_strlen(subpath) + 1; + if (SDL_WriteIO(data->string_stream, subpath, slen) != slen) { + SDL_free(fullpath); + return SDL_ENUM_FAILURE; // stop enumerating, return failure to the app. + } + data->num_entries++; + } + + SDL_EnumerationResult result = SDL_ENUM_CONTINUE; // keep enumerating by default. + if (matched_to_dir) { + SDL_PathInfo info; + if (data->getpathinfo(fullpath, &info, data->fsuserdata) && (info.type == SDL_PATHTYPE_DIRECTORY)) { + //SDL_Log("GlobDirectoryCallback: Descending into subdir '%s'", fname); + if (!data->enumerator(fullpath, GlobDirectoryCallback, data, data->fsuserdata)) { + result = SDL_ENUM_FAILURE; + } + } + } + + SDL_free(fullpath); + + return result; +} + +char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata) +{ + int dummycount; + if (!count) { + count = &dummycount; + } + *count = 0; + + if (!path) { + SDL_InvalidParamError("path"); + return NULL; + } + + // if path ends with any slash, chop them off, so we don't confuse the pattern matcher later. + char *pathcpy = NULL; + size_t pathlen = SDL_strlen(path); + if ((pathlen > 1) && ((path[pathlen-1] == '/') || (path[pathlen-1] == '\\'))) { + pathcpy = SDL_strdup(path); + if (!pathcpy) { + return NULL; + } + char *ptr = &pathcpy[pathlen-1]; + while ((ptr >= pathcpy) && ((*ptr == '/') || (*ptr == '\\'))) { + *(ptr--) = '\0'; + } + path = pathcpy; + } + + if (!pattern) { + flags &= ~SDL_GLOB_CASEINSENSITIVE; // avoid some unnecessary allocations and work later. + } + + char *folded = NULL; + if (flags & SDL_GLOB_CASEINSENSITIVE) { + SDL_assert(pattern != NULL); + folded = CaseFoldUtf8String(pattern); + if (!folded) { + SDL_free(pathcpy); + return NULL; + } + } + + GlobDirCallbackData data; + SDL_zero(data); + data.string_stream = SDL_IOFromDynamicMem(); + if (!data.string_stream) { + SDL_free(folded); + SDL_free(pathcpy); + return NULL; + } + + if (!pattern) { + data.matcher = EverythingMatch; // no pattern? Everything matches. + + // !!! FIXME + //} else if (flags & SDL_GLOB_GITIGNORE) { + // data.matcher = GitIgnoreMatch; + + } else { + data.matcher = WildcardMatch; + } + + data.pattern = folded ? folded : pattern; + data.flags = flags; + data.enumerator = enumerator; + data.getpathinfo = getpathinfo; + data.fsuserdata = userdata; + data.basedirlen = *path ? (SDL_strlen(path) + 1) : 0; // +1 for the '/' we'll be adding. + + + char **result = NULL; + if (data.enumerator(path, GlobDirectoryCallback, &data, data.fsuserdata)) { + const size_t streamlen = (size_t) SDL_GetIOSize(data.string_stream); + const size_t buflen = streamlen + ((data.num_entries + 1) * sizeof (char *)); // +1 for NULL terminator at end of array. + result = (char **) SDL_malloc(buflen); + if (result) { + if (data.num_entries > 0) { + Sint64 iorc = SDL_SeekIO(data.string_stream, 0, SDL_IO_SEEK_SET); + SDL_assert(iorc == 0); // this should never fail for a memory stream! + char *ptr = (char *) (result + (data.num_entries + 1)); + iorc = SDL_ReadIO(data.string_stream, ptr, streamlen); + SDL_assert(iorc == (Sint64) streamlen); // this should never fail for a memory stream! + for (int i = 0; i < data.num_entries; i++) { + result[i] = ptr; + ptr += SDL_strlen(ptr) + 1; + } + } + result[data.num_entries] = NULL; // NULL terminate the list. + *count = data.num_entries; + } + } + + SDL_CloseIO(data.string_stream); + SDL_free(folded); + SDL_free(pathcpy); + + return result; +} + +static bool GlobDirectoryGetPathInfo(const char *path, SDL_PathInfo *info, void *userdata) +{ + return SDL_GetPathInfo(path, info); +} + +static bool GlobDirectoryEnumerator(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata) +{ + return SDL_EnumerateDirectory(path, cb, cbuserdata); +} + +char **SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count) +{ + //SDL_Log("SDL_GlobDirectory('%s', '%s') ...", path, pattern); + return SDL_InternalGlobDirectory(path, pattern, flags, count, GlobDirectoryEnumerator, GlobDirectoryGetPathInfo, NULL); +} + + +static char *CachedBasePath = NULL; + +const char *SDL_GetBasePath(void) +{ + if (!CachedBasePath) { + CachedBasePath = SDL_SYS_GetBasePath(); + } + return CachedBasePath; +} + + +static char *CachedUserFolders[SDL_FOLDER_COUNT]; + +const char *SDL_GetUserFolder(SDL_Folder folder) +{ + const int idx = (int) folder; + if ((idx < 0) || (idx >= SDL_arraysize(CachedUserFolders))) { + SDL_InvalidParamError("folder"); + return NULL; + } + + if (!CachedUserFolders[idx]) { + CachedUserFolders[idx] = SDL_SYS_GetUserFolder(folder); + } + return CachedUserFolders[idx]; +} + + +char *SDL_GetPrefPath(const char *org, const char *app) +{ + return SDL_SYS_GetPrefPath(org, app); +} + +char *SDL_GetCurrentDirectory(void) +{ + return SDL_SYS_GetCurrentDirectory(); +} + +void SDL_InitFilesystem(void) +{ +} + +void SDL_QuitFilesystem(void) +{ + if (CachedBasePath) { + SDL_free(CachedBasePath); + CachedBasePath = NULL; + } + for (int i = 0; i < SDL_arraysize(CachedUserFolders); i++) { + if (CachedUserFolders[i]) { + SDL_free(CachedUserFolders[i]); + CachedUserFolders[i] = NULL; + } + } +} + 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_filesystem_c_h_ +#define SDL_filesystem_c_h_ + +extern void SDL_InitFilesystem(void); +extern void SDL_QuitFilesystem(void); + +#endif + 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_sysfilesystem_h_ +#define SDL_sysfilesystem_h_ + +// return a string that we can SDL_free(). It will be cached at the higher level. +extern char *SDL_SYS_GetBasePath(void); +extern char *SDL_SYS_GetPrefPath(const char *org, const char *app); +extern char *SDL_SYS_GetUserFolder(SDL_Folder folder); +extern char *SDL_SYS_GetCurrentDirectory(void); + +extern bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata); +extern bool SDL_SYS_RemovePath(const char *path); +extern bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath); +extern bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath); +extern bool SDL_SYS_CreateDirectory(const char *path); +extern bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info); + +typedef bool (*SDL_GlobEnumeratorFunc)(const char *path, SDL_EnumerateDirectoryCallback cb, void *cbuserdata, void *userdata); +typedef bool (*SDL_GlobGetPathInfoFunc)(const char *path, SDL_PathInfo *info, void *userdata); +extern char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count, SDL_GlobEnumeratorFunc enumerator, SDL_GlobGetPathInfoFunc getpathinfo, void *userdata); + +#endif + 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_ANDROID + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include + +char *SDL_SYS_GetBasePath(void) +{ + // The current working directory is / on Android + SDL_Unsupported(); + return NULL; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + const char *path = SDL_GetAndroidInternalStoragePath(); + if (path) { + size_t pathlen = SDL_strlen(path) + 2; + char *fullpath = (char *)SDL_malloc(pathlen); + if (!fullpath) { + return NULL; + } + SDL_snprintf(fullpath, pathlen, "%s/", path); + return fullpath; + } + return NULL; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + /* TODO: see https://developer.android.com/reference/android/os/Environment#lfields + and https://stackoverflow.com/questions/39332085/get-path-to-pictures-directory */ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_COCOA + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include +#include + +char *SDL_SYS_GetBasePath(void) +{ + @autoreleasepool { + NSBundle *bundle = [NSBundle mainBundle]; + const char *baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String]; + const char *base = NULL; + char *result = NULL; + + if (baseType == NULL) { + baseType = "resource"; + } + if (SDL_strcasecmp(baseType, "bundle") == 0) { + base = [[bundle bundlePath] fileSystemRepresentation]; + } else if (SDL_strcasecmp(baseType, "parent") == 0) { + base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation]; + } else { + // this returns the exedir for non-bundled and the resourceDir for bundled apps + base = [[bundle resourcePath] fileSystemRepresentation]; + } + + if (base) { + const size_t len = SDL_strlen(base) + 2; + result = (char *)SDL_malloc(len); + if (result != NULL) { + SDL_snprintf(result, len, "%s/", base); + } + } + + return result; + } +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + @autoreleasepool { + char *result = NULL; + NSArray *array; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + +#ifndef SDL_PLATFORM_TVOS + array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); +#else + /* tvOS does not have persistent local storage! + * The only place on-device where we can store data is + * a cache directory that the OS can empty at any time. + * + * It's therefore very likely that save data will be erased + * between sessions. If you want your app's save data to + * actually stick around, you'll need to use iCloud storage. + */ + { + static bool shown = false; + if (!shown) { + shown = true; + 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."); + } + } + + array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); +#endif // !SDL_PLATFORM_TVOS + + if ([array count] > 0) { // we only want the first item in the list. + NSString *str = [array objectAtIndex:0]; + const char *base = [str fileSystemRepresentation]; + if (base) { + const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; + result = (char *)SDL_malloc(len); + if (result != NULL) { + char *ptr; + if (*org) { + SDL_snprintf(result, len, "%s/%s/%s/", base, org, app); + } else { + SDL_snprintf(result, len, "%s/%s/", base, app); + } + for (ptr = result + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + mkdir(result, 0700); + *ptr = '/'; + } + } + mkdir(result, 0700); + } + } + } + + return result; + } +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + @autoreleasepool { +#ifdef SDL_PLATFORM_TVOS + SDL_SetError("tvOS does not have persistent storage"); + return NULL; +#else + char *result = NULL; + const char* base; + NSArray *array; + NSSearchPathDirectory dir; + NSString *str; + char *ptr; + + switch (folder) { + case SDL_FOLDER_HOME: + base = SDL_getenv("HOME"); + + if (!base) { + SDL_SetError("No $HOME environment variable available"); + return NULL; + } + + goto append_slash; + + case SDL_FOLDER_DESKTOP: + dir = NSDesktopDirectory; + break; + + case SDL_FOLDER_DOCUMENTS: + dir = NSDocumentDirectory; + break; + + case SDL_FOLDER_DOWNLOADS: + dir = NSDownloadsDirectory; + break; + + case SDL_FOLDER_MUSIC: + dir = NSMusicDirectory; + break; + + case SDL_FOLDER_PICTURES: + dir = NSPicturesDirectory; + break; + + case SDL_FOLDER_PUBLICSHARE: + dir = NSSharedPublicDirectory; + break; + + case SDL_FOLDER_SAVEDGAMES: + SDL_SetError("Saved games folder not supported on Cocoa"); + return NULL; + + case SDL_FOLDER_SCREENSHOTS: + SDL_SetError("Screenshots folder not supported on Cocoa"); + return NULL; + + case SDL_FOLDER_TEMPLATES: + SDL_SetError("Templates folder not supported on Cocoa"); + return NULL; + + case SDL_FOLDER_VIDEOS: + dir = NSMoviesDirectory; + break; + + default: + SDL_SetError("Invalid SDL_Folder: %d", (int) folder); + return NULL; + }; + + array = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES); + + if ([array count] <= 0) { + SDL_SetError("Directory not found"); + return NULL; + } + + str = [array objectAtIndex:0]; + base = [str fileSystemRepresentation]; + if (!base) { + SDL_SetError("Couldn't get folder path"); + return NULL; + } + +append_slash: + result = SDL_malloc(SDL_strlen(base) + 2); + if (result == NULL) { + return NULL; + } + + if (SDL_snprintf(result, SDL_strlen(base) + 2, "%s/", base) < 0) { + SDL_SetError("Couldn't snprintf folder path for Cocoa: %s", base); + SDL_free(result); + return NULL; + } + + for (ptr = result + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + mkdir(result, 0700); + *ptr = '/'; + } + } + + return result; +#endif // SDL_PLATFORM_TVOS + } +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#if defined(SDL_FILESYSTEM_DUMMY) || defined(SDL_FILESYSTEM_DISABLED) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +char *SDL_SYS_GetBasePath(void) +{ + SDL_Unsupported(); + return NULL; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + SDL_Unsupported(); + return NULL; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +char *SDL_SYS_GetCurrentDirectory(void) +{ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#if defined(SDL_FSOPS_DUMMY) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) +{ + return SDL_Unsupported(); +} + +bool SDL_SYS_RemovePath(const char *path) +{ + return SDL_Unsupported(); +} + +bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath) +{ + return SDL_Unsupported(); +} + +bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath) +{ + return SDL_Unsupported(); +} + +bool SDL_SYS_CreateDirectory(const char *path) +{ + return SDL_Unsupported(); +} + +bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) +{ + return SDL_Unsupported(); +} + +#endif // SDL_FSOPS_DUMMY + 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_EMSCRIPTEN + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include + +#include + +char *SDL_SYS_GetBasePath(void) +{ + return SDL_strdup("/"); +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + const char *append = "/libsdl/"; + char *result; + char *ptr = NULL; + size_t len = 0; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + len = SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3; + result = (char *)SDL_malloc(len); + if (!result) { + return NULL; + } + + if (*org) { + SDL_snprintf(result, len, "%s%s/%s/", append, org, app); + } else { + SDL_snprintf(result, len, "%s%s/", append, app); + } + + for (ptr = result + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + if (mkdir(result, 0700) != 0 && errno != EEXIST) { + goto error; + } + *ptr = '/'; + } + } + + if (mkdir(result, 0700) != 0 && errno != EEXIST) { + error: + SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno)); + SDL_free(result); + return NULL; + } + + return result; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + const char *home = NULL; + + if (folder != SDL_FOLDER_HOME) { + SDL_SetError("Emscripten only supports the home folder"); + return NULL; + } + + home = SDL_getenv("HOME"); + if (!home) { + SDL_SetError("No $HOME environment variable available"); + return NULL; + } + + char *result = SDL_malloc(SDL_strlen(home) + 2); + if (!result) { + return NULL; + } + + if (SDL_snprintf(result, SDL_strlen(home) + 2, "%s/", home) < 0) { + SDL_SetError("Couldn't snprintf home path for Emscripten: %s", home); + SDL_free(result); + return NULL; + } + + return result; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +extern "C" { +#include "../SDL_sysfilesystem.h" +} + +#include "../../core/windows/SDL_windows.h" +#include +#include +#include +#include + +char * +SDL_SYS_GetBasePath(void) +{ + /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()! + * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o + */ + DWORD buflen = 128; + CHAR *path = NULL; + DWORD len = 0; + int i; + + while (true) { + void *ptr = SDL_realloc(path, buflen * sizeof(CHAR)); + if (!ptr) { + SDL_free(path); + return NULL; + } + + path = (CHAR *)ptr; + + len = GetModuleFileNameA(NULL, path, buflen); + // if it truncated, then len >= buflen - 1 + // if there was enough room (or failure), len < buflen - 1 + if (len < buflen - 1) { + break; + } + + // buffer too small? Try again. + buflen *= 2; + } + + if (len == 0) { + SDL_free(path); + WIN_SetError("Couldn't locate our .exe"); + return NULL; + } + + for (i = len - 1; i > 0; i--) { + if (path[i] == '\\') { + break; + } + } + + SDL_assert(i > 0); // Should have been an absolute path. + path[i + 1] = '\0'; // chop off filename. + + return path; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + XUserHandle user = NULL; + XAsyncBlock block = { 0 }; + char *folderPath; + HRESULT result; + const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID"); + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + + // This should be set before calling SDL_GetPrefPath! + if (!csid) { + SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!"); + return SDL_strdup("T:\\"); + } + + if (!SDL_GetGDKDefaultUser(&user)) { + // Error already set, just return + return NULL; + } + + if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result); + return NULL; + } + + folderPath = (char*) SDL_malloc(MAX_PATH); + do { + result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath); + } while (result == E_PENDING); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result); + SDL_free(folderPath); + return NULL; + } + + /* We aren't using 'app' here because the container rules are a lot more + * strict than the NTFS rules, so it will most likely be invalid :( + */ + SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH); + if (CreateDirectoryA(folderPath, NULL) == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + WIN_SetError("CreateDirectoryA"); + SDL_free(folderPath); + return NULL; + } + } + return folderPath; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +// TODO +char *SDL_SYS_GetCurrentDirectory(void) +{ + SDL_Unsupported(); + return NULL; +} 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_HAIKU + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +extern "C" { +#include "../SDL_sysfilesystem.h" +} + +#include +#include +#include +#include +#include + + +char *SDL_SYS_GetBasePath(void) +{ + char name[MAXPATHLEN]; + + if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, name, sizeof(name)) != B_OK) { + return NULL; + } + + BEntry entry(name, true); + BPath path; + status_t rc = entry.GetPath(&path); // (path) now has binary's path. + SDL_assert(rc == B_OK); + rc = path.GetParent(&path); // chop filename, keep directory. + SDL_assert(rc == B_OK); + const char *str = path.Path(); + SDL_assert(str != NULL); + + const size_t len = SDL_strlen(str); + char *result = (char *) SDL_malloc(len + 2); + if (result) { + SDL_memcpy(result, str, len); + result[len] = '/'; + result[len+1] = '\0'; + } + + return result; +} + + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + // !!! FIXME: is there a better way to do this? + const char *home = SDL_getenv("HOME"); + const char *append = "/config/settings/"; + size_t len = SDL_strlen(home); + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + if (!len || (home[len - 1] == '/')) { + ++append; // home empty or ends with separator, skip the one from append + } + len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3; + char *result = (char *) SDL_malloc(len); + if (result) { + if (*org) { + SDL_snprintf(result, len, "%s%s%s/%s/", home, append, org, app); + } else { + SDL_snprintf(result, len, "%s%s%s/", home, append, app); + } + create_directory(result, 0700); // Haiku api: creates missing dirs + } + + return result; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + const char *home = NULL; + char *result; + + home = SDL_getenv("HOME"); + if (!home) { + SDL_SetError("No $HOME environment variable available"); + return NULL; + } + + switch (folder) { + case SDL_FOLDER_HOME: + result = (char *) SDL_malloc(SDL_strlen(home) + 2); + if (!result) { + return NULL; + } + + if (SDL_snprintf(result, SDL_strlen(home) + 2, "%s/", home) < 0) { + SDL_SetError("Couldn't snprintf home path for Haiku: %s", home); + SDL_free(result); + return NULL; + } + + return result; + + // TODO: Is Haiku's desktop folder always ~/Desktop/ ? + case SDL_FOLDER_DESKTOP: + result = (char *) SDL_malloc(SDL_strlen(home) + 10); + if (!result) { + return NULL; + } + + if (SDL_snprintf(result, SDL_strlen(home) + 10, "%s/Desktop/", home) < 0) { + SDL_SetError("Couldn't snprintf desktop path for Haiku: %s/Desktop/", home); + SDL_free(result); + return NULL; + } + + return result; + + case SDL_FOLDER_DOCUMENTS: + case SDL_FOLDER_DOWNLOADS: + case SDL_FOLDER_MUSIC: + case SDL_FOLDER_PICTURES: + case SDL_FOLDER_PUBLICSHARE: + case SDL_FOLDER_SAVEDGAMES: + case SDL_FOLDER_SCREENSHOTS: + case SDL_FOLDER_TEMPLATES: + case SDL_FOLDER_VIDEOS: + default: + SDL_SetError("Only HOME and DESKTOP available on Haiku"); + return NULL; + } +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_N3DS + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include <3ds.h> +#include +#include + +static char *MakePrefPath(const char *app); +static bool CreatePrefPathDir(const char *pref); + +char *SDL_SYS_GetBasePath(void) +{ + char *base_path = SDL_strdup("romfs:/"); + return base_path; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + char *pref_path = NULL; + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + + pref_path = MakePrefPath(app); + if (!pref_path) { + return NULL; + } + + if (!CreatePrefPathDir(pref_path)) { + SDL_free(pref_path); + return NULL; + } + + return pref_path; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +static char *MakePrefPath(const char *app) +{ + char *pref_path; + if (SDL_asprintf(&pref_path, "sdmc:/3ds/%s/", app) < 0) { + return NULL; + } + return pref_path; +} + +static bool CreatePrefPathDir(const char *pref) +{ + int result = mkdir(pref, 0666); + + if (result == -1 && errno != EEXIST) { + return SDL_SetError("Failed to create '%s' (%s)", pref, strerror(errno)); + } + return true; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#if defined(SDL_FSOPS_POSIX) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include +#include +#include +#include +#include + +bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) +{ + char *pathwithsep = NULL; + int pathwithseplen = SDL_asprintf(&pathwithsep, "%s/", path); + if ((pathwithseplen == -1) || (!pathwithsep)) { + return false; + } + + // trim down to a single path separator at the end, in case the caller added one or more. + pathwithseplen--; + while ((pathwithseplen >= 0) && (pathwithsep[pathwithseplen] == '/')) { + pathwithsep[pathwithseplen--] = '\0'; + } + + DIR *dir = opendir(pathwithsep); + if (!dir) { + SDL_free(pathwithsep); + return SDL_SetError("Can't open directory: %s", strerror(errno)); + } + + // make sure there's a path separator at the end now for the actual callback. + pathwithsep[++pathwithseplen] = '/'; + pathwithsep[++pathwithseplen] = '\0'; + + SDL_EnumerationResult result = SDL_ENUM_CONTINUE; + struct dirent *ent; + while ((result == SDL_ENUM_CONTINUE) && ((ent = readdir(dir)) != NULL)) { + const char *name = ent->d_name; + if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) { + continue; + } + result = cb(userdata, pathwithsep, name); + } + + closedir(dir); + + SDL_free(pathwithsep); + + return (result != SDL_ENUM_FAILURE); +} + +bool SDL_SYS_RemovePath(const char *path) +{ + int rc = remove(path); + if (rc < 0) { + if (errno == ENOENT) { + // It's already gone, this is a success + return true; + } + return SDL_SetError("Can't remove path: %s", strerror(errno)); + } + return true; +} + +bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath) +{ + if (rename(oldpath, newpath) < 0) { + return SDL_SetError("Can't rename path: %s", strerror(errno)); + } + return true; +} + +bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath) +{ + char *buffer = NULL; + SDL_IOStream *input = NULL; + SDL_IOStream *output = NULL; + const size_t maxlen = 4096; + size_t len; + bool result = false; + + input = SDL_IOFromFile(oldpath, "rb"); + if (!input) { + goto done; + } + + output = SDL_IOFromFile(newpath, "wb"); + if (!output) { + goto done; + } + + buffer = (char *)SDL_malloc(maxlen); + if (!buffer) { + goto done; + } + + while ((len = SDL_ReadIO(input, buffer, maxlen)) > 0) { + if (SDL_WriteIO(output, buffer, len) < len) { + goto done; + } + } + if (SDL_GetIOStatus(input) != SDL_IO_STATUS_EOF) { + goto done; + } + + SDL_CloseIO(input); + input = NULL; + + if (!SDL_FlushIO(output)) { + goto done; + } + + result = SDL_CloseIO(output); + output = NULL; // it's gone, even if it failed. + +done: + if (output) { + SDL_CloseIO(output); + } + if (input) { + SDL_CloseIO(input); + } + SDL_free(buffer); + + return result; +} + +bool SDL_SYS_CreateDirectory(const char *path) +{ + const int rc = mkdir(path, 0770); + if (rc < 0) { + const int origerrno = errno; + if (origerrno == EEXIST) { + struct stat statbuf; + if ((stat(path, &statbuf) == 0) && (S_ISDIR(statbuf.st_mode))) { + return true; // it already exists and it's a directory, consider it success. + } + } + return SDL_SetError("Can't create directory: %s", strerror(origerrno)); + } + return true; +} + +bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) +{ + struct stat statbuf; + const int rc = stat(path, &statbuf); + if (rc < 0) { + return SDL_SetError("Can't stat: %s", strerror(errno)); + } else if (S_ISREG(statbuf.st_mode)) { + info->type = SDL_PATHTYPE_FILE; + info->size = (Uint64) statbuf.st_size; + } else if (S_ISDIR(statbuf.st_mode)) { + info->type = SDL_PATHTYPE_DIRECTORY; + info->size = 0; + } else { + info->type = SDL_PATHTYPE_OTHER; + info->size = (Uint64) statbuf.st_size; + } + +#if defined(HAVE_ST_MTIM) + // POSIX.1-2008 standard + info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctim.tv_sec) + statbuf.st_ctim.tv_nsec; + info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtim.tv_sec) + statbuf.st_mtim.tv_nsec; + info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atim.tv_sec) + statbuf.st_atim.tv_nsec; +#elif defined(SDL_PLATFORM_APPLE) + /* Apple platform stat structs use 'st_*timespec' naming. */ + info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctimespec.tv_sec) + statbuf.st_ctimespec.tv_nsec; + info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtimespec.tv_sec) + statbuf.st_mtimespec.tv_nsec; + info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atimespec.tv_sec) + statbuf.st_atimespec.tv_nsec; +#else + info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctime); + info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtime); + info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atime); +#endif + return true; +} + +// 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. +char *SDL_SYS_GetCurrentDirectory(void) +{ + size_t buflen = 64; + char *buf = NULL; + + while (true) { + void *ptr = SDL_realloc(buf, buflen); + if (!ptr) { + SDL_free(buf); + return NULL; + } + buf = (char *) ptr; + + if (getcwd(buf, buflen-1) != NULL) { + break; // we got it! + } + + if (errno == ERANGE) { + buflen *= 2; // try again with a bigger buffer. + continue; + } + + SDL_free(buf); + SDL_SetError("getcwd failed: %s", strerror(errno)); + return NULL; + } + + // make sure there's a path separator at the end. + SDL_assert(SDL_strlen(buf) < (buflen + 2)); + buflen = SDL_strlen(buf); + if ((buflen == 0) || (buf[buflen-1] != '/')) { + buf[buflen] = '/'; + buf[buflen + 1] = '\0'; + } + + return buf; +} + +#endif // SDL_FSOPS_POSIX + 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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_PS2 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include + +char *SDL_SYS_GetBasePath(void) +{ + char *result = NULL; + size_t len; + char cwd[FILENAME_MAX]; + + getcwd(cwd, sizeof(cwd)); + len = SDL_strlen(cwd) + 2; + result = (char *)SDL_malloc(len); + if (result) { + SDL_snprintf(result, len, "%s/", cwd); + } + + return result; +} + +// Do a recursive mkdir of parents folders +static void recursive_mkdir(const char *dir) +{ + char tmp[FILENAME_MAX]; + const char *base = SDL_GetBasePath(); + char *p = NULL; + size_t len; + + SDL_snprintf(tmp, sizeof(tmp), "%s", dir); + len = SDL_strlen(tmp); + if (tmp[len - 1] == '/') { + tmp[len - 1] = 0; + } + + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + // Just creating subfolders from current path + if (base && SDL_strstr(tmp, base) != NULL) { + mkdir(tmp, S_IRWXU); + } + + *p = '/'; + } + } + + mkdir(tmp, S_IRWXU); +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + char *result = NULL; + size_t len; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + + if (!org) { + org = ""; + } + + const char *base = SDL_GetBasePath(); + if (!base) { + return NULL; + } + + len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; + result = (char *)SDL_malloc(len); + if (result) { + if (*org) { + SDL_snprintf(result, len, "%s%s/%s/", base, org, app); + } else { + SDL_snprintf(result, len, "%s%s/", base, app); + } + + recursive_mkdir(result); + } + + return result; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_PSP + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include + +char *SDL_SYS_GetBasePath(void) +{ + char *result = NULL; + size_t len; + char cwd[FILENAME_MAX]; + + getcwd(cwd, sizeof(cwd)); + len = SDL_strlen(cwd) + 2; + result = (char *)SDL_malloc(len); + if (result) { + SDL_snprintf(result, len, "%s/", cwd); + } + + return result; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + char *result = NULL; + size_t len; + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + + const char *base = SDL_GetBasePath(); + if (!base) { + return NULL; + } + + if (!org) { + org = ""; + } + + len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; + result = (char *)SDL_malloc(len); + if (result) { + if (*org) { + SDL_snprintf(result, len, "%s%s/%s/", base, org, app); + } else { + SDL_snprintf(result, len, "%s%s/", base, app); + } + + mkdir(result, 0755); + } + + return result; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_RISCOS + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include +#include + +// Wrapper around __unixify_std that uses SDL's memory allocators +static char *SDL_unixify_std(const char *ro_path, char *buffer, size_t buf_len, int filetype) +{ + const char *const in_buf = buffer; // = NULL if we allocate the buffer. + + if (!buffer) { + /* This matches the logic in __unixify, with an additional byte for the + * extra path separator. + */ + buf_len = SDL_strlen(ro_path) + 14 + 1; + buffer = SDL_malloc(buf_len); + + if (!buffer) { + return NULL; + } + } + + if (!__unixify_std(ro_path, buffer, buf_len, filetype)) { + if (!in_buf) { + SDL_free(buffer); + } + + SDL_SetError("Could not convert '%s' to a Unix-style path", ro_path); + return NULL; + } + + /* HACK: It's necessary to add an extra path separator here since SDL's API + * requires it, however paths with trailing separators aren't normally valid + * on RISC OS. + */ + if (__get_riscosify_control() & __RISCOSIFY_NO_PROCESS) + SDL_strlcat(buffer, ".", buf_len); + else + SDL_strlcat(buffer, "/", buf_len); + + return buffer; +} + +static char *canonicalisePath(const char *path, const char *pathVar) +{ + _kernel_oserror *error; + _kernel_swi_regs regs; + char *buf; + + regs.r[0] = 37; + regs.r[1] = (int)path; + regs.r[2] = 0; + regs.r[3] = (int)pathVar; + regs.r[4] = 0; + regs.r[5] = 0; + error = _kernel_swi(OS_FSControl, ®s, ®s); + if (error) { + SDL_SetError("Couldn't canonicalise path: %s", error->errmess); + return NULL; + } + + regs.r[5] = 1 - regs.r[5]; + buf = SDL_malloc(regs.r[5]); + if (!buf) { + return NULL; + } + regs.r[2] = (int)buf; + error = _kernel_swi(OS_FSControl, ®s, ®s); + if (error) { + SDL_SetError("Couldn't canonicalise path: %s", error->errmess); + SDL_free(buf); + return NULL; + } + + return buf; +} + +static _kernel_oserror *createDirectoryRecursive(char *path) +{ + char *ptr = NULL; + _kernel_oserror *error; + _kernel_swi_regs regs; + regs.r[0] = 8; + regs.r[1] = (int)path; + regs.r[2] = 0; + + for (ptr = path + 1; *ptr; ptr++) { + if (*ptr == '.') { + *ptr = '\0'; + error = _kernel_swi(OS_File, ®s, ®s); + *ptr = '.'; + if (error) { + return error; + } + } + } + return _kernel_swi(OS_File, ®s, ®s); +} + +char *SDL_SYS_GetBasePath(void) +{ + _kernel_swi_regs regs; + _kernel_oserror *error; + char *canon, *ptr, *result; + + error = _kernel_swi(OS_GetEnv, ®s, ®s); + if (error) { + return NULL; + } + + canon = canonicalisePath((const char *)regs.r[0], "Run$Path"); + if (!canon) { + return NULL; + } + + // chop off filename. + ptr = SDL_strrchr(canon, '.'); + if (ptr) { + *ptr = '\0'; + } + + result = SDL_unixify_std(canon, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED); + SDL_free(canon); + return result; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + char *canon, *dir, *result; + size_t len; + _kernel_oserror *error; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + canon = canonicalisePath("", "Run$Path"); + if (!canon) { + return NULL; + } + + len = SDL_strlen(canon) + SDL_strlen(org) + SDL_strlen(app) + 4; + dir = (char *)SDL_malloc(len); + if (!dir) { + SDL_free(canon); + return NULL; + } + + if (*org) { + SDL_snprintf(dir, len, "%s.%s.%s", canon, org, app); + } else { + SDL_snprintf(dir, len, "%s.%s", canon, app); + } + + SDL_free(canon); + + error = createDirectoryRecursive(dir); + if (error) { + SDL_SetError("Couldn't create directory: %s", error->errmess); + SDL_free(dir); + return NULL; + } + + result = SDL_unixify_std(dir, NULL, 0, __RISCOSIFY_FILETYPE_NOTSPECIFIED); + SDL_free(dir); + return result; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_UNIX + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD) +#include +#endif + +static char *readSymLink(const char *path) +{ + char *result = NULL; + ssize_t len = 64; + ssize_t rc = -1; + + while (1) { + char *ptr = (char *)SDL_realloc(result, (size_t)len); + if (!ptr) { + break; + } + + result = ptr; + + rc = readlink(path, result, len); + if (rc == -1) { + break; // not a symlink, i/o error, etc. + } else if (rc < len) { + result[rc] = '\0'; // readlink doesn't null-terminate. + return result; // we're good to go. + } + + len *= 2; // grow buffer, try again. + } + + SDL_free(result); + return NULL; +} + +#ifdef SDL_PLATFORM_OPENBSD +static char *search_path_for_binary(const char *bin) +{ + const char *envr_real = SDL_getenv("PATH"); + char *envr; + size_t alloc_size; + char *exe = NULL; + char *start = envr; + char *ptr; + + if (!envr_real) { + SDL_SetError("No $PATH set"); + return NULL; + } + + envr = SDL_strdup(envr_real); + if (!envr) { + return NULL; + } + + SDL_assert(bin != NULL); + + alloc_size = SDL_strlen(bin) + SDL_strlen(envr) + 2; + exe = (char *)SDL_malloc(alloc_size); + + do { + ptr = SDL_strchr(start, ':'); // find next $PATH separator. + if (ptr != start) { + if (ptr) { + *ptr = '\0'; + } + + // build full binary path... + SDL_snprintf(exe, alloc_size, "%s%s%s", start, (ptr && (ptr[-1] == '/')) ? "" : "/", bin); + + if (access(exe, X_OK) == 0) { // Exists as executable? We're done. + SDL_free(envr); + return exe; + } + } + start = ptr + 1; // start points to beginning of next element. + } while (ptr); + + SDL_free(envr); + SDL_free(exe); + + SDL_SetError("Process not found in $PATH"); + return NULL; // doesn't exist in path. +} +#endif + +char *SDL_SYS_GetBasePath(void) +{ + char *result = NULL; + +#ifdef SDL_PLATFORM_FREEBSD + char fullpath[PATH_MAX]; + size_t buflen = sizeof(fullpath); + const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) { + result = SDL_strdup(fullpath); + if (!result) { + return NULL; + } + } +#endif +#ifdef SDL_PLATFORM_OPENBSD + // 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. + char **cmdline; + size_t len; + const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) { + char *exe, *pwddst; + char *realpathbuf = (char *)SDL_malloc(PATH_MAX + 1); + if (!realpathbuf) { + return NULL; + } + + cmdline = SDL_malloc(len); + if (!cmdline) { + SDL_free(realpathbuf); + return NULL; + } + + sysctl(mib, 4, cmdline, &len, NULL, 0); + + exe = cmdline[0]; + pwddst = NULL; + if (SDL_strchr(exe, '/') == NULL) { // not a relative or absolute path, check $PATH for it + exe = search_path_for_binary(cmdline[0]); + } else { + if (exe && *exe == '.') { + const char *pwd = SDL_getenv("PWD"); + if (pwd && *pwd) { + SDL_asprintf(&pwddst, "%s/%s", pwd, exe); + } + } + } + + if (exe) { + if (!pwddst) { + if (realpath(exe, realpathbuf) != NULL) { + result = realpathbuf; + } + } else { + if (realpath(pwddst, realpathbuf) != NULL) { + result = realpathbuf; + } + SDL_free(pwddst); + } + + if (exe != cmdline[0]) { + SDL_free(exe); + } + } + + if (!result) { + SDL_free(realpathbuf); + } + + SDL_free(cmdline); + } +#endif + + // is a Linux-style /proc filesystem available? + if (!result && (access("/proc", F_OK) == 0)) { + /* !!! FIXME: after 2.0.6 ships, let's delete this code and just + use the /proc/%llu version. There's no reason to have + two copies of this plus all the #ifdefs. --ryan. */ +#ifdef SDL_PLATFORM_FREEBSD + result = readSymLink("/proc/curproc/file"); +#elif defined(SDL_PLATFORM_NETBSD) + result = readSymLink("/proc/curproc/exe"); +#elif defined(SDL_PLATFORM_SOLARIS) + result = readSymLink("/proc/self/path/a.out"); +#else + result = readSymLink("/proc/self/exe"); // linux. + if (!result) { + // older kernels don't have /proc/self ... try PID version... + char path[64]; + const int rc = SDL_snprintf(path, sizeof(path), + "/proc/%llu/exe", + (unsigned long long)getpid()); + if ((rc > 0) && (rc < sizeof(path))) { + result = readSymLink(path); + } + } +#endif + } + +#ifdef SDL_PLATFORM_SOLARIS // try this as a fallback if /proc didn't pan out + if (!result) { + const char *path = getexecname(); + if ((path) && (path[0] == '/')) { // must be absolute path... + result = SDL_strdup(path); + if (!result) { + return NULL; + } + } + } +#endif + /* If we had access to argv[0] here, we could check it for a path, + or troll through $PATH looking for it, too. */ + + if (result) { // chop off filename. + char *ptr = SDL_strrchr(result, '/'); + if (ptr) { + *(ptr + 1) = '\0'; + } else { // shouldn't happen, but just in case... + SDL_free(result); + result = NULL; + } + } + + if (result) { + // try to shrink buffer... + char *ptr = (char *)SDL_realloc(result, SDL_strlen(result) + 1); + if (ptr) { + result = ptr; // oh well if it failed. + } + } + + return result; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + /* + * We use XDG's base directory spec, even if you're not on Linux. + * This isn't strictly correct, but the results are relatively sane + * in any case. + * + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + const char *envr = SDL_getenv("XDG_DATA_HOME"); + const char *append; + char *result = NULL; + char *ptr = NULL; + size_t len = 0; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + if (!envr) { + // You end up with "$HOME/.local/share/Game Name 2" + envr = SDL_getenv("HOME"); + if (!envr) { + // we could take heroic measures with /etc/passwd, but oh well. + SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set"); + return NULL; + } + append = "/.local/share/"; + } else { + append = "/"; + } + + len = SDL_strlen(envr); + if (envr[len - 1] == '/') { + append += 1; + } + + len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3; + result = (char *)SDL_malloc(len); + if (!result) { + return NULL; + } + + if (*org) { + (void)SDL_snprintf(result, len, "%s%s%s/%s/", envr, append, org, app); + } else { + (void)SDL_snprintf(result, len, "%s%s%s/", envr, append, app); + } + + for (ptr = result + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + if (mkdir(result, 0700) != 0 && errno != EEXIST) { + goto error; + } + *ptr = '/'; + } + } + if (mkdir(result, 0700) != 0 && errno != EEXIST) { + error: + SDL_SetError("Couldn't create directory '%s': '%s'", result, strerror(errno)); + SDL_free(result); + return NULL; + } + + return result; +} + +/* + The two functions below (prefixed with `xdg_`) have been copied from: + https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/blob/master/xdg-user-dir-lookup.c + and have been adapted to work with SDL. They are licensed under the following + terms: + + Copyright (c) 2007 Red Hat, Inc. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback) +{ + FILE *file; + const char *home_dir, *config_home; + char *config_file; + char buffer[512]; + char *user_dir; + char *p, *d; + int len; + int relative; + size_t l; + + home_dir = SDL_getenv("HOME"); + + if (!home_dir) + goto error; + + config_home = SDL_getenv("XDG_CONFIG_HOME"); + if (!config_home || config_home[0] == 0) + { + l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1; + config_file = (char*) SDL_malloc (l); + if (!config_file) + goto error; + + SDL_strlcpy (config_file, home_dir, l); + SDL_strlcat (config_file, "/.config/user-dirs.dirs", l); + } + else + { + l = SDL_strlen (config_home) + SDL_strlen ("/user-dirs.dirs") + 1; + config_file = (char*) SDL_malloc (l); + if (!config_file) + goto error; + + SDL_strlcpy (config_file, config_home, l); + SDL_strlcat (config_file, "/user-dirs.dirs", l); + } + + file = fopen (config_file, "r"); + SDL_free (config_file); + if (!file) + goto error; + + user_dir = NULL; + while (fgets (buffer, sizeof (buffer), file)) + { + // Remove newline at end + len = SDL_strlen (buffer); + if (len > 0 && buffer[len-1] == '\n') + buffer[len-1] = 0; + + p = buffer; + while (*p == ' ' || *p == '\t') + p++; + + if (SDL_strncmp (p, "XDG_", 4) != 0) + continue; + p += 4; + if (SDL_strncmp (p, type, SDL_strlen (type)) != 0) + continue; + p += SDL_strlen (type); + if (SDL_strncmp (p, "_DIR", 4) != 0) + continue; + p += 4; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '=') + continue; + p++; + + while (*p == ' ' || *p == '\t') + p++; + + if (*p != '"') + continue; + p++; + + relative = 0; + if (SDL_strncmp (p, "$HOME/", 6) == 0) + { + p += 6; + relative = 1; + } + else if (*p != '/') + continue; + + SDL_free (user_dir); + if (relative) + { + l = SDL_strlen (home_dir) + 1 + SDL_strlen (p) + 1; + user_dir = (char*) SDL_malloc (l); + if (!user_dir) + goto error2; + + SDL_strlcpy (user_dir, home_dir, l); + SDL_strlcat (user_dir, "/", l); + } + else + { + user_dir = (char*) SDL_malloc (SDL_strlen (p) + 1); + if (!user_dir) + goto error2; + + *user_dir = 0; + } + + d = user_dir + SDL_strlen (user_dir); + while (*p && *p != '"') + { + if ((*p == '\\') && (*(p+1) != 0)) + p++; + *d++ = *p++; + } + *d = 0; + } +error2: + fclose (file); + + if (user_dir) + return user_dir; + + error: + if (fallback) + return SDL_strdup (fallback); + return NULL; +} + +static char *xdg_user_dir_lookup (const char *type) +{ + const char *home_dir; + char *dir, *user_dir; + + dir = xdg_user_dir_lookup_with_fallback(type, NULL); + if (dir) + return dir; + + home_dir = SDL_getenv("HOME"); + + if (!home_dir) + return NULL; + + // Special case desktop for historical compatibility + if (SDL_strcmp(type, "DESKTOP") == 0) { + size_t length = SDL_strlen(home_dir) + SDL_strlen("/Desktop") + 1; + user_dir = (char*) SDL_malloc(length); + if (!user_dir) + return NULL; + + SDL_strlcpy(user_dir, home_dir, length); + SDL_strlcat(user_dir, "/Desktop", length); + return user_dir; + } + + return NULL; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + const char *param = NULL; + char *result; + char *newresult; + + /* According to `man xdg-user-dir`, the possible values are: + DESKTOP + DOWNLOAD + TEMPLATES + PUBLICSHARE + DOCUMENTS + MUSIC + PICTURES + VIDEOS + */ + switch(folder) { + case SDL_FOLDER_HOME: + param = SDL_getenv("HOME"); + + if (!param) { + SDL_SetError("No $HOME environment variable available"); + return NULL; + } + + result = SDL_strdup(param); + goto append_slash; + + case SDL_FOLDER_DESKTOP: + param = "DESKTOP"; + break; + + case SDL_FOLDER_DOCUMENTS: + param = "DOCUMENTS"; + break; + + case SDL_FOLDER_DOWNLOADS: + param = "DOWNLOAD"; + break; + + case SDL_FOLDER_MUSIC: + param = "MUSIC"; + break; + + case SDL_FOLDER_PICTURES: + param = "PICTURES"; + break; + + case SDL_FOLDER_PUBLICSHARE: + param = "PUBLICSHARE"; + break; + + case SDL_FOLDER_SAVEDGAMES: + SDL_SetError("Saved Games folder unavailable on XDG"); + return NULL; + + case SDL_FOLDER_SCREENSHOTS: + SDL_SetError("Screenshots folder unavailable on XDG"); + return NULL; + + case SDL_FOLDER_TEMPLATES: + param = "TEMPLATES"; + break; + + case SDL_FOLDER_VIDEOS: + param = "VIDEOS"; + break; + + default: + SDL_SetError("Invalid SDL_Folder: %d", (int) folder); + return NULL; + } + + /* param *should* to be set to something at this point, but just in case */ + if (!param) { + SDL_SetError("No corresponding XDG user directory"); + return NULL; + } + + result = xdg_user_dir_lookup(param); + + if (!result) { + SDL_SetError("XDG directory not available"); + return NULL; + } + +append_slash: + newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2); + + if (!newresult) { + SDL_free(result); + return NULL; + } + + result = newresult; + SDL_strlcat(result, "/", SDL_strlen(result) + 2); + + return result; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_VITA + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +char *SDL_SYS_GetBasePath(void) +{ + return SDL_strdup("app0:/"); +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + const char *envr = "ux0:/data/"; + char *result = NULL; + char *ptr = NULL; + size_t len = 0; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + len = SDL_strlen(envr); + + len += SDL_strlen(org) + SDL_strlen(app) + 3; + result = (char *)SDL_malloc(len); + if (!result) { + return NULL; + } + + if (*org) { + SDL_snprintf(result, len, "%s%s/%s/", envr, org, app); + } else { + SDL_snprintf(result, len, "%s%s/", envr, app); + } + + for (ptr = result + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + sceIoMkdir(result, 0777); + *ptr = '/'; + } + } + sceIoMkdir(result, 0777); + + return result; +} + +// TODO +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + SDL_Unsupported(); + return NULL; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_FILESYSTEM_WINDOWS + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../SDL_sysfilesystem.h" + +#include "../../core/windows/SDL_windows.h" +#include +#include + +// These aren't all defined in older SDKs, so define them here +DEFINE_GUID(SDL_FOLDERID_Profile, 0x5E6C858F, 0x0E22, 0x4760, 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73); +DEFINE_GUID(SDL_FOLDERID_Desktop, 0xB4BFCC3A, 0xDB2C, 0x424C, 0xB0, 0x29, 0x7F, 0xE9, 0x9A, 0x87, 0xC6, 0x41); +DEFINE_GUID(SDL_FOLDERID_Documents, 0xFDD39AD0, 0x238F, 0x46AF, 0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7); +DEFINE_GUID(SDL_FOLDERID_Downloads, 0x374de290, 0x123f, 0x4565, 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b); +DEFINE_GUID(SDL_FOLDERID_Music, 0x4BD8D571, 0x6D19, 0x48D3, 0xBE, 0x97, 0x42, 0x22, 0x20, 0x08, 0x0E, 0x43); +DEFINE_GUID(SDL_FOLDERID_Pictures, 0x33E28130, 0x4E1E, 0x4676, 0x83, 0x5A, 0x98, 0x39, 0x5C, 0x3B, 0xC3, 0xBB); +DEFINE_GUID(SDL_FOLDERID_SavedGames, 0x4c5c32ff, 0xbb9d, 0x43b0, 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4); +DEFINE_GUID(SDL_FOLDERID_Screenshots, 0xb7bede81, 0xdf94, 0x4682, 0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f); +DEFINE_GUID(SDL_FOLDERID_Templates, 0xA63293E8, 0x664E, 0x48DB, 0xA0, 0x79, 0xDF, 0x75, 0x9E, 0x05, 0x09, 0xF7); +DEFINE_GUID(SDL_FOLDERID_Videos, 0x18989B1D, 0x99B5, 0x455B, 0x84, 0x1C, 0xAB, 0x7C, 0x74, 0xE4, 0xDD, 0xFC); + +char *SDL_SYS_GetBasePath(void) +{ + DWORD buflen = 128; + WCHAR *path = NULL; + char *result = NULL; + DWORD len = 0; + int i; + + while (true) { + void *ptr = SDL_realloc(path, buflen * sizeof(WCHAR)); + if (!ptr) { + SDL_free(path); + return NULL; + } + + path = (WCHAR *)ptr; + + len = GetModuleFileNameW(NULL, path, buflen); + // if it truncated, then len >= buflen - 1 + // if there was enough room (or failure), len < buflen - 1 + if (len < buflen - 1) { + break; + } + + // buffer too small? Try again. + buflen *= 2; + } + + if (len == 0) { + SDL_free(path); + WIN_SetError("Couldn't locate our .exe"); + return NULL; + } + + for (i = len - 1; i > 0; i--) { + if (path[i] == '\\') { + break; + } + } + + SDL_assert(i > 0); // Should have been an absolute path. + path[i + 1] = '\0'; // chop off filename. + + result = WIN_StringToUTF8W(path); + SDL_free(path); + + return result; +} + +char *SDL_SYS_GetPrefPath(const char *org, const char *app) +{ + /* + * Vista and later has a new API for this, but SHGetFolderPath works there, + * and apparently just wraps the new API. This is the new way to do it: + * + * SHGetKnownFolderPath(SDL_FOLDERID_RoamingAppData, KF_FLAG_CREATE, + * NULL, &wszPath); + */ + + HRESULT hr = E_FAIL; + WCHAR path[MAX_PATH]; + char *result = NULL; + WCHAR *worg = NULL; + WCHAR *wapp = NULL; + size_t new_wpath_len = 0; + BOOL api_result = FALSE; + + if (!app) { + SDL_InvalidParamError("app"); + return NULL; + } + if (!org) { + org = ""; + } + + hr = SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path); + if (!SUCCEEDED(hr)) { + WIN_SetErrorFromHRESULT("Couldn't locate our prefpath", hr); + return NULL; + } + + worg = WIN_UTF8ToStringW(org); + if (!worg) { + return NULL; + } + + wapp = WIN_UTF8ToStringW(app); + if (!wapp) { + SDL_free(worg); + return NULL; + } + + new_wpath_len = SDL_wcslen(worg) + SDL_wcslen(wapp) + SDL_wcslen(path) + 3; + + if ((new_wpath_len + 1) > MAX_PATH) { + SDL_free(worg); + SDL_free(wapp); + WIN_SetError("Path too long."); + return NULL; + } + + if (*worg) { + SDL_wcslcat(path, L"\\", SDL_arraysize(path)); + SDL_wcslcat(path, worg, SDL_arraysize(path)); + } + SDL_free(worg); + + api_result = CreateDirectoryW(path, NULL); + if (api_result == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + SDL_free(wapp); + WIN_SetError("Couldn't create a prefpath."); + return NULL; + } + } + + SDL_wcslcat(path, L"\\", SDL_arraysize(path)); + SDL_wcslcat(path, wapp, SDL_arraysize(path)); + SDL_free(wapp); + + api_result = CreateDirectoryW(path, NULL); + if (api_result == FALSE) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + WIN_SetError("Couldn't create a prefpath."); + return NULL; + } + } + + SDL_wcslcat(path, L"\\", SDL_arraysize(path)); + + result = WIN_StringToUTF8W(path); + + return result; +} + +char *SDL_SYS_GetUserFolder(SDL_Folder folder) +{ + typedef HRESULT (WINAPI *pfnSHGetKnownFolderPath)(REFGUID /* REFKNOWNFOLDERID */, DWORD, HANDLE, PWSTR*); + HMODULE lib = LoadLibrary(L"Shell32.dll"); + pfnSHGetKnownFolderPath pSHGetKnownFolderPath = NULL; + char *result = NULL; + + if (lib) { + pSHGetKnownFolderPath = (pfnSHGetKnownFolderPath)GetProcAddress(lib, "SHGetKnownFolderPath"); + } + + if (pSHGetKnownFolderPath) { + GUID type; // KNOWNFOLDERID + HRESULT hr; + wchar_t *path; + + switch (folder) { + case SDL_FOLDER_HOME: + type = SDL_FOLDERID_Profile; + break; + + case SDL_FOLDER_DESKTOP: + type = SDL_FOLDERID_Desktop; + break; + + case SDL_FOLDER_DOCUMENTS: + type = SDL_FOLDERID_Documents; + break; + + case SDL_FOLDER_DOWNLOADS: + type = SDL_FOLDERID_Downloads; + break; + + case SDL_FOLDER_MUSIC: + type = SDL_FOLDERID_Music; + break; + + case SDL_FOLDER_PICTURES: + type = SDL_FOLDERID_Pictures; + break; + + case SDL_FOLDER_PUBLICSHARE: + SDL_SetError("Public share unavailable on Windows"); + goto done; + + case SDL_FOLDER_SAVEDGAMES: + type = SDL_FOLDERID_SavedGames; + break; + + case SDL_FOLDER_SCREENSHOTS: + type = SDL_FOLDERID_Screenshots; + break; + + case SDL_FOLDER_TEMPLATES: + type = SDL_FOLDERID_Templates; + break; + + case SDL_FOLDER_VIDEOS: + type = SDL_FOLDERID_Videos; + break; + + default: + SDL_SetError("Invalid SDL_Folder: %d", (int)folder); + goto done; + }; + + hr = pSHGetKnownFolderPath(&type, 0x00008000 /* KF_FLAG_CREATE */, NULL, &path); + if (SUCCEEDED(hr)) { + result = WIN_StringToUTF8W(path); + } else { + WIN_SetErrorFromHRESULT("Couldn't get folder", hr); + } + + } else { + int type; + HRESULT hr; + wchar_t path[MAX_PATH]; + + switch (folder) { + case SDL_FOLDER_HOME: + type = CSIDL_PROFILE; + break; + + case SDL_FOLDER_DESKTOP: + type = CSIDL_DESKTOP; + break; + + case SDL_FOLDER_DOCUMENTS: + type = CSIDL_MYDOCUMENTS; + break; + + case SDL_FOLDER_DOWNLOADS: + SDL_SetError("Downloads folder unavailable before Vista"); + goto done; + + case SDL_FOLDER_MUSIC: + type = CSIDL_MYMUSIC; + break; + + case SDL_FOLDER_PICTURES: + type = CSIDL_MYPICTURES; + break; + + case SDL_FOLDER_PUBLICSHARE: + SDL_SetError("Public share unavailable on Windows"); + goto done; + + case SDL_FOLDER_SAVEDGAMES: + SDL_SetError("Saved games unavailable before Vista"); + goto done; + + case SDL_FOLDER_SCREENSHOTS: + SDL_SetError("Screenshots folder unavailable before Vista"); + goto done; + + case SDL_FOLDER_TEMPLATES: + type = CSIDL_TEMPLATES; + break; + + case SDL_FOLDER_VIDEOS: + type = CSIDL_MYVIDEO; + break; + + default: + SDL_SetError("Unsupported SDL_Folder on Windows before Vista: %d", (int)folder); + goto done; + }; + + // Create the OS-specific folder if it doesn't already exist + type |= CSIDL_FLAG_CREATE; + +#if 0 + // Apparently the oldest, but not supported in modern Windows + HRESULT hr = SHGetSpecialFolderPath(NULL, path, type, TRUE); +#endif + + /* Windows 2000/XP and later, deprecated as of Windows 10 (still + available), available in Wine (tested 6.0.3) */ + hr = SHGetFolderPathW(NULL, type, NULL, SHGFP_TYPE_CURRENT, path); + + // use `== TRUE` for SHGetSpecialFolderPath + if (SUCCEEDED(hr)) { + result = WIN_StringToUTF8W(path); + } else { + WIN_SetErrorFromHRESULT("Couldn't get folder", hr); + } + } + + if (result) { + char *newresult = (char *) SDL_realloc(result, SDL_strlen(result) + 2); + + if (!newresult) { + SDL_free(result); + result = NULL; // will be returned + goto done; + } + + result = newresult; + SDL_strlcat(result, "\\", SDL_strlen(result) + 2); + } + +done: + if (lib) { + FreeLibrary(lib); + } + return result; +} + +char *SDL_SYS_GetCurrentDirectory(void) +{ + WCHAR *wstr = NULL; + DWORD buflen = 0; + while (true) { + const DWORD bw = GetCurrentDirectoryW(buflen, wstr); + if (bw == 0) { + WIN_SetError("GetCurrentDirectoryW failed"); + return NULL; + } else if (bw < buflen) { // we got it! + // make sure there's a path separator at the end. + SDL_assert(bw < (buflen + 2)); + if ((bw == 0) || (wstr[bw-1] != '\\')) { + wstr[bw] = '\\'; + wstr[bw + 1] = '\0'; + } + break; + } + + void *ptr = SDL_realloc(wstr, (bw + 1) * sizeof (WCHAR)); + if (!ptr) { + SDL_free(wstr); + return NULL; + } + wstr = (WCHAR *) ptr; + buflen = bw; + } + + char *retval = WIN_StringToUTF8W(wstr); + SDL_free(wstr); + return retval; +} + +#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 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#if defined(SDL_FSOPS_WINDOWS) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// System dependent filesystem routines + +#include "../../core/windows/SDL_windows.h" +#include "../SDL_sysfilesystem.h" + +bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata) +{ + SDL_EnumerationResult result = SDL_ENUM_CONTINUE; + if (*path == '\0') { // if empty (completely at the root), we need to enumerate drive letters. + const DWORD drives = GetLogicalDrives(); + char name[] = { 0, ':', '\\', '\0' }; + for (int i = 'A'; (result == SDL_ENUM_CONTINUE) && (i <= 'Z'); i++) { + if (drives & (1 << (i - 'A'))) { + name[0] = (char) i; + result = cb(userdata, "", name); + } + } + } else { + // you need a wildcard to enumerate through FindFirstFileEx(), but the wildcard is only checked in the + // filename element at the end of the path string, so always tack on a "\\*" to get everything, and + // also prevent any wildcards inserted by the app from being respected. + char *pattern = NULL; + int patternlen = SDL_asprintf(&pattern, "%s\\\\", path); // we'll replace that second '\\' in the trimdown. + if ((patternlen == -1) || (!pattern)) { + return false; + } + + // trim down to a single path separator at the end, in case the caller added one or more. + patternlen--; + while ((patternlen >= 0) && ((pattern[patternlen] == '\\') || (pattern[patternlen] == '/'))) { + pattern[patternlen--] ='\0'; + } + pattern[++patternlen] = '\\'; + pattern[++patternlen] = '*'; + pattern[++patternlen] = '\0'; + + WCHAR *wpattern = WIN_UTF8ToStringW(pattern); + if (!wpattern) { + SDL_free(pattern); + return false; + } + + pattern[--patternlen] = '\0'; // chop off the '*' so we just have the dirname with a path separator. + + WIN32_FIND_DATAW entw; + HANDLE dir = FindFirstFileExW(wpattern, FindExInfoStandard, &entw, FindExSearchNameMatch, NULL, 0); + SDL_free(wpattern); + if (dir == INVALID_HANDLE_VALUE) { + SDL_free(pattern); + return WIN_SetError("Failed to enumerate directory"); + } + + do { + const WCHAR *fn = entw.cFileName; + + if (fn[0] == '.') { // ignore "." and ".." + if ((fn[1] == '\0') || ((fn[1] == '.') && (fn[2] == '\0'))) { + continue; + } + } + + char *utf8fn = WIN_StringToUTF8W(fn); + if (!utf8fn) { + result = SDL_ENUM_FAILURE; + } else { + result = cb(userdata, pattern, utf8fn); + SDL_free(utf8fn); + } + } while ((result == SDL_ENUM_CONTINUE) && (FindNextFileW(dir, &entw) != 0)); + + FindClose(dir); + SDL_free(pattern); + } + + return (result != SDL_ENUM_FAILURE); +} + +bool SDL_SYS_RemovePath(const char *path) +{ + WCHAR *wpath = WIN_UTF8ToStringW(path); + if (!wpath) { + return false; + } + + WIN32_FILE_ATTRIBUTE_DATA info; + if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &info)) { + SDL_free(wpath); + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + // Note that ERROR_PATH_NOT_FOUND means a parent dir is missing, and we consider that an error. + return true; // thing is already gone, call it a success. + } + return WIN_SetError("Couldn't get path's attributes"); + } + + const int isdir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + const BOOL rc = isdir ? RemoveDirectoryW(wpath) : DeleteFileW(wpath); + SDL_free(wpath); + if (!rc) { + return WIN_SetError("Couldn't remove path"); + } + return true; +} + +bool SDL_SYS_RenamePath(const char *oldpath, const char *newpath) +{ + WCHAR *woldpath = WIN_UTF8ToStringW(oldpath); + if (!woldpath) { + return false; + } + + WCHAR *wnewpath = WIN_UTF8ToStringW(newpath); + if (!wnewpath) { + SDL_free(woldpath); + return false; + } + + const BOOL rc = MoveFileExW(woldpath, wnewpath, MOVEFILE_REPLACE_EXISTING); + SDL_free(wnewpath); + SDL_free(woldpath); + if (!rc) { + return WIN_SetError("Couldn't rename path"); + } + return true; +} + +bool SDL_SYS_CopyFile(const char *oldpath, const char *newpath) +{ + WCHAR *woldpath = WIN_UTF8ToStringW(oldpath); + if (!woldpath) { + return false; + } + + WCHAR *wnewpath = WIN_UTF8ToStringW(newpath); + if (!wnewpath) { + SDL_free(woldpath); + return false; + } + + const BOOL rc = CopyFileExW(woldpath, wnewpath, NULL, NULL, NULL, COPY_FILE_ALLOW_DECRYPTED_DESTINATION|COPY_FILE_NO_BUFFERING); + SDL_free(wnewpath); + SDL_free(woldpath); + if (!rc) { + return WIN_SetError("Couldn't copy path"); + } + return true; +} + +bool SDL_SYS_CreateDirectory(const char *path) +{ + WCHAR *wpath = WIN_UTF8ToStringW(path); + if (!wpath) { + return false; + } + + DWORD rc = CreateDirectoryW(wpath, NULL); + if (!rc && (GetLastError() == ERROR_ALREADY_EXISTS)) { + WIN32_FILE_ATTRIBUTE_DATA winstat; + if (GetFileAttributesExW(wpath, GetFileExInfoStandard, &winstat)) { + if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + rc = 1; // exists and is already a directory: cool. + } + } + } + + SDL_free(wpath); + if (!rc) { + return WIN_SetError("Couldn't create directory"); + } + return true; +} + +bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) +{ + WCHAR *wpath = WIN_UTF8ToStringW(path); + if (!wpath) { + return false; + } + + WIN32_FILE_ATTRIBUTE_DATA winstat; + const BOOL rc = GetFileAttributesExW(wpath, GetFileExInfoStandard, &winstat); + SDL_free(wpath); + if (!rc) { + return WIN_SetError("Can't stat"); + } + + if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = SDL_PATHTYPE_DIRECTORY; + info->size = 0; + } else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) { + info->type = SDL_PATHTYPE_OTHER; + info->size = ((((Uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow); + } else { + info->type = SDL_PATHTYPE_FILE; + info->size = ((((Uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow); + } + + info->create_time = SDL_TimeFromWindows(winstat.ftCreationTime.dwLowDateTime, winstat.ftCreationTime.dwHighDateTime); + info->modify_time = SDL_TimeFromWindows(winstat.ftLastWriteTime.dwLowDateTime, winstat.ftLastWriteTime.dwHighDateTime); + info->access_time = SDL_TimeFromWindows(winstat.ftLastAccessTime.dwLowDateTime, winstat.ftLastAccessTime.dwHighDateTime); + + return true; +} + +#endif // SDL_FSOPS_WINDOWS + -- cgit v1.2.3