diff --git a/Android.mk b/Android.mk
index 2758331744..2b42555596 100644
--- a/Android.mk
+++ b/Android.mk
@@ -50,7 +50,9 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/filesystem/*.c) \
$(wildcard $(LOCAL_PATH)/src/filesystem/android/*.c) \
+ $(wildcard $(LOCAL_PATH)/src/filesystem/posix/*.c) \
$(wildcard $(LOCAL_PATH)/src/sensor/*.c) \
$(wildcard $(LOCAL_PATH)/src/sensor/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/render/*.c) \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcbb93fc3d..8017bc2b79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -482,6 +482,7 @@ sdl_glob_sources(
"${SDL3_SOURCE_DIR}/src/dynapi/*.c"
"${SDL3_SOURCE_DIR}/src/events/*.c"
"${SDL3_SOURCE_DIR}/src/file/*.c"
+ "${SDL3_SOURCE_DIR}/src/filesystem/*.c"
"${SDL3_SOURCE_DIR}/src/joystick/*.c"
"${SDL3_SOURCE_DIR}/src/haptic/*.c"
"${SDL3_SOURCE_DIR}/src/hidapi/*.c"
@@ -1288,6 +1289,14 @@ if(ANDROID)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/android/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_FSOPS_POSIX 1) # !!! FIXME: this might need something else for .apk data?
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
+ set(SDL_FSOPS_POSIX 1) # !!! FIXME: this might need something else for .apk data?
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
if(SDL_HAPTIC)
set(SDL_HAPTIC_ANDROID 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/android/*.c")
@@ -1446,6 +1455,14 @@ elseif(EMSCRIPTEN)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
if(SDL_CAMERA)
set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1)
set(HAVE_CAMERA TRUE)
@@ -1762,6 +1779,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
endif()
set(HAVE_SDL_STORAGE 1)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
set(SDL_TIMER_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
set(HAVE_SDL_TIMERS TRUE)
@@ -1976,11 +1997,15 @@ elseif(WINDOWS)
set(SDL_FILESYSTEM_WINDOWS 1)
if(WINDOWS_STORE)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/winrt/*.cpp")
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/SDL_sysfsops.c")
else()
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c")
endif()
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_FSOPS_WINDOWS 1)
+ set(HAVE_SDL_FSOPS TRUE)
+
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
if(NOT WINDOWS_STORE)
@@ -2230,6 +2255,10 @@ elseif(APPLE)
endif()
set(HAVE_SDL_STORAGE 1)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
if(SDL_SENSOR)
if(IOS OR VISIONOS)
set(SDL_SENSOR_COREMOTION 1)
@@ -2422,6 +2451,14 @@ elseif(HAIKU)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/haiku/*.cc")
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
set(SDL_TIMER_HAIKU 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/haiku/*.c")
set(HAVE_SDL_TIMERS TRUE)
@@ -2454,6 +2491,14 @@ elseif(RISCOS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/riscos/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
set(SDL_TIMER_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
set(HAVE_SDL_TIMERS TRUE)
@@ -2491,6 +2536,10 @@ elseif(VITA)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/vita/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ # !!! FIXME: do we need a FSops implementation for this?
+
+ # !!! FIXME: do we need a FSops implementation for this?
+
if(SDL_JOYSTICK)
set(SDL_JOYSTICK_VITA 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/vita/*.c")
@@ -2625,6 +2674,10 @@ elseif(PSP)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/psp/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ # !!! FIXME: do we need a FSops implementation for this?
+
+ # !!! FIXME: do we need a FSops implementation for this?
+
if(SDL_JOYSTICK)
set(SDL_JOYSTICK_PSP 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/psp/*.c")
@@ -2688,6 +2741,10 @@ elseif(PS2)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/ps2/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ # !!! FIXME: do we need a FSops implementation for this?
+
+ # !!! FIXME: do we need a FSops implementation for this?
+
if(SDL_JOYSTICK)
set(SDL_JOYSTICK_PS2 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/ps2/*.c")
@@ -2739,6 +2796,10 @@ elseif(N3DS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/n3ds/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
+ # !!! FIXME: do we need a FSops implementation for this?
+
+ # !!! FIXME: do we need a FSops implementation for this?
+
if(SDL_JOYSTICK)
set(SDL_JOYSTICK_N3DS 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/n3ds/*.c")
@@ -2763,6 +2824,10 @@ elseif(N3DS)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/n3ds/*.c")
set(HAVE_SDL_TIMERS TRUE)
+ set(SDL_FSOPS_POSIX 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c")
+ set(HAVE_SDL_FSOPS TRUE)
+
if(SDL_SENSOR)
set(SDL_SENSOR_N3DS 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/n3ds/*.c")
@@ -2850,6 +2915,14 @@ if(NOT HAVE_SDL_STORAGE)
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
endif()
+if(NOT HAVE_SDL_FSOPS)
+ set(SDL_FSOPS_DUMMY 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_sysfsops.c")
+endif()
+if(NOT HAVE_SDL_FSOPS)
+ set(SDL_FSOPS_DUMMY 1)
+ sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/SDL_sysfsops.c")
+endif()
if(NOT HAVE_SDL_LOCALE)
set(SDL_LOCALE_DUMMY 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index e50832cb1a..948e75578a 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -422,6 +422,7 @@
+
@@ -504,6 +505,8 @@
+
+
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 49c00513c5..bc62d7a0ba 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -4,6 +4,12 @@
+
+ filesystem
+
+
+ filesystem\windows
+
@@ -313,6 +319,9 @@
+
+ filesystem
+
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index 79d7a60978..4948abf9b5 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -121,6 +121,7 @@
+
@@ -334,6 +335,8 @@
$(IntDir)$(TargetName)_cpp.pch
+
+
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index c143ae9e9f..ca56e1757e 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -19,6 +19,12 @@
{000031d805439b865ff4550d2f620000}
+
+ {00004389761f0ae646deb5a3d65f0000}
+
+
+ {0000bc587ef6c558d75ce2e620cb0000}
+
@@ -180,6 +186,9 @@
camera
+
+ filesystem
+
Header Files
@@ -579,6 +588,12 @@
Source Files
+
+ filesystem
+
+
+ filesystem\windows
+
Source Files
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 504e3f9c00..24c397472c 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -344,6 +344,7 @@
+
@@ -401,6 +402,8 @@
+
+
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index c6568f85ff..5d3e43001a 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -417,6 +417,9 @@
camera
+
+ filesystem
+
main
@@ -871,6 +874,12 @@
camera
+
+ filesystem
+
+
+ filesystem\windows
+
main\generic
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index b326435071..df7d4548dd 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -503,6 +503,9 @@
F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; };
F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; };
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); };
+ 000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 00002B010DB1A70931C20000 /* SDL_filesystem.c */; };
+ 00000D60346481EEC8FB0000 /* SDL_filesystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */; };
+ 0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1034,6 +1037,9 @@
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = ""; };
F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
+ 00002B010DB1A70931C20000 /* SDL_filesystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_filesystem.c; path = SDL_filesystem.c; sourceTree = ""; };
+ 0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_filesystem.h; path = SDL_filesystem.h; sourceTree = ""; };
+ 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_sysfsops.c; path = SDL_sysfsops.c; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1858,6 +1864,9 @@
children = (
A7D8A7FD23E2513F00DCD162 /* cocoa */,
A7D8A7F723E2513F00DCD162 /* dummy */,
+ 00002B010DB1A70931C20000 /* SDL_filesystem.c */,
+ 0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */,
+ 000050A2BB34616138570000 /* posix */,
);
path = filesystem;
sourceTree = "";
@@ -2209,6 +2218,14 @@
path = resources;
sourceTree = "";
};
+ 000050A2BB34616138570000 /* posix */ = {
+ isa = PBXGroup;
+ children = (
+ 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */,
+ );
+ path = posix;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -2792,6 +2809,8 @@
000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */,
00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */,
00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */,
+ 000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */,
+ 0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index 8030c04354..75e71bf973 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -236,6 +236,102 @@ typedef enum
*/
extern DECLSPEC char *SDLCALL SDL_GetUserFolder(SDL_Folder folder);
+
+/* Abstract filesystem interface */
+
+typedef enum SDL_PathType
+{
+ SDL_PATHTYPE_FILE, /**< a normal file */
+ SDL_PATHTYPE_DIRECTORY, /**< a directory */
+ SDL_PATHTYPE_OTHER /**< something completely different like a device node (not a symlink, those are always followed) */
+} SDL_PathType;
+
+/* SDL file timestamps are 64-bit integers representing seconds since the Unix epoch (Jan 1, 1970) */
+typedef Sint64 SDL_FileTimestamp;
+
+typedef struct SDL_PathInfo
+{
+ SDL_PathType type; /* the path type */
+ Uint64 size; /* the file size in bytes */
+ SDL_FileTimestamp create_time; /* the time when the path was created */
+ SDL_FileTimestamp modify_time; /* the last time the path was modified */
+ SDL_FileTimestamp access_time; /* the last time the path was read */
+} SDL_PathInfo;
+
+/**
+ * Create a directory.
+ *
+ * \param path the path of the directory to create
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_CreateDirectory(const char *path);
+
+/* Callback for filesystem enumeration. Return 1 to keep enumerating, 0 to stop enumerating (no error), -1 to stop enumerating and report an error. "origdir" is the directory being enumerated, "fname" is the enumerated entry. */
+typedef int (SDLCALL *SDL_EnumerateDirectoryCallback)(void *userdata, void *reserved, const char *origdir, const char *fname);
+
+/**
+ * Enumerate a directory.
+ *
+ * \param path the path of the directory to enumerate
+ * \param callback a function that is called for each entry in the directory
+ * \param userdata a pointer that is passed to `callback`
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata);
+
+/**
+ * Remove a file or an empty directory.
+ *
+ * \param path the path of the directory to enumerate
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_RemovePath(const char *path);
+
+/**
+ * Rename a file or directory.
+ *
+ * \param oldpath the old path
+ * \param newpath the new path
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_RenamePath(const char *oldpath, const char *newpath);
+
+/**
+ * Get information about a filesystem path.
+ *
+ * \param path the path to query
+ * \param info a pointer filled in with information about the path
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo *info);
+
+/* some helper functions ... */
+
+/* Converts an SDL file timestamp into a Windows FILETIME (100-nanosecond intervals since January 1, 1601). Fills in the two 32-bit values of the FILETIME structure.
+ *
+ * \param ftime the time to convert
+ * \param low a pointer filled in with the low portion of the Windows FILETIME value
+ * \param high a pointer filled in with the high portion of the Windows FILETIME value
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC void SDLCALL SDL_FileTimeToWindows(Sint64 ftime, Uint32 *low, Uint32 *high);
+
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 1e650c491c..c71a0cafb0 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -469,6 +469,11 @@
#cmakedefine SDL_STORAGE_GENERIC @SDL_STORAGE_GENERIC@
#cmakedefine SDL_STORAGE_STEAM @SDL_STORAGE_STEAM@
+/* Enable system FSops support */
+#cmakedefine SDL_FSOPS_POSIX @SDL_FSOPS_POSIX@
+#cmakedefine SDL_FSOPS_WINDOWS @SDL_FSOPS_WINDOWS@
+#cmakedefine SDL_FSOPS_DUMMY @SDL_FSOPS_DUMMY@
+
/* Enable camera subsystem */
#cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@
/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */
diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h
index 64f8076e00..b784afd000 100644
--- a/include/build_config/SDL_build_config_android.h
+++ b/include/build_config/SDL_build_config_android.h
@@ -187,6 +187,7 @@
/* Enable the filesystem driver */
#define SDL_FILESYSTEM_ANDROID 1
+#define SDL_FSOPS_POSIX 1
/* Enable the camera driver */
#define SDL_CAMERA_DRIVER_ANDROID 1
diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h
index 89d5531f30..bf6e919cf1 100644
--- a/include/build_config/SDL_build_config_emscripten.h
+++ b/include/build_config/SDL_build_config_emscripten.h
@@ -206,6 +206,7 @@
/* Enable system filesystem support */
#define SDL_FILESYSTEM_EMSCRIPTEN 1
+#define SDL_FSOPS_POSIX 1
/* Enable the camera driver */
#define SDL_CAMERA_DRIVER_EMSCRIPTEN 1
diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h
index e130cc4673..875f1942aa 100644
--- a/include/build_config/SDL_build_config_ios.h
+++ b/include/build_config/SDL_build_config_ios.h
@@ -207,6 +207,7 @@
/* enable filesystem support */
#define SDL_FILESYSTEM_COCOA 1
+#define SDL_FSOPS_POSIX 1
/* enable camera support */
#ifndef SDL_PLATFORM_TVOS
diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h
index 39ee50d456..c853e58a9b 100644
--- a/include/build_config/SDL_build_config_macos.h
+++ b/include/build_config/SDL_build_config_macos.h
@@ -264,6 +264,7 @@
/* enable filesystem support */
#define SDL_FILESYSTEM_COCOA 1
+#define SDL_FSOPS_POSIX 1
/* enable camera support */
#define SDL_CAMERA_DRIVER_COREMEDIA 1
diff --git a/include/build_config/SDL_build_config_minimal.h b/include/build_config/SDL_build_config_minimal.h
index 06d02557ea..f949f94f63 100644
--- a/include/build_config/SDL_build_config_minimal.h
+++ b/include/build_config/SDL_build_config_minimal.h
@@ -88,6 +88,7 @@ typedef unsigned int uintptr_t;
/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
#define SDL_FILESYSTEM_DUMMY 1
+#define SDL_FSOPS_DUMMY 1
/* Enable the camera driver (src/camera/dummy/\*.c) */
#define SDL_CAMERA_DRIVER_DUMMY 1
diff --git a/include/build_config/SDL_build_config_ngage.h b/include/build_config/SDL_build_config_ngage.h
index 3449627b02..5969437032 100644
--- a/include/build_config/SDL_build_config_ngage.h
+++ b/include/build_config/SDL_build_config_ngage.h
@@ -85,6 +85,7 @@ typedef unsigned long uintptr_t;
/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
#define SDL_FILESYSTEM_DUMMY 1
+#define SDL_FSOPS_DUMMY 1
/* Enable the camera driver (src/camera/dummy/\*.c) */
#define SDL_CAMERA_DRIVER_DUMMY 1
diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h
index 19873dbf17..69852a766d 100644
--- a/include/build_config/SDL_build_config_windows.h
+++ b/include/build_config/SDL_build_config_windows.h
@@ -312,6 +312,7 @@ typedef unsigned int uintptr_t;
/* Enable filesystem support */
#define SDL_FILESYSTEM_WINDOWS 1
+#define SDL_FSOPS_WINDOWS 1
/* Enable the camera driver */
#define SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1
diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h
index 992052a819..f760cf477e 100644
--- a/include/build_config/SDL_build_config_wingdk.h
+++ b/include/build_config/SDL_build_config_wingdk.h
@@ -244,6 +244,7 @@
/* Enable filesystem support */
#define SDL_FILESYSTEM_WINDOWS 1
+#define SDL_FSOPS_WINDOWS 1
/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */
#define SDL_CAMERA_DRIVER_DUMMY 1
diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h
index b5e5725fba..9a7702d870 100644
--- a/include/build_config/SDL_build_config_winrt.h
+++ b/include/build_config/SDL_build_config_winrt.h
@@ -213,6 +213,10 @@
/* Enable system power support */
#define SDL_POWER_WINRT 1
+/* Enable filesystem support */
+#define SDL_FILESYSTEM_WINDOWS 1
+#define SDL_FSOPS_WINDOWS 1
+
/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */
#define SDL_CAMERA_DRIVER_DUMMY 1
diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h
index 8615036734..4822fe6293 100644
--- a/include/build_config/SDL_build_config_xbox.h
+++ b/include/build_config/SDL_build_config_xbox.h
@@ -228,6 +228,8 @@
/* Enable filesystem support */
/* #define SDL_FILESYSTEM_WINDOWS 1*/
#define SDL_FILESYSTEM_XBOX 1
+#define SDL_FSOPS_WINDOWS 1
+
/* Disable IME as not supported yet (TODO: Xbox IME?) */
#define SDL_DISABLE_WINDOWS_IME 1
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 5ab6162200..e233484a0a 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -987,6 +987,12 @@ SDL3_0.0.0 {
SDL_GetStorageFileSize;
SDL_ReadStorageFile;
SDL_GetStorageSpaceRemaining;
+ SDL_CreateDirectory;
+ SDL_EnumerateDirectory;
+ SDL_RemovePath;
+ SDL_RenamePath;
+ SDL_GetPathInfo;
+ SDL_FileTimeToWindows;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 9f55177bea..f12c82ea58 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1012,3 +1012,9 @@
#define SDL_GetStorageFileSize SDL_GetStorageFileSize_REAL
#define SDL_ReadStorageFile SDL_ReadStorageFile_REAL
#define SDL_GetStorageSpaceRemaining SDL_GetStorageSpaceRemaining_REAL
+#define SDL_CreateDirectory SDL_CreateDirectory_REAL
+#define SDL_EnumerateDirectory SDL_EnumerateDirectory_REAL
+#define SDL_RemovePath SDL_RemovePath_REAL
+#define SDL_RenamePath SDL_RenamePath_REAL
+#define SDL_GetPathInfo SDL_GetPathInfo_REAL
+#define SDL_FileTimeToWindows SDL_FileTimeToWindows_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 86263add1f..fa2c50937a 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1037,3 +1037,9 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_StorageReady,(SDL_Storage *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetStorageFileSize,(SDL_Storage *a, const char *b, Uint64 *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_ReadStorageFile,(SDL_Storage *a, const char *b, void *c, Uint64 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(Uint64,SDL_GetStorageSpaceRemaining,(SDL_Storage *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_CreateDirectory,(const char *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_EnumerateDirectory,(const char *a, SDL_EnumerateDirectoryCallback b, void *c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_RemovePath,(const char *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_RenamePath,(const char *a, const char *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_GetPathInfo,(const char *a, SDL_PathInfo *b),(a,b),return)
+SDL_DYNAPI_PROC(void,SDL_FileTimeToWindows,(Sint64 a, Uint32 *b, Uint32 *c),(a,b,c),)
diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c
new file mode 100644
index 0000000000..a06d19ac7c
--- /dev/null
+++ b/src/filesystem/SDL_filesystem.c
@@ -0,0 +1,91 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 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_sysfilesystem.h"
+
+void SDL_FileTimeToWindows(Sint64 ftime, Uint32 *low, Uint32 *high)
+{
+ const Sint64 delta_1601_epoch_s = 11644473600ull; // [seconds] (seconds between 1/1/1601 and 1/1/1970, 11644473600 seconds)
+
+ Sint64 cvt = (ftime + delta_1601_epoch_s) * (SDL_NS_PER_SECOND / 100ull); // [100ns] (adjust to epoch and convert nanoseconds to 1/100th nanosecond units).
+
+ // Windows FILETIME is unsigned, so if we're trying to show a timestamp from before before the
+ // Windows epoch, (Jan 1, 1601), clamp it to zero so it doesn't go way into the future.
+ if (cvt < 0) {
+ cvt = 0;
+ }
+
+ if (low) {
+ *low = (Uint32) cvt;
+ }
+
+ if (high) {
+ *high = (Uint32) (cvt >> 32);
+ }
+}
+
+int SDL_RemovePath(const char *path)
+{
+ if (!path) {
+ return SDL_InvalidParamError("path");
+ }
+ return SDL_SYS_FSremove(path);
+}
+
+int SDL_RenamePath(const char *oldpath, const char *newpath)
+{
+ if (!oldpath) {
+ return SDL_InvalidParamError("oldpath");
+ } else if (!newpath) {
+ return SDL_InvalidParamError("newpath");
+ }
+ return SDL_SYS_FSrename(oldpath, newpath);
+}
+
+int SDL_CreateDirectory(const char *path)
+{
+ /* TODO: Recursively create subdirectories */
+ if (!path) {
+ return SDL_InvalidParamError("path");
+ }
+ return SDL_SYS_FSmkdir(path);
+}
+
+int 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_FSenumerate(path, path, callback, userdata);
+}
+
+int SDL_GetPathInfo(const char *path, SDL_PathInfo *stat)
+{
+ if (!path) {
+ return SDL_InvalidParamError("path");
+ } else if (!stat) {
+ return SDL_InvalidParamError("stat");
+ }
+ return SDL_SYS_FSstat(path, stat);
+}
diff --git a/src/filesystem/SDL_sysfilesystem.h b/src/filesystem/SDL_sysfilesystem.h
new file mode 100644
index 0000000000..c8fb4e7a45
--- /dev/null
+++ b/src/filesystem/SDL_sysfilesystem.h
@@ -0,0 +1,32 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 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_
+
+int SDL_SYS_FSenumerate(const char *fullpath, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata);
+int SDL_SYS_FSremove(const char *fullpath);
+int SDL_SYS_FSrename(const char *oldfullpath, const char *newfullpath);
+int SDL_SYS_FSmkdir(const char *fullpath);
+int SDL_SYS_FSstat(const char *fullpath, SDL_PathInfo *info);
+
+#endif
+
diff --git a/src/filesystem/dummy/SDL_sysfsops.c b/src/filesystem/dummy/SDL_sysfsops.c
new file mode 100644
index 0000000000..3e014722db
--- /dev/null
+++ b/src/filesystem/dummy/SDL_sysfsops.c
@@ -0,0 +1,54 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 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)
+
+#include "../SDL_sysfilesystem.h"
+
+int SDL_SYS_FSenumerate(const char *fullpath, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata)
+{
+ return SDL_Unsupported();
+}
+
+int SDL_SYS_FSremove(const char *fullpath)
+{
+ return SDL_Unsupported();
+}
+
+int SDL_SYS_FSrename(const char *oldfullpath, const char *newfullpath)
+{
+ return SDL_Unsupported();
+}
+
+int SDL_SYS_FSmkdir(const char *fullpath)
+{
+ return SDL_Unsupported();
+}
+
+int SDL_SYS_FSstat(const char *fullpath, SDL_PathInfo *st)
+{
+ return SDL_Unsupported();
+}
+
+#endif
+
diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c
new file mode 100644
index 0000000000..69e42d6eef
--- /dev/null
+++ b/src/filesystem/posix/SDL_sysfsops.c
@@ -0,0 +1,138 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 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)
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../SDL_sysfilesystem.h"
+
+int SDL_SYS_FSenumerate(const char *fullpath, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata)
+{
+ int retval = 1;
+
+ DIR *dir = opendir(fullpath);
+ if (!dir) {
+ return SDL_SetError("Can't open directory: %s", strerror(errno));
+ }
+
+ struct dirent *ent;
+ while ((retval == 1) && ((ent = readdir(dir)) != NULL))
+ {
+ const char *name = ent->d_name;
+ if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
+ continue;
+ }
+ retval = cb(userdata, NULL, dirname, name);
+ }
+
+ closedir(dir);
+
+ return retval;
+}
+
+int SDL_SYS_FSremove(const char *fullpath)
+{
+ int rc = remove(fullpath);
+ if (rc < 0) {
+ const int origerrno = errno;
+ if (origerrno == ENOENT) {
+ char *parent = SDL_strdup(fullpath);
+ if (!parent) {
+ return -1;
+ }
+
+ char *ptr = SDL_strrchr(parent, '/');
+ if (ptr) {
+ *ptr = '\0'; // chop off thing we were removing, see if parent is there.
+ }
+
+ struct stat statbuf;
+ rc = stat(ptr ? parent : ".", &statbuf);
+ SDL_free(parent);
+ if (rc == 0) {
+ return 0; // it's already gone, and parent exists, consider it success.
+ }
+ }
+ return SDL_SetError("Can't remove path: %s", strerror(origerrno));
+ }
+ return 0;
+}
+
+int SDL_SYS_FSrename(const char *oldfullpath, const char *newfullpath)
+{
+ if (rename(oldfullpath, newfullpath) < 0) {
+ return SDL_SetError("Can't remove path: %s", strerror(errno));
+ }
+ return 0;
+}
+
+int SDL_SYS_FSmkdir(const char *fullpath)
+{
+ const int rc = mkdir(fullpath, 0770);
+ if (rc < 0) {
+ const int origerrno = errno;
+ if (origerrno == EEXIST) {
+ struct stat statbuf;
+ if ((stat(fullpath, &statbuf) == 0) && (S_ISDIR(statbuf.st_mode))) {
+ return 0; // it already exists and it's a directory, consider it success.
+ }
+ }
+ return SDL_SetError("Can't create directory: %s", strerror(origerrno));
+ }
+ return 0;
+}
+
+int SDL_SYS_FSstat(const char *fullpath, SDL_PathInfo *info)
+{
+ struct stat statbuf;
+ const int rc = stat(fullpath, &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;
+ }
+
+ // SDL file time is seconds since the Unix epoch, so we're already good here.
+ // Note that this will fail on machines with 32-bit time_t in 2038, but that's not
+ // an SDL bug; those machines need to be fixed or everything will fail in the same way.
+ info->create_time = (Sint64) statbuf.st_ctime;
+ info->modify_time = (Sint64) statbuf.st_mtime;
+ info->access_time = (Sint64) statbuf.st_atime;
+
+ return 0;
+}
+
+#endif
+
diff --git a/src/filesystem/windows/SDL_sysfsops.c b/src/filesystem/windows/SDL_sysfsops.c
new file mode 100644
index 0000000000..571c83eb13
--- /dev/null
+++ b/src/filesystem/windows/SDL_sysfsops.c
@@ -0,0 +1,188 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 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)
+
+#include "../../core/windows/SDL_windows.h"
+#include "../SDL_sysfilesystem.h"
+
+int SDL_SYS_FSenumerate(const char *fullpath, const char *dirname, SDL_EnumerateDirectoryCallback cb, void *userdata)
+{
+ int retval = 1;
+ if (*fullpath == '\0') { // if empty (completely at the root), we need to enumerate drive letters.
+ const DWORD drives = GetLogicalDrives();
+ char name[3] = { 0, ':', '\0' };
+ for (int i = 'A'; (retval == 1) && (i <= 'Z'); i++) {
+ if (drives & (1 << (i - 'A'))) {
+ name[0] = (char) i;
+ retval = cb(userdata, NULL, dirname, name);
+ }
+ }
+ } else {
+ const size_t patternlen = SDL_strlen(fullpath) + 3;
+ char *pattern = (char *) SDL_malloc(patternlen);
+ if (!pattern) {
+ return -1;
+ }
+
+ // 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.
+ SDL_snprintf(pattern, patternlen, "%s\\*", fullpath);
+
+ WCHAR *wpattern = WIN_UTF8ToString(pattern);
+ SDL_free(pattern);
+ if (!wpattern) {
+ return -1;
+ }
+
+ WIN32_FIND_DATAW entw;
+ HANDLE dir = FindFirstFileExW(wpattern, FindExInfoStandard, &entw, FindExSearchNameMatch, NULL, 0);
+ SDL_free(wpattern);
+ if (dir == INVALID_HANDLE_VALUE) {
+ 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_StringToUTF8(fn);
+ if (!utf8fn) {
+ retval = -1;
+ } else {
+ retval = cb(userdata, NULL, dirname, utf8fn);
+ SDL_free(utf8fn);
+ }
+ } while ((retval == 1) && (FindNextFileW(dir, &entw) != 0));
+
+ FindClose(dir);
+ }
+
+ return retval;
+}
+
+int SDL_SYS_FSremove(const char *fullpath)
+{
+ WCHAR *wpath = WIN_UTF8ToString(fullpath);
+ if (!wpath) {
+ return -1;
+ }
+
+ WIN32_FILE_ATTRIBUTE_DATA info;
+ if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &info)) {
+ 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 0; // 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);
+ return !rc ? WIN_SetError("Couldn't remove path") : 0;
+}
+
+int SDL_SYS_FSrename(const char *oldfullpath, const char *newfullpath)
+{
+ WCHAR *woldpath = WIN_UTF8ToString(oldfullpath);
+ if (!woldpath) {
+ return -1;
+ }
+
+ WCHAR *wnewpath = WIN_UTF8ToString(newfullpath);
+ if (!wnewpath) {
+ SDL_free(woldpath);
+ return -1;
+ }
+
+ const BOOL rc = MoveFileExW(woldpath, wnewpath, MOVEFILE_REPLACE_EXISTING);
+ SDL_free(wnewpath);
+ SDL_free(woldpath);
+ return !rc ? WIN_SetError("Couldn't rename path") : 0;
+}
+
+int SDL_SYS_FSmkdir(const char *fullpath)
+{
+ WCHAR *wpath = WIN_UTF8ToString(fullpath);
+ if (!wpath) {
+ return -1;
+ }
+
+ const DWORD rc = CreateDirectoryW(wpath, NULL);
+ SDL_free(wpath);
+ return !rc ? WIN_SetError("Couldn't create directory") : 0;
+}
+
+static Sint64 FileTimeToSDLTime(const FILETIME *ft)
+{
+ const Uint64 delta_1601_epoch_100ns = 11644473600ull * 10000000ull; // [100ns] (100-ns chunks between 1/1/1601 and 1/1/1970, 11644473600 seconds * 10000000)
+ ULARGE_INTEGER large;
+ large.LowPart = ft->dwLowDateTime;
+ large.HighPart = ft->dwHighDateTime;
+ if (large.QuadPart == 0) {
+ return 0; // unsupported on this filesystem...0 is fine, I guess.
+ }
+ return (Sint64) ((((Uint64)large.QuadPart) - delta_1601_epoch_100ns) / (SDL_NS_PER_SECOND / 100ull)); // [secs] (adjust to epoch and convert 1/100th nanosecond units to seconds).
+}
+
+int SDL_SYS_FSstat(const char *fullpath, SDL_PathInfo *info)
+{
+ WCHAR *wpath = WIN_UTF8ToString(fullpath);
+ if (!wpath) {
+ return -1;
+ }
+
+ 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 = FileTimeToSDLTime(&winstat.ftCreationTime);
+ info->modify_time = FileTimeToSDLTime(&winstat.ftLastWriteTime);
+ info->access_time = FileTimeToSDLTime(&winstat.ftLastAccessTime);
+
+ return 1;
+}
+
+#endif
+
diff --git a/test/testfilesystem.c b/test/testfilesystem.c
index e1986698b0..35f99c8674 100644
--- a/test/testfilesystem.c
+++ b/test/testfilesystem.c
@@ -15,11 +15,54 @@
#include
#include
+static int SDLCALL enum_callback(void *userdata, void *reserved, const char *origdir, const char *fname)
+{
+ SDL_PathInfo info;
+ char *fullpath = NULL;
+
+ /* you can use '/' for a path separator on Windows, but to make the log output look correct, we'll #ifdef this... */
+ #ifdef SDL_PLATFORM_WINDOWS
+ const char *pathsep = "\\";
+ #else
+ const char *pathsep = "/";
+ #endif
+
+ if (SDL_asprintf(&fullpath, "%s%s%s", origdir, *origdir ? pathsep : "", fname) < 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
+ return -1;
+ }
+
+ if (SDL_GetPathInfo(fullpath, &info) < 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't stat '%s': %s", fullpath, SDL_GetError());
+ } else {
+ const char *type;
+ if (info.type == SDL_PATHTYPE_FILE) {
+ type = "FILE";
+ } else if (info.type == SDL_PATHTYPE_DIRECTORY) {
+ type = "DIRECTORY";
+ } else {
+ type = "OTHER";
+ }
+ SDL_Log("%s (type=%s, size=%" SDL_PRIu64 ", create=%" SDL_PRIu64 ", mod=%" SDL_PRIu64 ", access=%" SDL_PRIu64 ")",
+ fullpath, type, info.size, info.modify_time, info.create_time, info.access_time);
+
+ if (info.type == SDL_PATHTYPE_DIRECTORY) {
+ if (SDL_EnumerateDirectory(fullpath, enum_callback, userdata) < 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Enumeration failed!");
+ }
+ }
+ }
+
+ SDL_free(fullpath);
+ return 1; /* keep going */
+}
+
+
int main(int argc, char *argv[])
{
SDLTest_CommonState *state;
- char *base_path;
char *pref_path;
+ char *base_path;
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
@@ -46,7 +89,6 @@ int main(int argc, char *argv[])
SDL_GetError());
} else {
SDL_Log("base path: '%s'\n", base_path);
- SDL_free(base_path);
}
pref_path = SDL_GetPrefPath("libsdl", "test_filesystem");
@@ -67,6 +109,31 @@ int main(int argc, char *argv[])
SDL_free(pref_path);
}
+ if (base_path) {
+ if (SDL_EnumerateDirectory(base_path, enum_callback, NULL) < 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Base path enumeration failed!");
+ }
+
+ /* !!! FIXME: put this in a subroutine and make it test more thoroughly (and put it in testautomation). */
+ if (SDL_CreateDirectory("testfilesystem-test") == -1) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateDirectory('testfilesystem-test') failed: %s", SDL_GetError());
+ } else if (SDL_CreateDirectory("testfilesystem-test/1") == -1) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateDirectory('testfilesystem-test/1') failed: %s", SDL_GetError());
+ } else if (SDL_CreateDirectory("testfilesystem-test/1") == -1) { /* THIS SHOULD NOT FAIL! Making a directory that already exists should succeed here. */
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateDirectory('testfilesystem-test/1') failed: %s", SDL_GetError());
+ } else if (SDL_RenamePath("testfilesystem-test/1", "testfilesystem-test/2") == -1) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_RenamePath('testfilesystem-test/1', 'testfilesystem-test/2') failed: %s", SDL_GetError());
+ } else if (SDL_RemovePath("testfilesystem-test/2") == -1) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_RemovePath('testfilesystem-test/2') failed: %s", SDL_GetError());
+ } else if (SDL_RemovePath("testfilesystem-test") == -1) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_RemovePath('testfilesystem-test') failed: %s", SDL_GetError());
+ } else if (SDL_RemovePath("testfilesystem-test") == -1) { /* THIS SHOULD NOT FAIL! Removing a directory that is already gone should succeed here. */
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_RemovePath('testfilesystem-test') failed: %s", SDL_GetError());
+ }
+ }
+
+ SDL_free(base_path);
+
SDL_Quit();
SDLTest_CommonDestroyState(state);
return 0;