diff --git a/Android.mk b/Android.mk index 275833174..2b4255559 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 fcbb93fc3..8017bc2b7 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 e50832cb1..948e75578 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 49c00513c..bc62d7a0b 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 79d7a6097..4948abf9b 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 c143ae9e9..ca56e1757 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 504e3f9c0..24c397472 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 c6568f85f..5d3e43001 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 b32643507..df7d4548d 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 8030c0435..75e71bf97 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 1e650c491..c71a0cafb 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 64f8076e0..b784afd00 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 89d5531f3..bf6e919cf 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 e130cc467..875f1942a 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 39ee50d45..c853e58a9 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 06d02557e..f949f94f6 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 3449627b0..596943703 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 19873dbf1..69852a766 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 992052a81..f760cf477 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 b5e5725fb..9a7702d87 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 861503673..4822fe629 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 5ab616220..e233484a0 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 9f55177be..f12c82ea5 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 86263add1..fa2c50937 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 000000000..a06d19ac7 --- /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 000000000..c8fb4e7a4 --- /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 000000000..3e014722d --- /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 000000000..69e42d6ee --- /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 000000000..571c83eb1 --- /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 e1986698b..35f99c867 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;