2
0
Эх сурвалжийг харах

Added portable file and directory operations (thanks @icculus!)

Sam Lantinga 1 жил өмнө
parent
commit
db0c1d7aeb

+ 2 - 0
Android.mk

@@ -50,7 +50,9 @@ LOCAL_SRC_FILES := \
 	$(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/android/*.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/android/*.c) \
+	$(wildcard $(LOCAL_PATH)/src/filesystem/posix/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/sensor/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/sensor/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/sensor/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/sensor/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/render/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/render/*.c) \

+ 73 - 0
CMakeLists.txt

@@ -482,6 +482,7 @@ sdl_glob_sources(
   "${SDL3_SOURCE_DIR}/src/dynapi/*.c"
   "${SDL3_SOURCE_DIR}/src/dynapi/*.c"
   "${SDL3_SOURCE_DIR}/src/events/*.c"
   "${SDL3_SOURCE_DIR}/src/events/*.c"
   "${SDL3_SOURCE_DIR}/src/file/*.c"
   "${SDL3_SOURCE_DIR}/src/file/*.c"
+  "${SDL3_SOURCE_DIR}/src/filesystem/*.c"
   "${SDL3_SOURCE_DIR}/src/joystick/*.c"
   "${SDL3_SOURCE_DIR}/src/joystick/*.c"
   "${SDL3_SOURCE_DIR}/src/haptic/*.c"
   "${SDL3_SOURCE_DIR}/src/haptic/*.c"
   "${SDL3_SOURCE_DIR}/src/hidapi/*.c"
   "${SDL3_SOURCE_DIR}/src/hidapi/*.c"
@@ -1288,6 +1289,14 @@ if(ANDROID)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/android/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/android/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_HAPTIC)
     set(SDL_HAPTIC_ANDROID 1)
     set(SDL_HAPTIC_ANDROID 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/haptic/android/*.c")
     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")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_CAMERA)
     set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1)
     set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1)
     set(HAVE_CAMERA TRUE)
     set(HAVE_CAMERA TRUE)
@@ -1762,6 +1779,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
   endif()
   endif()
   set(HAVE_SDL_STORAGE 1)
   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)
   set(SDL_TIMER_UNIX 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
   set(HAVE_SDL_TIMERS TRUE)
   set(HAVE_SDL_TIMERS TRUE)
@@ -1976,11 +1997,15 @@ elseif(WINDOWS)
   set(SDL_FILESYSTEM_WINDOWS 1)
   set(SDL_FILESYSTEM_WINDOWS 1)
   if(WINDOWS_STORE)
   if(WINDOWS_STORE)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/winrt/*.cpp")
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/winrt/*.cpp")
+    sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/SDL_sysfsops.c")
   else()
   else()
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c")
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/windows/*.c")
   endif()
   endif()
   set(HAVE_SDL_FILESYSTEM TRUE)
   set(HAVE_SDL_FILESYSTEM TRUE)
 
 
+  set(SDL_FSOPS_WINDOWS 1)
+  set(HAVE_SDL_FSOPS TRUE)
+
   set(SDL_STORAGE_GENERIC 1)
   set(SDL_STORAGE_GENERIC 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
   if(NOT WINDOWS_STORE)
   if(NOT WINDOWS_STORE)
@@ -2230,6 +2255,10 @@ elseif(APPLE)
   endif()
   endif()
   set(HAVE_SDL_STORAGE 1)
   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(SDL_SENSOR)
     if(IOS OR VISIONOS)
     if(IOS OR VISIONOS)
       set(SDL_SENSOR_COREMOTION 1)
       set(SDL_SENSOR_COREMOTION 1)
@@ -2422,6 +2451,14 @@ elseif(HAIKU)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/haiku/*.cc")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/haiku/*.cc")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   set(SDL_TIMER_HAIKU 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/haiku/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/haiku/*.c")
   set(HAVE_SDL_TIMERS TRUE)
   set(HAVE_SDL_TIMERS TRUE)
@@ -2454,6 +2491,14 @@ elseif(RISCOS)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/riscos/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/riscos/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   set(SDL_TIMER_UNIX 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
   set(HAVE_SDL_TIMERS TRUE)
   set(HAVE_SDL_TIMERS TRUE)
@@ -2491,6 +2536,10 @@ elseif(VITA)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/vita/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/vita/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_JOYSTICK)
     set(SDL_JOYSTICK_VITA 1)
     set(SDL_JOYSTICK_VITA 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/vita/*.c")
     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")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/psp/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_JOYSTICK)
     set(SDL_JOYSTICK_PSP 1)
     set(SDL_JOYSTICK_PSP 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/psp/*.c")
     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")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/ps2/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_JOYSTICK)
     set(SDL_JOYSTICK_PS2 1)
     set(SDL_JOYSTICK_PS2 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/ps2/*.c")
     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")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/n3ds/*.c")
   set(HAVE_SDL_FILESYSTEM TRUE)
   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)
   if(SDL_JOYSTICK)
     set(SDL_JOYSTICK_N3DS 1)
     set(SDL_JOYSTICK_N3DS 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/n3ds/*.c")
     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")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/n3ds/*.c")
   set(HAVE_SDL_TIMERS TRUE)
   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)
   if(SDL_SENSOR)
     set(SDL_SENSOR_N3DS 1)
     set(SDL_SENSOR_N3DS 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/n3ds/*.c")
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/n3ds/*.c")
@@ -2850,6 +2915,14 @@ if(NOT HAVE_SDL_STORAGE)
   set(SDL_STORAGE_GENERIC 1)
   set(SDL_STORAGE_GENERIC 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
 endif()
 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)
 if(NOT HAVE_SDL_LOCALE)
   set(SDL_LOCALE_DUMMY 1)
   set(SDL_LOCALE_DUMMY 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")

+ 3 - 0
VisualC-GDK/SDL/SDL.vcxproj

@@ -422,6 +422,7 @@
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
+    <ClInclude Include="..\..\src\filesystem\SDL_filesystem.h" />
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
@@ -504,6 +505,8 @@
     </ClCompile>
     </ClCompile>
     <ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
     <ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
     <ClCompile Include="..\..\src\camera\SDL_camera.c" />
     <ClCompile Include="..\..\src\camera\SDL_camera.c" />
+    <ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
+    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c" />
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
     <ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
     <ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
     <ClCompile Include="..\..\src\SDL_guid.c" />
     <ClCompile Include="..\..\src\SDL_guid.c" />

+ 9 - 0
VisualC-GDK/SDL/SDL.vcxproj.filters

@@ -4,6 +4,12 @@
     <ClCompile Include="..\..\src\core\gdk\SDL_gdk.cpp" />
     <ClCompile Include="..\..\src\core\gdk\SDL_gdk.cpp" />
     <ClCompile Include="..\..\src\core\windows\pch.c" />
     <ClCompile Include="..\..\src\core\windows\pch.c" />
     <ClCompile Include="..\..\src\core\windows\pch_cpp.cpp" />
     <ClCompile Include="..\..\src\core\windows\pch_cpp.cpp" />
+    <ClCompile Include="..\..\src\filesystem\SDL_filesystem.c">
+      <Filter>filesystem</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c">
+      <Filter>filesystem\windows</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12_xbox.cpp" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12_xbox.cpp" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxone.cpp" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxone.cpp" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxseries.cpp" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12_xboxseries.cpp" />
@@ -313,6 +319,9 @@
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
+    <ClInclude Include="..\..\src\filesystem\SDL_filesystem.h">
+      <Filter>filesystem</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />

+ 3 - 0
VisualC-WinRT/SDL-UWP.vcxproj

@@ -121,6 +121,7 @@
     <ClInclude Include="..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\src\events\SDL_windowevents_c.h" />
     <ClInclude Include="..\src\events\SDL_windowevents_c.h" />
+    <ClInclude Include="..\src\filesystem\SDL_filesystem.h" />
     <ClInclude Include="..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\src\haptic\windows\SDL_dinputhaptic_c.h" />
     <ClInclude Include="..\src\haptic\windows\SDL_dinputhaptic_c.h" />
@@ -334,6 +335,8 @@
       <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
       <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
     </ClCompile>
     </ClCompile>
     <ClCompile Include="..\src\file\SDL_iostream.c" />
     <ClCompile Include="..\src\file\SDL_iostream.c" />
+    <ClCompile Include="..\src\filesystem\SDL_filesystem.c" />
+    <ClCompile Include="..\src\filesystem\windows\SDL_sysfsops.c" />
     <ClCompile Include="..\src\haptic\dummy\SDL_syshaptic.c" />
     <ClCompile Include="..\src\haptic\dummy\SDL_syshaptic.c" />
     <ClCompile Include="..\src\haptic\SDL_haptic.c" />
     <ClCompile Include="..\src\haptic\SDL_haptic.c" />
     <ClCompile Include="..\src\haptic\windows\SDL_dinputhaptic.c" />
     <ClCompile Include="..\src\haptic\windows\SDL_dinputhaptic.c" />

+ 15 - 0
VisualC-WinRT/SDL-UWP.vcxproj.filters

@@ -19,6 +19,12 @@
     <Filter Include="camera\dummy">
     <Filter Include="camera\dummy">
       <UniqueIdentifier>{000031d805439b865ff4550d2f620000}</UniqueIdentifier>
       <UniqueIdentifier>{000031d805439b865ff4550d2f620000}</UniqueIdentifier>
     </Filter>
     </Filter>
+    <Filter Include="filesystem">
+      <UniqueIdentifier>{00004389761f0ae646deb5a3d65f0000}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="filesystem\windows">
+      <UniqueIdentifier>{0000bc587ef6c558d75ce2e620cb0000}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\include\SDL3\SDL_begin_code.h">
     <ClInclude Include="..\include\SDL3\SDL_begin_code.h">
@@ -180,6 +186,9 @@
     <ClInclude Include="..\src\camera\SDL_syscamera.h">
     <ClInclude Include="..\src\camera\SDL_syscamera.h">
       <Filter>camera</Filter>
       <Filter>camera</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\src\filesystem\SDL_filesystem.h">
+      <Filter>filesystem</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\joystick\SDL_gamepad_c.h">
     <ClInclude Include="..\src\joystick\SDL_gamepad_c.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
@@ -579,6 +588,12 @@
     <ClCompile Include="..\src\events\SDL_windowevents.c">
     <ClCompile Include="..\src\events\SDL_windowevents.c">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\src\filesystem\SDL_filesystem.c">
+      <Filter>filesystem</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\filesystem\windows\SDL_sysfsops.c">
+      <Filter>filesystem\windows</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\filesystem\winrt\SDL_sysfilesystem.cpp">
     <ClCompile Include="..\src\filesystem\winrt\SDL_sysfilesystem.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>

+ 3 - 0
VisualC/SDL/SDL.vcxproj

@@ -344,6 +344,7 @@
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
+    <ClInclude Include="..\..\src\filesystem\SDL_filesystem.h" />
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_haptic_c.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
@@ -401,6 +402,8 @@
     <ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
     <ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
     <ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
     <ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
     <ClCompile Include="..\..\src\camera\SDL_camera.c" />
     <ClCompile Include="..\..\src\camera\SDL_camera.c" />
+    <ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
+    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c" />
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
     <ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
     <ClCompile Include="..\..\src\main\SDL_main_callbacks.c" />
     <ClCompile Include="..\..\src\render\vulkan\SDL_render_vulkan.c" />
     <ClCompile Include="..\..\src\render\vulkan\SDL_render_vulkan.c" />

+ 9 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -417,6 +417,9 @@
     <ClInclude Include="..\..\src\camera\SDL_syscamera.h">
     <ClInclude Include="..\..\src\camera\SDL_syscamera.h">
       <Filter>camera</Filter>
       <Filter>camera</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\src\filesystem\SDL_filesystem.h">
+      <Filter>filesystem</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\main\SDL_main_callbacks.h">
     <ClInclude Include="..\..\src\main\SDL_main_callbacks.h">
       <Filter>main</Filter>
       <Filter>main</Filter>
     </ClInclude>
     </ClInclude>
@@ -871,6 +874,12 @@
     <ClCompile Include="..\..\src\camera\SDL_camera.c">
     <ClCompile Include="..\..\src\camera\SDL_camera.c">
       <Filter>camera</Filter>
       <Filter>camera</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\src\filesystem\SDL_filesystem.c">
+      <Filter>filesystem</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c">
+      <Filter>filesystem\windows</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c">
     <ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c">
       <Filter>main\generic</Filter>
       <Filter>main\generic</Filter>
     </ClCompile>
     </ClCompile>

+ 19 - 0
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 */; };
 		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 */; };
 		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, ); };
 		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 */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXContainerItemProxy section */
@@ -1034,6 +1037,9 @@
 		F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
 		F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
 		F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
 		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; };
 		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 = "<group>"; };
+		0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_filesystem.h; path = SDL_filesystem.h; sourceTree = "<group>"; };
+		0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_sysfsops.c; path = SDL_sysfsops.c; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
@@ -1858,6 +1864,9 @@
 			children = (
 			children = (
 				A7D8A7FD23E2513F00DCD162 /* cocoa */,
 				A7D8A7FD23E2513F00DCD162 /* cocoa */,
 				A7D8A7F723E2513F00DCD162 /* dummy */,
 				A7D8A7F723E2513F00DCD162 /* dummy */,
+				00002B010DB1A70931C20000 /* SDL_filesystem.c */,
+				0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */,
+				000050A2BB34616138570000 /* posix */,
 			);
 			);
 			path = filesystem;
 			path = filesystem;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -2209,6 +2218,14 @@
 			path = resources;
 			path = resources;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		000050A2BB34616138570000 /* posix */ = {
+			isa = PBXGroup;
+			children = (
+				0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */,
+			);
+			path = posix;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXHeadersBuildPhase section */
 /* Begin PBXHeadersBuildPhase section */
@@ -2792,6 +2809,8 @@
 				000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */,
 				000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */,
 				00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */,
 				00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */,
 				00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */,
 				00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */,
+				000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */,
+				0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 96 - 0
include/SDL3/SDL_filesystem.h

@@ -236,6 +236,102 @@ typedef enum
  */
  */
 extern DECLSPEC char *SDLCALL SDL_GetUserFolder(SDL_Folder folder);
 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++ */
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 5 - 0
include/build_config/SDL_build_config.h.cmake

@@ -469,6 +469,11 @@
 #cmakedefine SDL_STORAGE_GENERIC @SDL_STORAGE_GENERIC@
 #cmakedefine SDL_STORAGE_GENERIC @SDL_STORAGE_GENERIC@
 #cmakedefine SDL_STORAGE_STEAM @SDL_STORAGE_STEAM@
 #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 */
 /* Enable camera subsystem */
 #cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@
 #cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@
 /* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */
 /* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */

+ 1 - 0
include/build_config/SDL_build_config_android.h

@@ -187,6 +187,7 @@
 
 
 /* Enable the filesystem driver */
 /* Enable the filesystem driver */
 #define SDL_FILESYSTEM_ANDROID   1
 #define SDL_FILESYSTEM_ANDROID   1
+#define SDL_FSOPS_POSIX 1
 
 
 /* Enable the camera driver */
 /* Enable the camera driver */
 #define SDL_CAMERA_DRIVER_ANDROID 1
 #define SDL_CAMERA_DRIVER_ANDROID 1

+ 1 - 0
include/build_config/SDL_build_config_emscripten.h

@@ -206,6 +206,7 @@
 
 
 /* Enable system filesystem support */
 /* Enable system filesystem support */
 #define SDL_FILESYSTEM_EMSCRIPTEN 1
 #define SDL_FILESYSTEM_EMSCRIPTEN 1
+#define SDL_FSOPS_POSIX 1
 
 
 /* Enable the camera driver */
 /* Enable the camera driver */
 #define SDL_CAMERA_DRIVER_EMSCRIPTEN  1
 #define SDL_CAMERA_DRIVER_EMSCRIPTEN  1

+ 1 - 0
include/build_config/SDL_build_config_ios.h

@@ -207,6 +207,7 @@
 
 
 /* enable filesystem support */
 /* enable filesystem support */
 #define SDL_FILESYSTEM_COCOA   1
 #define SDL_FILESYSTEM_COCOA   1
+#define SDL_FSOPS_POSIX 1
 
 
 /* enable camera support */
 /* enable camera support */
 #ifndef SDL_PLATFORM_TVOS
 #ifndef SDL_PLATFORM_TVOS

+ 1 - 0
include/build_config/SDL_build_config_macos.h

@@ -264,6 +264,7 @@
 
 
 /* enable filesystem support */
 /* enable filesystem support */
 #define SDL_FILESYSTEM_COCOA   1
 #define SDL_FILESYSTEM_COCOA   1
+#define SDL_FSOPS_POSIX 1
 
 
 /* enable camera support */
 /* enable camera support */
 #define SDL_CAMERA_DRIVER_COREMEDIA 1
 #define SDL_CAMERA_DRIVER_COREMEDIA 1

+ 1 - 0
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) */
 /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
 #define SDL_FILESYSTEM_DUMMY  1
 #define SDL_FILESYSTEM_DUMMY  1
+#define SDL_FSOPS_DUMMY 1
 
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */
 /* Enable the camera driver (src/camera/dummy/\*.c) */
 #define SDL_CAMERA_DRIVER_DUMMY  1
 #define SDL_CAMERA_DRIVER_DUMMY  1

+ 1 - 0
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) */
 /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
 #define SDL_FILESYSTEM_DUMMY 1
 #define SDL_FILESYSTEM_DUMMY 1
+#define SDL_FSOPS_DUMMY 1
 
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */
 /* Enable the camera driver (src/camera/dummy/\*.c) */
 #define SDL_CAMERA_DRIVER_DUMMY  1
 #define SDL_CAMERA_DRIVER_DUMMY  1

+ 1 - 0
include/build_config/SDL_build_config_windows.h

@@ -312,6 +312,7 @@ typedef unsigned int uintptr_t;
 
 
 /* Enable filesystem support */
 /* Enable filesystem support */
 #define SDL_FILESYSTEM_WINDOWS  1
 #define SDL_FILESYSTEM_WINDOWS  1
+#define SDL_FSOPS_WINDOWS 1
 
 
 /* Enable the camera driver */
 /* Enable the camera driver */
 #define SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1
 #define SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1

+ 1 - 0
include/build_config/SDL_build_config_wingdk.h

@@ -244,6 +244,7 @@
 
 
 /* Enable filesystem support */
 /* Enable filesystem support */
 #define SDL_FILESYSTEM_WINDOWS  1
 #define SDL_FILESYSTEM_WINDOWS  1
+#define SDL_FSOPS_WINDOWS 1
 
 
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
 #define SDL_CAMERA_DRIVER_DUMMY  1
 #define SDL_CAMERA_DRIVER_DUMMY  1

+ 4 - 0
include/build_config/SDL_build_config_winrt.h

@@ -213,6 +213,10 @@
 /* Enable system power support */
 /* Enable system power support */
 #define SDL_POWER_WINRT 1
 #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 */
 /* Enable the camera driver (src/camera/dummy/\*.c) */  /* !!! FIXME */
 #define SDL_CAMERA_DRIVER_DUMMY  1
 #define SDL_CAMERA_DRIVER_DUMMY  1
 
 

+ 2 - 0
include/build_config/SDL_build_config_xbox.h

@@ -228,6 +228,8 @@
 /* Enable filesystem support */
 /* Enable filesystem support */
 /* #define SDL_FILESYSTEM_WINDOWS 1*/
 /* #define SDL_FILESYSTEM_WINDOWS 1*/
 #define SDL_FILESYSTEM_XBOX 1
 #define SDL_FILESYSTEM_XBOX 1
+#define SDL_FSOPS_WINDOWS 1
+
 
 
 /* Disable IME as not supported yet (TODO: Xbox IME?) */
 /* Disable IME as not supported yet (TODO: Xbox IME?) */
 #define SDL_DISABLE_WINDOWS_IME 1
 #define SDL_DISABLE_WINDOWS_IME 1

+ 6 - 0
src/dynapi/SDL_dynapi.sym

@@ -987,6 +987,12 @@ SDL3_0.0.0 {
     SDL_GetStorageFileSize;
     SDL_GetStorageFileSize;
     SDL_ReadStorageFile;
     SDL_ReadStorageFile;
     SDL_GetStorageSpaceRemaining;
     SDL_GetStorageSpaceRemaining;
+    SDL_CreateDirectory;
+    SDL_EnumerateDirectory;
+    SDL_RemovePath;
+    SDL_RenamePath;
+    SDL_GetPathInfo;
+    SDL_FileTimeToWindows;
     # extra symbols go here (don't modify this line)
     # extra symbols go here (don't modify this line)
   local: *;
   local: *;
 };
 };

+ 6 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1012,3 +1012,9 @@
 #define SDL_GetStorageFileSize SDL_GetStorageFileSize_REAL
 #define SDL_GetStorageFileSize SDL_GetStorageFileSize_REAL
 #define SDL_ReadStorageFile SDL_ReadStorageFile_REAL
 #define SDL_ReadStorageFile SDL_ReadStorageFile_REAL
 #define SDL_GetStorageSpaceRemaining SDL_GetStorageSpaceRemaining_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

+ 6 - 0
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_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(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(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),)

+ 91 - 0
src/filesystem/SDL_filesystem.c

@@ -0,0 +1,91 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
+
+  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);
+}

+ 32 - 0
src/filesystem/SDL_sysfilesystem.h

@@ -0,0 +1,32 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
+
+  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
+

+ 54 - 0
src/filesystem/dummy/SDL_sysfsops.c

@@ -0,0 +1,54 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
+
+  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
+

+ 138 - 0
src/filesystem/posix/SDL_sysfsops.c

@@ -0,0 +1,138 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
+
+  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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#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
+

+ 188 - 0
src/filesystem/windows/SDL_sysfsops.c

@@ -0,0 +1,188 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
+
+  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
+

+ 69 - 2
test/testfilesystem.c

@@ -15,11 +15,54 @@
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_test.h>
 #include <SDL3/SDL_test.h>
 
 
+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[])
 int main(int argc, char *argv[])
 {
 {
     SDLTest_CommonState *state;
     SDLTest_CommonState *state;
-    char *base_path;
     char *pref_path;
     char *pref_path;
+    char *base_path;
 
 
     /* Initialize test framework */
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, 0);
     state = SDLTest_CommonCreateState(argv, 0);
@@ -46,7 +89,6 @@ int main(int argc, char *argv[])
                      SDL_GetError());
                      SDL_GetError());
     } else {
     } else {
         SDL_Log("base path: '%s'\n", base_path);
         SDL_Log("base path: '%s'\n", base_path);
-        SDL_free(base_path);
     }
     }
 
 
     pref_path = SDL_GetPrefPath("libsdl", "test_filesystem");
     pref_path = SDL_GetPrefPath("libsdl", "test_filesystem");
@@ -67,6 +109,31 @@ int main(int argc, char *argv[])
         SDL_free(pref_path);
         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();
     SDL_Quit();
     SDLTest_CommonDestroyState(state);
     SDLTest_CommonDestroyState(state);
     return 0;
     return 0;