chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1,530 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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;
}
}
}

View file

@ -0,0 +1,29 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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

View file

@ -0,0 +1,43 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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

View file

@ -0,0 +1,62 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <unistd.h>
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

View file

@ -0,0 +1,240 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <Foundation/Foundation.h>
#include <sys/stat.h>
#include <sys/types.h>
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

View file

@ -0,0 +1,54 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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

View file

@ -0,0 +1,62 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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

View file

@ -0,0 +1,116 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <errno.h>
#include <sys/stat.h>
#include <emscripten/emscripten.h>
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

View file

@ -0,0 +1,150 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <SDL3/SDL_hints.h>
#include <SDL3/SDL_system.h>
#include <SDL3/SDL_filesystem.h>
#include <XGameSaveFiles.h>
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;
}

View file

@ -0,0 +1,156 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <kernel/image.h>
#include <storage/Directory.h>
#include <storage/Entry.h>
#include <storage/FindDirectory.h>
#include <storage/Path.h>
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

View file

@ -0,0 +1,90 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <dirent.h>
#include <errno.h>
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

View file

@ -0,0 +1,246 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
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

View file

@ -0,0 +1,119 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <sys/stat.h>
#include <unistd.h>
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

View file

@ -0,0 +1,89 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <sys/stat.h>
#include <unistd.h>
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

View file

@ -0,0 +1,208 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <kernel.h>
#include <swis.h>
#include <unixlib/local.h>
// 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, &regs, &regs);
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, &regs, &regs);
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, &regs, &regs);
*ptr = '.';
if (error) {
return error;
}
}
}
return _kernel_swi(OS_File, &regs, &regs);
}
char *SDL_SYS_GetBasePath(void)
{
_kernel_swi_regs regs;
_kernel_oserror *error;
char *canon, *ptr, *result;
error = _kernel_swi(OS_GetEnv, &regs, &regs);
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("<Choices$Write>", "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

View file

@ -0,0 +1,618 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD)
#include <sys/sysctl.h>
#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

View file

@ -0,0 +1,92 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <psp2/io/stat.h>
#include <sys/types.h>
#include <limits.h>
#include <fcntl.h>
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

View file

@ -0,0 +1,382 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <shlobj.h>
#include <initguid.h>
// 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

View file

@ -0,0 +1,231 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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